discovery+funding+peer+server: migrate gossip result to actor.Future[error]
What changed, and why it matters
This commit refactors how LND's gossip subsystem reports errors back to callers. Previously, code used buffered Go channels to carry a single error result. That pattern could deadlock if a deferred gossip message was processed twice, because the second write to an already-full channel would block forever. The change replaces those channels with a new 'actor.Promise/Future' abstraction whose completion is idempotent (safe to call many times). It also makes shutdown handling more uniform by deriving contexts from quit channels, preventing another latent deadlock in channel-policy propagation. The commit includes regression tests for premature-announcement reprocessing and shutdown paths.
Review the actor.Promise implementation for correct sync.Once behavior and memory visibility; ensure AwaitGossipResult handles context cancellation and future completion races safely. Monitor for any third-party callers or plugins that still expect a chan error return from ProcessRemoteAnnouncement/ProcessLocalAnnouncement. Consider backporting to supported release branches because the deadlock could stall the gossip handler and affect routing state.
Security signals we found
Eliminates latent goroutine/channel deadlock in gossip result reporting
Replaces non-idempotent buffered error channels with idempotent actor.Promise.Complete
Adds shutdown-aware context bridging via ContextFromQuit to prevent blocking awaits
Fixes latent deadlock in PropagateChanPolicyUpdate during gossiper shutdown
Adds regression tests explicitly referencing 'Network Isolation Attack' and premature-announcement reprocessing deadlock
Evidence from the diff
The patch migrates three buffered chan error patterns in the discovery package to actor.Promise[error]/actor.Future[error]: networkMsg.err, chanPolicyUpdateRequest.errChan, and syncTransitionReq.errChan. ProcessRemoteAnnouncement, ProcessLocalAnnouncement, and ProcessSyncTransition now return actor.Future[error]. A helper completeGossipResult resolves promises; because actor.Promise.Complete uses sync.Once, duplicate completions are no-ops, eliminating the deadlock where a copied premature networkMsg was re-enqueued and its err channel written twice. PropagateChanPolicyUpdate now uses AwaitGossipResult with a ContextFromQuit-derived context instead of blocking on <-errChan after enqueue, fixing a shutdown deadlock. Funding manager and server callers are updated to await futures with shutdown-aware contexts and map cancellation/gossiper-shutdown errors to their own shutdown sentinel. Tests are added for premature-announcement reprocessing, peer quit, context cancellation, and gossiper quit paths.
Changed components
discovery/gossiper.godiscovery/syncer.gofunding/manager.gopeer/brontide.goserver.goactor.Future / actor.Promise utilityInspect captured patch +833 / −890
diff --git a/discovery/gossiper.go b/discovery/gossiper.go
index ac3a6d8..0c533d4 100644
--- a/discovery/gossiper.go
+++ b/discovery/gossiper.go
@@ -21,6 +21,7 @@ import (
"github.com/btcsuite/btcd/wire"
"github.com/lightninglabs/neutrino/cache"
"github.com/lightninglabs/neutrino/cache/lru"
+ "github.com/lightningnetwork/lnd/actor"
"github.com/lightningnetwork/lnd/batch"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/channeldb"
@@ -82,6 +83,10 @@ var (
// is in the process of being shut down.
ErrGossiperShuttingDown = errors.New("gossiper is shutting down")
+ // ErrPeerQuitting is returned when the peer that sent a gossip message
+ // quits before the message could be enqueued for processing.
+ ErrPeerQuitting = errors.New("peer quitting")
+
// ErrGossipSyncerNotFound signals that we were unable to find an active
// gossip syncer corresponding to a gossip query message received from
// the remote peer.
@@ -177,7 +182,7 @@ type networkMsg struct {
isRemote bool
- err chan error
+ errPromise actor.Promise[error]
}
// chanPolicyUpdateRequest is a request that is sent to the server when a caller
@@ -186,7 +191,7 @@ type networkMsg struct {
// updates committed to the lower layer.
type chanPolicyUpdateRequest struct {
edgesToUpdate []EdgeWithInfo
- errChan chan error
+ errPromise actor.Promise[error]
}
// PinnedSyncers is a set of node pubkeys for which we will maintain an active
@@ -645,19 +650,22 @@ type EdgeWithInfo struct {
func (d *AuthenticatedGossiper) PropagateChanPolicyUpdate(
edgesToUpdate []EdgeWithInfo) error {
- errChan := make(chan error, 1)
+ promise := actor.NewPromise[error]()
policyUpdate := &chanPolicyUpdateRequest{
edgesToUpdate: edgesToUpdate,
- errChan: errChan,
+ errPromise: promise,
}
select {
case d.chanPolicyUpdates <- policyUpdate:
- err := <-errChan
- return err
case <-d.quit:
return fmt.Errorf("AuthenticatedGossiper shutting down")
}
+
+ ctx, cancel := lnutils.ContextFromQuit(d.quit)
+ defer cancel()
+
+ return AwaitGossipResult(ctx, promise.Future())
}
// Start spawns network messages handler goroutine and registers on new block
@@ -828,7 +836,10 @@ func (d *AuthenticatedGossiper) resendFutureMessages(height uint32) {
select {
case d.networkMsgs <- msg:
case <-d.quit:
- msg.err <- ErrGossiperShuttingDown
+ completeGossipResult(
+ msg.errPromise,
+ ErrGossiperShuttingDown,
+ )
}
}
}
@@ -877,16 +888,9 @@ func (d *AuthenticatedGossiper) stop() {
// peers. Remote channel announcements should contain the announcement proof
// and be fully validated.
func (d *AuthenticatedGossiper) ProcessRemoteAnnouncement(ctx context.Context,
- msg lnwire.Message, peer lnpeer.Peer) chan error {
+ msg lnwire.Message, peer lnpeer.Peer) actor.Future[error] {
- // Buffer up to two messages on errChan since up to two messages may be
- // written and not all callers of this function actually read from
- // errChan. Without this buffer goroutines end up blocking on writes to
- // errChan, which prevents the gossiper from shutting down cleanly.
- //
- // TODO(ziggie): Redesign this once the actor model pattern becomes
- // available. See https://github.com/lightningnetwork/lnd/pull/9820.
- errChan := make(chan error, 2)
+ promise := actor.NewPromise[error]()
// For messages in the known set of channel series queries, we'll
// dispatch the message directly to the GossipSyncer, and skip the main
@@ -902,8 +906,9 @@ func (d *AuthenticatedGossiper) ProcessRemoteAnnouncement(ctx context.Context,
log.Warnf("Gossip syncer for peer=%x not found",
peer.PubKey())
- errChan <- ErrGossipSyncerNotFound
- return errChan
+ completeGossipResult(promise, ErrGossipSyncerNotFound)
+
+ return promise.Future()
}
// If we've found the message target, then we'll dispatch the
@@ -914,8 +919,9 @@ func (d *AuthenticatedGossiper) ProcessRemoteAnnouncement(ctx context.Context,
peer.PubKey(), err)
}
- errChan <- err
- return errChan
+ completeGossipResult(promise, err)
+
+ return promise.Future()
// If a peer is updating its current update horizon, then we'll dispatch
// that directly to the proper GossipSyncer.
@@ -925,8 +931,9 @@ func (d *AuthenticatedGossiper) ProcessRemoteAnnouncement(ctx context.Context,
log.Warnf("Gossip syncer for peer=%x not found",
peer.PubKey())
- errChan <- ErrGossipSyncerNotFound
- return errChan
+ completeGossipResult(promise, ErrGossipSyncerNotFound)
+
+ return promise.Future()
}
// Queue the message for asynchronous processing to prevent
@@ -938,12 +945,14 @@ func (d *AuthenticatedGossiper) ProcessRemoteAnnouncement(ctx context.Context,
// Return nil to indicate we've handled the message,
// even though it was dropped. This prevents the peer
// from being disconnected.
- errChan <- nil
- return errChan
+ completeGossipResult(promise, nil)
+
+ return promise.Future()
}
- errChan <- nil
- return errChan
+ completeGossipResult(promise, nil)
+
+ return promise.Future()
// To avoid inserting edges in the graph for our own channels that we
// have already closed, we ignore such channel announcements coming
@@ -957,35 +966,34 @@ func (d *AuthenticatedGossiper) ProcessRemoteAnnouncement(ctx context.Context,
bytes.Equal(m.NodeID2[:], ownKey) {
log.Warn(ownErr)
- errChan <- ownErr
- return errChan
+ completeGossipResult(promise, ownErr)
+
+ return promise.Future()
}
}
nMsg := &networkMsg{
- msg: msg,
- isRemote: true,
- peer: peer,
- source: peer.IdentityKey(),
- err: errChan,
+ msg: msg,
+ isRemote: true,
+ peer: peer,
+ source: peer.IdentityKey(),
+ errPromise: promise,
}
select {
case d.networkMsgs <- nMsg:
- // If the peer that sent us this error is quitting, then we don't need
- // to send back an error and can return immediately.
- // TODO(elle): the peer should now just rely on canceling the passed
- // context.
+ // If the peer that sent us this message is quitting, complete the
+ // promise so any awaiter does not block indefinitely.
case <-peer.QuitSignal():
- return nil
+ completeGossipResult(promise, ErrPeerQuitting)
case <-ctx.Done():
- return nil
+ completeGossipResult(promise, ctx.Err())
case <-d.quit:
- nMsg.err <- ErrGossiperShuttingDown
+ completeGossipResult(promise, ErrGossiperShuttingDown)
}
- return nMsg.err
+ return promise.Future()
}
// ProcessLocalAnnouncement sends a new remote announcement message along with
@@ -996,7 +1004,7 @@ func (d *AuthenticatedGossiper) ProcessRemoteAnnouncement(ctx context.Context,
// entire channel announcement and update messages will be re-constructed and
// broadcast to the rest of the network.
func (d *AuthenticatedGossiper) ProcessLocalAnnouncement(msg lnwire.Message,
- optionalFields ...OptionalMsgField) chan error {
+ optionalFields ...OptionalMsgField) actor.Future[error] {
optionalMsgFields := &optionalMsgFields{}
optionalMsgFields.apply(optionalFields...)
@@ -1006,16 +1014,16 @@ func (d *AuthenticatedGossiper) ProcessLocalAnnouncement(msg lnwire.Message,
optionalMsgFields: optionalMsgFields,
isRemote: false,
source: d.selfKey,
- err: make(chan error, 1),
+ errPromise: actor.NewPromise[error](),
}
select {
case d.networkMsgs <- nMsg:
case <-d.quit:
- nMsg.err <- ErrGossiperShuttingDown
+ completeGossipResult(nMsg.errPromise, ErrGossiperShuttingDown)
}
- return nMsg.err
+ return nMsg.errPromise.Future()
}
// channelUpdateID is a unique identifier for ChannelUpdate messages, as
@@ -1505,7 +1513,7 @@ func (d *AuthenticatedGossiper) networkHandler(ctx context.Context) {
newChanUpdates, err := d.processChanPolicyUpdate(
ctx, policyUpdate.edgesToUpdate,
)
- policyUpdate.errChan <- err
+ completeGossipResult(policyUpdate.errPromise, err)
if err != nil {
log.Errorf("Unable to craft policy updates: %v",
err)
@@ -1564,8 +1572,10 @@ func (d *AuthenticatedGossiper) networkHandler(ctx context.Context) {
sourceToPub(announcement.source),
) {
- announcement.err <- fmt.Errorf("recently " +
- "rejected")
+ completeGossipResult(
+ announcement.errPromise,
+ fmt.Errorf("recently rejected"),
+ )
continue
}
@@ -1576,7 +1586,10 @@ func (d *AuthenticatedGossiper) networkHandler(ctx context.Context) {
announcement.msg,
)
if err != nil {
- announcement.err <- err
+ completeGossipResult(
+ announcement.errPromise, err,
+ )
+
continue
}
@@ -1653,7 +1666,7 @@ func (d *AuthenticatedGossiper) handleNetworkMessages(ctx context.Context,
log.Warnf("unexpected error during validation "+
"barrier shutdown: %v", err)
}
- nMsg.err <- err
+ completeGossipResult(nMsg.errPromise, err)
return
}
@@ -1675,7 +1688,7 @@ func (d *AuthenticatedGossiper) handleNetworkMessages(ctx context.Context,
log.Errorf("SignalDependents returned error for msg=%v with "+
"JobID=%v", lnutils.SpewLogClosure(nMsg.msg), jobID)
- nMsg.err <- err
+ completeGossipResult(nMsg.errPromise, err)
return
}
@@ -1756,16 +1769,12 @@ func (d *AuthenticatedGossiper) finalizeGossipProcessing(logCtx context.Context,
}
// Send an error back to the caller if possible.
- if nMsg != nil && nMsg.err != nil {
- select {
- case nMsg.err <- fmt.Errorf("panic while %s gossip "+
- "message %s: %v", ctxStr, msgType, r):
- default:
- log.WarnS(logCtx, "Unable to send panic error, "+
- "error channel blocked", nil,
- slog.String("msg_type", msgType),
- )
- }
+ if nMsg != nil {
+ completeGossipResult(
+ nMsg.errPromise,
+ fmt.Errorf("panic while %s gossip message %s: %v",
+ ctxStr, msgType, r),
+ )
}
}
@@ -2219,15 +2228,16 @@ func (d *AuthenticatedGossiper) isPremature(chanID lnwire.ShortChannelID,
// Add the premature message to our future messages which will be
// resent once the block height has reached.
//
- // Copy the networkMsgs since the old message's err chan will be
- // consumed.
+ // Copy the networkMsg and allocate a fresh promise for the copy.
+ // The original message's errPromise is resolved by the caller with nil
+ // to indicate the message was accepted for deferred processing.
copied := &networkMsg{
peer: msg.peer,
source: msg.source,
msg: msg.msg,
optionalMsgFields: msg.optionalMsgFields,
isRemote: msg.isRemote,
- err: make(chan error, 1),
+ errPromise: actor.NewPromise[error](),
}
// Create the cached message.
@@ -2295,7 +2305,7 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement(ctx context.Context,
default:
err := errors.New("wrong type of the announcement")
- nMsg.err <- err
+ completeGossipResult(nMsg.errPromise, err)
return nil, false
}
}
@@ -2585,7 +2595,7 @@ func (d *AuthenticatedGossiper) handleNodeAnnouncement(ctx context.Context,
log.Warnf("Rejecting node announcement from peer=%v: %v",
nMsg.peer, err)
- nMsg.err <- err
+ completeGossipResult(nMsg.errPromise, err)
return nil, false
}
@@ -2594,7 +2604,7 @@ func (d *AuthenticatedGossiper) handleNodeAnnouncement(ctx context.Context,
// this node so we can skip validating signatures if not required.
if d.cfg.Graph.IsStaleNode(ctx, nodeAnn.NodeID, timestamp) {
log.Debugf("Skipped processing stale node: %x", nodeAnn.NodeID)
- nMsg.err <- nil
+ completeGossipResult(nMsg.errPromise, nil)
return nil, true
}
@@ -2611,7 +2621,7 @@ func (d *AuthenticatedGossiper) handleNodeAnnouncement(ctx context.Context,
log.Error(err)
}
- nMsg.err <- err
+ completeGossipResult(nMsg.errPromise, err)
return nil, false
}
@@ -2622,7 +2632,7 @@ func (d *AuthenticatedGossiper) handleNodeAnnouncement(ctx context.Context,
if err != nil {
log.Errorf("Unable to determine if node %x is advertised: %v",
nodeAnn.NodeID, err)
- nMsg.err <- err
+ completeGossipResult(nMsg.errPromise, err)
return nil, false
}
@@ -2642,7 +2652,7 @@ func (d *AuthenticatedGossiper) handleNodeAnnouncement(ctx context.Context,
"due to being unadvertised", nodeAnn.NodeID)
}
- nMsg.err <- nil
+ completeGossipResult(nMsg.errPromise, nil)
// TODO(roasbeef): get rid of the above
log.Debugf("Processed NodeAnnouncement1: peer=%v, timestamp=%v, "+
@@ -2679,7 +2689,7 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(ctx context.Context,
)
_, _ = d.recentRejects.Put(key, &cachedReject{})
- nMsg.err <- err
+ completeGossipResult(nMsg.errPromise, err)
return nil, false
}
@@ -2697,7 +2707,7 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(ctx context.Context,
)
_, _ = d.recentRejects.Put(key, &cachedReject{})
- nMsg.err <- err
+ completeGossipResult(nMsg.errPromise, err)
return nil, false
}
@@ -2709,7 +2719,7 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(ctx context.Context,
"advertises height %v, only height %v is known",
scid.ToUint64(), scid.BlockHeight, d.bestHeight)
d.Unlock()
- nMsg.err <- nil
+ completeGossipResult(nMsg.errPromise, nil)
return nil, false
}
d.Unlock()
@@ -2717,7 +2727,7 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(ctx context.Context,
// At this point, we'll now ask the router if this is a zombie/known
// edge. If so we can skip all the processing below.
if d.cfg.Graph.IsKnownEdge(scid) {
- nMsg.err <- nil
+ completeGossipResult(nMsg.errPromise, nil)
return nil, true
}
@@ -2727,7 +2737,7 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(ctx context.Context,
if err != nil {
log.Errorf("failed to check if scid %v is closed: %v", scid,
err)
- nMsg.err <- err
+ completeGossipResult(nMsg.errPromise, err)
return nil, false
}
@@ -2737,7 +2747,7 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(ctx context.Context,
// If this is an announcement from us, we'll just ignore it.
if !nMsg.isRemote {
- nMsg.err <- err
+ completeGossipResult(nMsg.errPromise, err)
return nil, false
}
@@ -2751,7 +2761,7 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(ctx context.Context,
err = dcErr
}
- nMsg.err <- err
+ completeGossipResult(nMsg.errPromise, err)
return nil, false
}
@@ -2773,7 +2783,7 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(ctx context.Context,
_, _ = d.recentRejects.Put(key, &cachedReject{})
log.Error(err)
- nMsg.err <- err
+ completeGossipResult(nMsg.errPromise, err)
return nil, false
}
@@ -2808,7 +2818,8 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(ctx context.Context,
_, _ = d.recentRejects.Put(key, &cachedReject{})
log.Errorf("unable to create channel edge: %v", err)
- nMsg.err <- err
+ completeGossipResult(nMsg.errPromise, err)
+
return nil, false
}
@@ -2883,7 +2894,9 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(ctx context.Context,
log.Errorf("failed to mark scid(%v) "+
"as closed: %v", scid, dbErr)
- nMsg.err <- dbErr
+ completeGossipResult(
+ nMsg.errPromise, dbErr,
+ )
return nil, false
}
@@ -2899,7 +2912,7 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(ctx context.Context,
)
_, _ = d.recentRejects.Put(key, &cachedReject{})
- nMsg.err <- err
+ completeGossipResult(nMsg.errPromise, err)
return nil, false
}
@@ -2907,7 +2920,7 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(ctx context.Context,
if !nMsg.isRemote {
log.Errorf("failed to add edge for local "+
"channel: %v", err)
- nMsg.err <- err
+ completeGossipResult(nMsg.errPromise, err)
return nil, false
}
@@ -2923,7 +2936,7 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(ctx context.Context,
err = dcErr
}
- nMsg.err <- err
+ completeGossipResult(nMsg.errPromise, err)
return nil, false
}
@@ -2964,7 +2977,7 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(ctx context.Context,
cr := &cachedReject{}
_, _ = d.recentRejects.Put(key, cr)
- nMsg.err <- rErr
+ completeGossipResult(nMsg.errPromise, rErr)
return nil, false
}
@@ -2978,7 +2991,7 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(ctx context.Context,
//
// NOTE: since this is an ErrIgnored, we can return
// true here to signal "allow" to its dependants.
- nMsg.err <- nil
+ completeGossipResult(nMsg.errPromise, nil)
return anns, true
}
@@ -2994,7 +3007,7 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(ctx context.Context,
if !nMsg.isRemote {
log.Errorf("failed to add edge for local channel: %v",
err)
- nMsg.err <- err
+ completeGossipResult(nMsg.errPromise, err)
return nil, false
}
@@ -3003,7 +3016,7 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(ctx context.Context,
if dcErr != nil {
log.Errorf("failed to check if we should disconnect "+
"peer: %v", dcErr)
- nMsg.err <- dcErr
+ completeGossipResult(nMsg.errPromise, dcErr)
return nil, false
}
@@ -3012,7 +3025,7 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(ctx context.Context,
nMsg.peer.Disconnect(ErrPeerBanned)
}
- nMsg.err <- err
+ completeGossipResult(nMsg.errPromise, err)
return nil, false
}
@@ -3064,7 +3077,10 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(ctx context.Context,
select {
case d.networkMsgs <- updMsg:
case <-d.quit:
- updMsg.err <- ErrGossiperShuttingDown
+ completeGossipResult(
+ updMsg.errPromise,
+ ErrGossiperShuttingDown,
+ )
}
// We don't expect any other message type than
@@ -3090,7 +3106,7 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(ctx context.Context,
})
}
- nMsg.err <- nil
+ completeGossipResult(nMsg.errPromise, nil)
log.Debugf("Processed ChannelAnnouncement1: peer=%v, short_chan_id=%v",
nMsg.peer, scid.ToUint64())
@@ -3124,7 +3140,7 @@ func (d *AuthenticatedGossiper) handleChanUpdate(ctx context.Context,
)
_, _ = d.recentRejects.Put(key, &cachedReject{})
- nMsg.err <- err
+ completeGossipResult(nMsg.errPromise, err)
return nil, false
}
@@ -3144,7 +3160,7 @@ func (d *AuthenticatedGossiper) handleChanUpdate(ctx context.Context,
"premature: advertises height %v, only height %v is "+
"known", shortChanID, blockHeight, d.bestHeight)
d.Unlock()
- nMsg.err <- nil
+ completeGossipResult(nMsg.errPromise, nil)
return nil, false
}
d.Unlock()
@@ -3170,7 +3186,7 @@ func (d *AuthenticatedGossiper) handleChanUpdate(ctx context.Context,
}
}
- nMsg.err <- err
+ completeGossipResult(nMsg.errPromise, err)
return nil, false
}
@@ -3202,7 +3218,7 @@ func (d *AuthenticatedGossiper) handleChanUpdate(ctx context.Context,
nMsg.peer, nMsg.msg.MsgType(), nMsg.isRemote,
)
- nMsg.err <- nil
+ completeGossipResult(nMsg.errPromise, nil)
return nil, true
}
@@ -3214,7 +3230,7 @@ func (d *AuthenticatedGossiper) handleChanUpdate(ctx context.Context,
// If this is a channel_update from us, we'll just ignore it.
if !nMsg.isRemote {
- nMsg.err <- err
+ completeGossipResult(nMsg.errPromise, err)
return nil, false
}
@@ -3230,7 +3246,7 @@ func (d *AuthenticatedGossiper) handleChanUpdate(ctx context.Context,
err = dcErr
}
- nMsg.err <- err
+ completeGossipResult(nMsg.errPromise, err)
return nil, false
}
@@ -3248,7 +3264,7 @@ func (d *AuthenticatedGossiper) handleChanUpdate(ctx context.Context,
err = d.processZombieUpdate(ctx, chanInfo, graphScid, upd)
if err != nil {
log.Debug(err)
- nMsg.err <- err
+ completeGossipResult(nMsg.errPromise, err)
return nil, false
}
@@ -3317,7 +3333,7 @@ func (d *AuthenticatedGossiper) handleChanUpdate(ctx context.Context,
err := fmt.Errorf("unable to validate channel update "+
"short_chan_id=%v: %v", shortChanID, err)
log.Error(err)
- nMsg.err <- err
+ completeGossipResult(nMsg.errPromise, err)
key := newRejectCacheKey(
upd.GossipVersion(),
@@ -3360,7 +3376,7 @@ func (d *AuthenticatedGossiper) handleChanUpdate(ctx context.Context,
lnutils.SpewLogClosure(upd.ShortChannelID), err)
log.Error(rErr)
- nMsg.err <- rErr
+ completeGossipResult(nMsg.errPromise, rErr)
return nil, false
}
@@ -3377,7 +3393,7 @@ func (d *AuthenticatedGossiper) handleChanUpdate(ctx context.Context,
log.Debugf("Ignoring keep alive update not "+
"within %v period for channel %v",
d.cfg.RebroadcastInterval, shortChanID)
- nMsg.err <- nil
+ completeGossipResult(nMsg.errPromise, nil)
return nil, false
}
} else {
@@ -3410,7 +3426,7 @@ func (d *AuthenticatedGossiper) handleChanUpdate(ctx context.Context,
log.Debugf("Rate limiting update for channel "+
"%v from direction %x", shortChanID,
pubKey.SerializeCompressed())
- nMsg.err <- nil
+ completeGossipResult(nMsg.errPromise, nil)
return nil, false
}
}
@@ -3427,7 +3443,7 @@ func (d *AuthenticatedGossiper) handleChanUpdate(ctx context.Context,
chanInfo.ChannelID, upd,
)
if err != nil {
- nMsg.err <- err
+ completeGossipResult(nMsg.errPromise, err)
return nil, false
}
@@ -3453,7 +3469,7 @@ func (d *AuthenticatedGossiper) handleChanUpdate(ctx context.Context,
shortChanID, err)
}
- nMsg.err <- err
+ completeGossipResult(nMsg.errPromise, err)
return nil, false
}
@@ -3477,14 +3493,20 @@ func (d *AuthenticatedGossiper) handleChanUpdate(ctx context.Context,
sig, err := d.cfg.SignAliasUpdate(upd)
if err != nil {
log.Error(err)
- nMsg.err <- err
+ completeGossipResult(
+ nMsg.errPromise, err,
+ )
+
return nil, false
}
lnSig, err := lnwire.NewSigFromSignature(sig)
if err != nil {
log.Error(err)
- nMsg.err <- err
+ completeGossipResult(
+ nMsg.errPromise, err,
+ )
+
return nil, false
}
@@ -3508,7 +3530,7 @@ func (d *AuthenticatedGossiper) handleChanUpdate(ctx context.Context,
err := fmt.Errorf("unable to reliably send %v for "+
"channel=%v to peer=%x: %v", upd.MsgType(),
upd.ShortChannelID, remotePubKey, err)
- nMsg.err <- err
+ completeGossipResult(nMsg.errPromise, err)
return nil, false
}
}
@@ -3528,7 +3550,7 @@ func (d *AuthenticatedGossiper) handleChanUpdate(ctx context.Context,
})
}
- nMsg.err <- nil
+ completeGossipResult(nMsg.errPromise, nil)
log.Debugf("Processed ChannelUpdate: peer=%v, short_chan_id=%v, "+
"timestamp=%v", nMsg.peer, upd.ShortChannelID.ToUint64(),
@@ -3567,7 +3589,7 @@ func (d *AuthenticatedGossiper) handleAnnSig(ctx context.Context,
"lower than needed: %v < %v", d.bestHeight,
needBlockHeight)
d.Unlock()
- nMsg.err <- nil
+ completeGossipResult(nMsg.errPromise, nil)
return nil, false
}
d.Unlock()
@@ -3590,7 +3612,7 @@ func (d *AuthenticatedGossiper) handleAnnSig(ctx context.Context,
err := fmt.Errorf("unable to store the proof for "+
"short_chan_id=%v: %v", shortChanID, err)
log.Error(err)
- nMsg.err <- err
+ completeGossipResult(nMsg.errPromise, err)
return nil, false
}
@@ -3601,13 +3623,13 @@ func (d *AuthenticatedGossiper) handleAnnSig(ctx context.Context,
err := fmt.Errorf("unable to store the proof for "+
"short_chan_id=%v: %v", shortChanID, err)
log.Error(err)
- nMsg.err <- err
+ completeGossipResult(nMsg.errPromise, err)
return nil, false
}
log.Infof("Orphan %v proof announcement with short_chan_id=%v"+
", adding to waiting batch", prefix, shortChanID)
- nMsg.err <- nil
+ completeGossipResult(nMsg.errPromise, nil)
return nil, false
}
@@ -3622,7 +3644,7 @@ func (d *AuthenticatedGossiper) handleAnnSig(ctx context.Context,
"to the peer which sent the proof, short_chan_id=%v",
shortChanID)
log.Error(err)
- nMsg.err <- err
+ completeGossipResult(nMsg.errPromise, err)
return nil, false
}
@@ -3645,7 +3667,7 @@ func (d *AuthenticatedGossiper) handleAnnSig(ctx context.Context,
err := fmt.Errorf("unable to reliably send %v for "+
"channel=%v to peer=%x: %v", ann.MsgType(),
ann.ShortChannelID, remotePubKey, err)
- nMsg.err <- err
+ completeGossipResult(nMsg.errPromise, err)
return nil, false
}
}
@@ -3692,7 +3714,7 @@ func (d *AuthenticatedGossiper) handleAnnSig(ctx context.Context,
log.Debugf("Already have proof for channel with chanID=%v",
ann.ChannelID)
- nMsg.err <- nil
+ completeGossipResult(nMsg.errPromise, nil)
return nil, true
}
@@ -3707,7 +3729,7 @@ func (d *AuthenticatedGossiper) handleAnnSig(ctx context.Context,
err := fmt.Errorf("unable to get the opposite proof for "+
"short_chan_id=%v: %v", shortChanID, err)
log.Error(err)
- nMsg.err <- err
+ completeGossipResult(nMsg.errPromise, err)
return nil, false
}
@@ -3717,7 +3739,7 @@ func (d *AuthenticatedGossiper) handleAnnSig(ctx context.Context,
err := fmt.Errorf("unable to store the proof for "+
"short_chan_id=%v: %v", shortChanID, err)
log.Error(err)
- nMsg.err <- err
+ completeGossipResult(nMsg.errPromise, err)
return nil, false
}
@@ -3725,7 +3747,7 @@ func (d *AuthenticatedGossiper) handleAnnSig(ctx context.Context,
"short_chan_id=%v, waiting for other half",
shortChanID)
- nMsg.err <- nil
+ completeGossipResult(nMsg.errPromise, nil)
return nil, false
}
@@ -3741,7 +3763,7 @@ func (d *AuthenticatedGossiper) handleAnnSig(ctx context.Context,
err := fmt.Errorf("expected V1 waiting proof, got %T",
oppProof.WaitingProofInner)
log.Error(err)
- nMsg.err <- err
+ completeGossipResult(nMsg.errPromise, err)
return nil, false
}
@@ -3772,7 +3794,7 @@ func (d *AuthenticatedGossiper) handleAnnSig(ctx context.Context,
)
if err != nil {
log.Error(err)
- nMsg.err <- err
+ completeGossipResult(nMsg.errPromise, err)
return nil, false
}
@@ -3784,7 +3806,7 @@ func (d *AuthenticatedGossiper) handleAnnSig(ctx context.Context,
"short_chan_id=%v isn't valid: %v", shortChanID, err)
log.Error(err)
- nMsg.err <- err
+ completeGossipResult(nMsg.errPromise, err)
return nil, false
}
@@ -3799,7 +3821,7 @@ func (d *AuthenticatedGossiper) handleAnnSig(ctx context.Context,
err := fmt.Errorf("unable add proof to the channel chanID=%v:"+
" %v", ann.ChannelID, err)
log.Error(err)
- nMsg.err <- err
+ completeGossipResult(nMsg.errPromise, err)
return nil, false
}
@@ -3808,7 +3830,7 @@ func (d *AuthenticatedGossiper) handleAnnSig(ctx context.Context,
err := fmt.Errorf("unable to remove opposite proof for the "+
"channel with chanID=%v: %v", ann.ChannelID, err)
log.Error(err)
- nMsg.err <- err
+ completeGossipResult(nMsg.errPromise, err)
return nil, false
}
@@ -3875,7 +3897,7 @@ func (d *AuthenticatedGossiper) handleAnnSig(ctx context.Context,
}
}
- nMsg.err <- nil
+ completeGossipResult(nMsg.errPromise, nil)
return announcements, true
}
diff --git a/discovery/gossiper_test.go b/discovery/gossiper_test.go
index 198f14d..f4bffc5 100644
--- a/discovery/gossiper_test.go
+++ b/discovery/gossiper_test.go
@@ -23,6 +23,7 @@ import (
"github.com/btcsuite/btcd/wire"
"github.com/davecgh/go-spew/spew"
"github.com/lightninglabs/neutrino/cache"
+ "github.com/lightningnetwork/lnd/actor"
"github.com/lightningnetwork/lnd/batch"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/channeldb"
@@ -1061,11 +1062,11 @@ func TestProcessAnnouncement(t *testing.T) {
ca, err := tCtx.createRemoteChannelAnnouncement(0)
require.NoError(t, err, "can't create channel announcement")
- select {
- case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(ctx, ca, nodePeer):
- case <-time.After(2 * time.Second):
- t.Fatal("remote announcement not processed")
- }
+ err = mustProcess(
+ t, tCtx.gossiper.ProcessRemoteAnnouncement(
+ ctx, ca, nodePeer,
+ ),
+ )
require.NoError(t, err, "can't process remote announcement")
// The announcement should be broadcast and included in our local view
@@ -1087,11 +1088,11 @@ func TestProcessAnnouncement(t *testing.T) {
ua.MessageFlags = 0
// We send an invalid channel update and expect it to fail.
- select {
- case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(ctx, ua, nodePeer):
- case <-time.After(2 * time.Second):
- t.Fatal("remote announcement not processed")
- }
+ err = mustProcess(
+ t, tCtx.gossiper.ProcessRemoteAnnouncement(
+ ctx, ua, nodePeer,
+ ),
+ )
require.ErrorContains(t, err, "max htlc flag not set for channel "+
"update")
@@ -1107,11 +1108,11 @@ func TestProcessAnnouncement(t *testing.T) {
ua, err = createUpdateAnnouncement(0, 0, remoteKeyPriv1, timestamp)
require.NoError(t, err, "can't create update announcement")
- select {
- case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(ctx, ua, nodePeer):
- case <-time.After(2 * time.Second):
- t.Fatal("remote announcement not processed")
- }
+ err = mustProcess(
+ t, tCtx.gossiper.ProcessRemoteAnnouncement(
+ ctx, ua, nodePeer,
+ ),
+ )
require.NoError(t, err, "can't process remote announcement")
// The channel policy should be broadcast to the rest of the network.
@@ -1130,11 +1131,11 @@ func TestProcessAnnouncement(t *testing.T) {
na, err := createNodeAnnouncement(remoteKeyPriv1, timestamp)
require.NoError(t, err, "can't create node announcement")
- select {
- case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(ctx, na, nodePeer):
- case <-time.After(2 * time.Second):
- t.Fatal("remote announcement not processed")
- }
+ err = mustProcess(
+ t, tCtx.gossiper.ProcessRemoteAnnouncement(
+ ctx, na, nodePeer,
+ ),
+ )
require.NoError(t, err, "can't process remote announcement")
// It should also be broadcast to the network and included in our local
@@ -1176,11 +1177,11 @@ func TestPrematureAnnouncement(t *testing.T) {
)
require.NoError(t, err, "can't create channel announcement")
- select {
- case <-tCtx.gossiper.ProcessRemoteAnnouncement(ctx, ca, nodePeer):
- case <-time.After(time.Second):
- t.Fatal("announcement was not processed")
- }
+ _ = mustProcess(
+ t, tCtx.gossiper.ProcessRemoteAnnouncement(
+ ctx, ca, nodePeer,
+ ),
+ )
if len(tCtx.router.infos) != 0 {
t.Fatal("edge was added to router")
@@ -1223,11 +1224,9 @@ func TestSignatureAnnouncementLocalFirst(t *testing.T) {
// Recreate lightning network topology. Initialize router with channel
// between two nodes.
- select {
- case err = <-tCtx.gossiper.ProcessLocalAnnouncement(batch.chanAnn):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process local announcement")
- }
+ err = mustProcess(t, tCtx.gossiper.ProcessLocalAnnouncement(
+ batch.chanAnn,
+ ))
require.NoError(t, err, "unable to process channel ann")
select {
case <-tCtx.broadcastedMessage:
@@ -1235,11 +1234,9 @@ func TestSignatureAnnouncementLocalFirst(t *testing.T) {
case <-time.After(2 * trickleDelay):
}
- select {
- case err = <-tCtx.gossiper.ProcessLocalAnnouncement(batch.chanUpdAnn1):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process local announcement")
- }
+ err = mustProcess(t, tCtx.gossiper.ProcessLocalAnnouncement(
+ batch.chanUpdAnn1,
+ ))
require.NoError(t, err, "unable to process channel update")
select {
case <-tCtx.broadcastedMessage:
@@ -1247,11 +1244,9 @@ func TestSignatureAnnouncementLocalFirst(t *testing.T) {
case <-time.After(2 * trickleDelay):
}
- select {
- case err = <-tCtx.gossiper.ProcessLocalAnnouncement(batch.nodeAnn1):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process local announcement")
- }
+ err = mustProcess(t, tCtx.gossiper.ProcessLocalAnnouncement(
+ batch.nodeAnn1,
+ ))
require.NoError(t, err, "unable to process node ann")
select {
case <-tCtx.broadcastedMessage:
@@ -1269,13 +1264,9 @@ func TestSignatureAnnouncementLocalFirst(t *testing.T) {
t.Fatal("gossiper did not send channel update to peer")
}
- select {
- case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, batch.chanUpdAnn2, remotePeer,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process remote announcement")
- }
+ ))
require.NoError(t, err, "unable to process channel update")
select {
case <-tCtx.broadcastedMessage:
@@ -1283,13 +1274,9 @@ func TestSignatureAnnouncementLocalFirst(t *testing.T) {
case <-time.After(2 * trickleDelay):
}
- select {
- case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, batch.nodeAnn2, remotePeer,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process remote announcement")
- }
+ ))
require.NoError(t, err, "unable to process node ann")
select {
case <-tCtx.broadcastedMessage:
@@ -1299,13 +1286,9 @@ func TestSignatureAnnouncementLocalFirst(t *testing.T) {
// Pretending that we receive local channel announcement from funding
// manager, thereby kick off the announcement exchange process.
- select {
- case err = <-tCtx.gossiper.ProcessLocalAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessLocalAnnouncement(
batch.localProofAnn,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process remote announcement")
- }
+ ))
require.NoError(t, err, "unable to process local proof")
select {
@@ -1331,13 +1314,9 @@ func TestSignatureAnnouncementLocalFirst(t *testing.T) {
t.Fatal("wrong number of objects in storage")
}
- select {
- case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, batch.remoteProofAnn, remotePeer,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process remote announcement")
- }
+ ))
require.NoError(t, err, "unable to process remote proof")
for i := 0; i < 5; i++ {
@@ -1404,13 +1383,9 @@ func TestOrphanSignatureAnnouncement(t *testing.T) {
// manager, thereby kick off the announcement exchange process, in
// this case the announcement should be added in the orphan batch
// because we haven't announce the channel yet.
- select {
- case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, batch.remoteProofAnn, remotePeer,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process remote announcement")
- }
+ ))
require.NoError(t, err, "unable to proceed announcement")
number := 0
@@ -1432,11 +1407,9 @@ func TestOrphanSignatureAnnouncement(t *testing.T) {
// Recreate lightning network topology. Initialize router with channel
// between two nodes.
- select {
- case err = <-tCtx.gossiper.ProcessLocalAnnouncement(batch.chanAnn):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process local announcement")
- }
+ err = mustProcess(t, tCtx.gossiper.ProcessLocalAnnouncement(
+ batch.chanAnn,
+ ))
require.NoError(t, err, "unable to process")
@@ -1446,11 +1419,9 @@ func TestOrphanSignatureAnnouncement(t *testing.T) {
case <-time.After(2 * trickleDelay):
}
- select {
- case err = <-tCtx.gossiper.ProcessLocalAnnouncement(batch.chanUpdAnn1):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process local announcement")
- }
+ err = mustProcess(t, tCtx.gossiper.ProcessLocalAnnouncement(
+ batch.chanUpdAnn1,
+ ))
require.NoError(t, err, "unable to process")
select {
@@ -1459,11 +1430,9 @@ func TestOrphanSignatureAnnouncement(t *testing.T) {
case <-time.After(2 * trickleDelay):
}
- select {
- case err = <-tCtx.gossiper.ProcessLocalAnnouncement(batch.nodeAnn1):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process local announcement")
- }
+ err = mustProcess(t, tCtx.gossiper.ProcessLocalAnnouncement(
+ batch.nodeAnn1,
+ ))
require.NoError(t, err, "unable to process node ann")
select {
case <-tCtx.broadcastedMessage:
@@ -1481,13 +1450,9 @@ func TestOrphanSignatureAnnouncement(t *testing.T) {
t.Fatal("gossiper did not send channel update to peer")
}
- select {
- case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, batch.chanUpdAnn2, remotePeer,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process remote announcement")
- }
+ ))
require.NoError(t, err, "unable to process node ann")
select {
case <-tCtx.broadcastedMessage:
@@ -1495,13 +1460,9 @@ func TestOrphanSignatureAnnouncement(t *testing.T) {
case <-time.After(2 * trickleDelay):
}
- select {
- case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, batch.nodeAnn2, remotePeer,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process remote announcement")
- }
+ ))
require.NoError(t, err, "unable to process")
select {
case <-tCtx.broadcastedMessage:
@@ -1511,13 +1472,9 @@ func TestOrphanSignatureAnnouncement(t *testing.T) {
// After that we process local announcement, and waiting to receive
// the channel announcement.
- select {
- case err = <-tCtx.gossiper.ProcessLocalAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessLocalAnnouncement(
batch.localProofAnn,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process remote announcement")
- }
+ ))
require.NoError(t, err, "unable to process")
// The local proof should be sent to the remote peer.
@@ -1591,11 +1548,9 @@ func TestSignatureAnnouncementRetryAtStartup(t *testing.T) {
// Recreate lightning network topology. Initialize router with channel
// between two nodes.
- select {
- case err = <-tCtx.gossiper.ProcessLocalAnnouncement(batch.chanAnn):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process local announcement")
- }
+ err = mustProcess(t, tCtx.gossiper.ProcessLocalAnnouncement(
+ batch.chanAnn,
+ ))
require.NoError(t, err, "unable to process channel ann")
select {
case <-tCtx.broadcastedMessage:
@@ -1605,13 +1560,9 @@ func TestSignatureAnnouncementRetryAtStartup(t *testing.T) {
// Pretending that we receive local channel announcement from funding
// manager, thereby kick off the announcement exchange process.
- select {
- case err = <-tCtx.gossiper.ProcessLocalAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessLocalAnnouncement(
batch.localProofAnn,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process remote announcement")
- }
+ ))
if err != nil {
t.Fatalf("unable to process :%v", err)
}
@@ -1759,13 +1710,9 @@ out:
// Now exchanging the remote channel proof, the channel announcement
// broadcast should continue as normal.
- select {
- case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, batch.remoteProofAnn, remotePeer,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process remote announcement")
- }
+ ))
if err != nil {
t.Fatalf("unable to process :%v", err)
}
@@ -1827,13 +1774,9 @@ func TestSignatureAnnouncementFullProofWhenRemoteProof(t *testing.T) {
// Recreate lightning network topology. Initialize router with channel
// between two nodes.
- select {
- case err = <-tCtx.gossiper.ProcessLocalAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessLocalAnnouncement(
batch.chanAnn,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process local announcement")
- }
+ ))
require.NoError(t, err, "unable to process channel ann")
select {
case <-tCtx.broadcastedMessage:
@@ -1841,13 +1784,9 @@ func TestSignatureAnnouncementFullProofWhenRemoteProof(t *testing.T) {
case <-time.After(2 * trickleDelay):
}
- select {
- case err = <-tCtx.gossiper.ProcessLocalAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessLocalAnnouncement(
batch.chanUpdAnn1,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process local announcement")
- }
+ ))
require.NoError(t, err, "unable to process channel update")
select {
case <-tCtx.broadcastedMessage:
@@ -1862,13 +1801,9 @@ func TestSignatureAnnouncementFullProofWhenRemoteProof(t *testing.T) {
t.Fatal("gossiper did not send channel update to remove peer")
}
- select {
- case err = <-tCtx.gossiper.ProcessLocalAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessLocalAnnouncement(
batch.nodeAnn1,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process local announcement")
- }
+ ))
if err != nil {
t.Fatalf("unable to process node ann:%v", err)
}
@@ -1878,26 +1813,18 @@ func TestSignatureAnnouncementFullProofWhenRemoteProof(t *testing.T) {
case <-time.After(2 * trickleDelay):
}
- select {
- case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, batch.chanUpdAnn2, remotePeer,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process remote announcement")
- }
+ ))
require.NoError(t, err, "unable to process channel update")
select {
case <-tCtx.broadcastedMessage:
t.Fatal("channel update announcement was broadcast")
case <-time.After(2 * trickleDelay):
}
- select {
- case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, batch.nodeAnn2, remotePeer,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process remote announcement")
- }
+ ))
require.NoError(t, err, "unable to process node ann")
select {
case <-tCtx.broadcastedMessage:
@@ -1907,22 +1834,14 @@ func TestSignatureAnnouncementFullProofWhenRemoteProof(t *testing.T) {
// Pretending that we receive local channel announcement from funding
// manager, thereby kick off the announcement exchange process.
- select {
- case err = <-tCtx.gossiper.ProcessLocalAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessLocalAnnouncement(
batch.localProofAnn,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process local announcement")
- }
+ ))
require.NoError(t, err, "unable to process local proof")
- select {
- case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, batch.remoteProofAnn, remotePeer,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process local announcement")
- }
+ ))
require.NoError(t, err, "unable to process remote proof")
// We expect the gossiper to send this message to the remote peer.
@@ -1961,13 +1880,9 @@ func TestSignatureAnnouncementFullProofWhenRemoteProof(t *testing.T) {
// Now give the gossiper the remote proof yet again. This should
// trigger a send of the full ChannelAnnouncement.
- select {
- case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, batch.remoteProofAnn, remotePeer,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process local announcement")
- }
+ ))
require.NoError(t, err, "unable to process remote proof")
// We expect the gossiper to send this message to the remote peer.
@@ -2259,13 +2174,9 @@ func TestForwardPrivateNodeAnnouncement(t *testing.T) {
)
pubKey := remoteKeyPriv1.PubKey()
- select {
- case err := <-tCtx.gossiper.ProcessLocalAnnouncement(chanAnn):
- if err != nil {
- t.Fatalf("unable to process local announcement: %v", err)
- }
- case <-time.After(2 * time.Second):
- t.Fatalf("local announcement not processed")
+ err = mustProcess(t, tCtx.gossiper.ProcessLocalAnnouncement(chanAnn))
+ if err != nil {
+ t.Fatalf("unable to process local announcement: %v", err)
}
// The gossiper should not broadcast the announcement due to it not
@@ -2279,14 +2190,9 @@ func TestForwardPrivateNodeAnnouncement(t *testing.T) {
nodeAnn, err := createNodeAnnouncement(remoteKeyPriv1, timestamp)
require.NoError(t, err, "unable to create node announcement")
- select {
- case err := <-tCtx.gossiper.ProcessLocalAnnouncement(nodeAnn):
- if err != nil {
- t.Fatalf("unable to process remote announcement: %v", err)
- }
- case <-time.After(2 * time.Second):
- t.Fatal("remote announcement not processed")
- }
+ _ = mustProcess(t, tCtx.gossiper.ProcessLocalAnnouncement(
+ nodeAnn,
+ ))
// The gossiper should also not broadcast the node announcement due to
// it not being part of any advertised channels.
@@ -2306,15 +2212,11 @@ func TestForwardPrivateNodeAnnouncement(t *testing.T) {
require.NoError(t, err, "unable to create remote channel announcement")
peer := &mockPeer{pubKey, nil, nil, atomic.Bool{}}
- select {
- case err := <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, remoteChanAnn, peer,
- ):
- if err != nil {
- t.Fatalf("unable to process remote announcement: %v", err)
- }
- case <-time.After(2 * time.Second):
- t.Fatal("remote announcement not processed")
+ ))
+ if err != nil {
+ t.Fatalf("unable to process remote announcement: %v", err)
}
select {
@@ -2329,15 +2231,11 @@ func TestForwardPrivateNodeAnnouncement(t *testing.T) {
nodeAnn, err = createNodeAnnouncement(remoteKeyPriv1, timestamp+1)
require.NoError(t, err, "unable to create node announcement")
- select {
- case err := <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, nodeAnn, peer,
- ):
- if err != nil {
- t.Fatalf("unable to process remote announcement: %v", err)
- }
- case <-time.After(2 * time.Second):
- t.Fatal("remote announcement not processed")
+ ))
+ if err != nil {
+ t.Fatalf("unable to process remote announcement: %v", err)
}
select {
@@ -2371,18 +2269,14 @@ func TestRejectZombieEdge(t *testing.T) {
errChan := tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, batch.chanAnn, remotePeer,
)
- select {
- case err := <-errChan:
- if isZombie && err != nil {
- t.Fatalf("expected to reject live channel "+
- "announcement with nil error: %v", err)
- }
- if !isZombie && err != nil {
- t.Fatalf("expected to process live channel "+
- "announcement: %v", err)
- }
- case <-time.After(time.Second):
- t.Fatal("expected to process channel announcement")
+ err := mustProcess(t, errChan)
+ if isZombie && err != nil {
+ t.Fatalf("expected to reject live channel "+
+ "announcement with nil error: %v", err)
+ }
+ if !isZombie && err != nil {
+ t.Fatalf("expected to process live channel "+
+ "announcement: %v", err)
}
select {
case <-tCtx.broadcastedMessage:
@@ -2400,18 +2294,14 @@ func TestRejectZombieEdge(t *testing.T) {
errChan = tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, batch.chanUpdAnn2, remotePeer,
)
- select {
- case err := <-errChan:
- if isZombie && err != nil {
- t.Fatalf("expected to reject zombie channel "+
- "update with nil error: %v", err)
- }
- if !isZombie && err != nil {
- t.Fatalf("expected to process live channel "+
- "update: %v", err)
- }
- case <-time.After(time.Second):
- t.Fatal("expected to process channel update")
+ err = mustProcess(t, errChan)
+ if isZombie && err != nil {
+ t.Fatalf("expected to reject zombie channel "+
+ "update with nil error: %v", err)
+ }
+ if !isZombie && err != nil {
+ t.Fatalf("expected to process live channel "+
+ "update: %v", err)
}
select {
case <-tCtx.broadcastedMessage:
@@ -2480,11 +2370,7 @@ func TestProcessZombieEdgeNowLive(t *testing.T) {
)
var err error
- select {
- case err = <-errChan:
- case <-time.After(time.Second):
- t.Fatal("expected to process announcement")
- }
+ err = mustProcess(t, errChan)
if expectsErr && err == nil {
t.Fatal("expected error when processing announcement")
}
@@ -2589,14 +2475,9 @@ func TestProcessZombieEdgeNowLive(t *testing.T) {
// After successfully processing the announcement, the channel update
// should have been processed and broadcast successfully as well.
- select {
- case err := <-updateErrChan:
- if err != nil {
- t.Fatalf("expected to process live channel update: %v",
- err)
- }
- case <-time.After(time.Second):
- t.Fatal("expected to process announcement")
+ err = mustProcess(t, updateErrChan)
+ if err != nil {
+ t.Fatalf("expected to process live channel update: %v", err)
}
select {
@@ -2650,9 +2531,9 @@ func TestReceiveRemoteChannelUpdateFirst(t *testing.T) {
case <-time.After(2 * trickleDelay):
}
- err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, batch.nodeAnn2, remotePeer,
- )
+ ))
require.NoError(t, err, "unable to process node ann")
select {
case <-tCtx.broadcastedMessage:
@@ -2682,7 +2563,9 @@ func TestReceiveRemoteChannelUpdateFirst(t *testing.T) {
// Recreate lightning network topology. Initialize router with channel
// between two nodes.
- err = <-tCtx.gossiper.ProcessLocalAnnouncement(batch.chanAnn)
+ err = mustProcess(t, tCtx.gossiper.ProcessLocalAnnouncement(
+ batch.chanAnn,
+ ))
if err != nil {
t.Fatalf("unable to process :%v", err)
}
@@ -2692,7 +2575,9 @@ func TestReceiveRemoteChannelUpdateFirst(t *testing.T) {
case <-time.After(2 * trickleDelay):
}
- err = <-tCtx.gossiper.ProcessLocalAnnouncement(batch.chanUpdAnn1)
+ err = mustProcess(t, tCtx.gossiper.ProcessLocalAnnouncement(
+ batch.chanUpdAnn1,
+ ))
if err != nil {
t.Fatalf("unable to process :%v", err)
}
@@ -2702,7 +2587,9 @@ func TestReceiveRemoteChannelUpdateFirst(t *testing.T) {
case <-time.After(2 * trickleDelay):
}
- err = <-tCtx.gossiper.ProcessLocalAnnouncement(batch.nodeAnn1)
+ err = mustProcess(t, tCtx.gossiper.ProcessLocalAnnouncement(
+ batch.nodeAnn1,
+ ))
if err != nil {
t.Fatalf("unable to process :%v", err)
}
@@ -2724,13 +2611,9 @@ func TestReceiveRemoteChannelUpdateFirst(t *testing.T) {
// At this point the remote ChannelUpdate we received earlier should
// be reprocessed, as we now have the necessary edge entry in the graph.
- select {
- case err := <-errRemoteAnn:
- if err != nil {
- t.Fatalf("error re-processing remote update: %v", err)
- }
- case <-time.After(2 * trickleDelay):
- t.Fatalf("remote update was not processed")
+ err = mustProcess(t, errRemoteAnn)
+ if err != nil {
+ t.Fatalf("error re-processing remote update: %v", err)
}
// Check that the ChannelEdgePolicy was added to the graph.
@@ -2750,7 +2633,9 @@ func TestReceiveRemoteChannelUpdateFirst(t *testing.T) {
// Pretending that we receive local channel announcement from funding
// manager, thereby kick off the announcement exchange process.
- err = <-tCtx.gossiper.ProcessLocalAnnouncement(batch.localProofAnn)
+ err = mustProcess(t, tCtx.gossiper.ProcessLocalAnnouncement(
+ batch.localProofAnn,
+ ))
if err != nil {
t.Fatalf("unable to process :%v", err)
}
@@ -2778,9 +2663,9 @@ func TestReceiveRemoteChannelUpdateFirst(t *testing.T) {
t.Fatal("wrong number of objects in storage")
}
- err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, batch.remoteProofAnn, remotePeer,
- )
+ ))
if err != nil {
t.Fatalf("unable to process :%v", err)
}
@@ -2836,13 +2721,9 @@ func TestExtraDataChannelAnnouncementValidation(t *testing.T) {
// We'll now send the announcement to the main gossiper. We should be
// able to validate this announcement to problem.
- select {
- case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, ca, remotePeer,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process remote announcement")
- }
+ ))
if err != nil {
t.Fatalf("unable to process :%v", err)
}
@@ -2881,31 +2762,19 @@ func TestExtraDataChannelUpdateValidation(t *testing.T) {
// We should be able to properly validate all three messages without
// any issue.
- select {
- case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, chanAnn, remotePeer,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process remote announcement")
- }
+ ))
require.NoError(t, err, "unable to process announcement")
- select {
- case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, chanUpdAnn1, remotePeer,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process remote announcement")
- }
+ ))
require.NoError(t, err, "unable to process announcement")
- select {
- case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, chanUpdAnn2, remotePeer,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process remote announcement")
- }
+ ))
require.NoError(t, err, "unable to process announcement")
}
@@ -2932,13 +2801,9 @@ func TestExtraDataNodeAnnouncementValidation(t *testing.T) {
)
require.NoError(t, err, "can't create node announcement")
- select {
- case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, nodeAnn, remotePeer,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process remote announcement")
- }
+ ))
require.NoError(t, err, "unable to process announcement")
}
@@ -2960,13 +2825,9 @@ func TestZeroTimestampNodeAnnouncementRejection(t *testing.T) {
require.NoError(t, err, "can't create node announcement")
// Processing the announcement should fail with a zero timestamp error.
- select {
- case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, nodeAnn, remotePeer,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process remote announcement")
- }
+ ))
require.Error(t, err)
require.Contains(t, err.Error(), "zero timestamp")
}
@@ -2989,13 +2850,9 @@ func TestZeroTimestampChannelUpdateRejection(t *testing.T) {
chanAnn, err := tCtx.createRemoteChannelAnnouncement(0)
require.NoError(t, err, "unable to create chan ann")
- select {
- case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, chanAnn, remotePeer,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process remote announcement")
- }
+ ))
require.NoError(t, err, "unable to process chan ann")
// Now create a channel update with a zero timestamp.
@@ -3003,13 +2860,9 @@ func TestZeroTimestampChannelUpdateRejection(t *testing.T) {
require.NoError(t, err, "unable to create chan update")
// Processing the update should fail with a zero timestamp error.
- select {
- case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, chanUpdAnn, remotePeer,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process remote announcement")
- }
+ ))
require.Error(t, err)
require.Contains(t, err.Error(), "zero timestamp")
}
@@ -3042,16 +2895,12 @@ func assertBroadcast(t *testing.T, ctx *testCtx, num int) []lnwire.Message {
// assertProcessAnnouncement is a helper method that checks that the result of
// processing an announcement is successful.
-func assertProcessAnnouncement(t *testing.T, result chan error) {
+func assertProcessAnnouncement(t *testing.T, result actor.Future[error]) {
t.Helper()
- select {
- case err := <-result:
- if err != nil {
- t.Fatalf("unable to process :%v", err)
- }
- case <-time.After(2 * time.Second):
- t.Fatal("did not process announcement")
+ err := mustProcess(t, result)
+ if err != nil {
+ t.Fatalf("unable to process :%v", err)
}
}
@@ -3182,13 +3031,9 @@ func TestNodeAnnouncementNoChannels(t *testing.T) {
remotePeer := &mockPeer{remoteKey, nil, nil, atomic.Bool{}}
// Process the remote node announcement.
- select {
- case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, batch.nodeAnn2, remotePeer,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process remote announcement")
- }
+ ))
require.NoError(t, err, "unable to process announcement")
// Since no channels or node announcements were already in the graph,
@@ -3201,32 +3046,20 @@ func TestNodeAnnouncementNoChannels(t *testing.T) {
// Now add the node's channel to the graph by processing the channel
// announcement and channel update.
- select {
- case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, batch.chanAnn, remotePeer,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process remote announcement")
- }
+ ))
require.NoError(t, err, "unable to process announcement")
- select {
- case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, batch.chanUpdAnn2, remotePeer,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process remote announcement")
- }
+ ))
require.NoError(t, err, "unable to process announcement")
// Now process the node announcement again.
- select {
- case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, batch.nodeAnn2, remotePeer,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process remote announcement")
- }
+ ))
require.NoError(t, err, "unable to process announcement")
// This time the node announcement should be forwarded. The same should
@@ -3241,13 +3074,9 @@ func TestNodeAnnouncementNoChannels(t *testing.T) {
// Processing the same node announcement again should be ignored, as it
// is stale.
- select {
- case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, batch.nodeAnn2, remotePeer,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process remote announcement")
- }
+ ))
require.NoError(t, err, "unable to process announcement")
select {
@@ -3277,11 +3106,7 @@ func TestOptionalFieldsChannelUpdateValidation(t *testing.T) {
chanAnn, err := tCtx.createRemoteChannelAnnouncement(chanUpdateHeight)
require.NoError(t, err, "can't create channel announcement")
- select {
- case err = <-processRemoteAnnouncement(ctx, chanAnn, nodePeer):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process remote announcement")
- }
+ err = mustProcess(t, processRemoteAnnouncement(ctx, chanAnn, nodePeer))
require.NoError(t, err, "unable to process announcement")
// The first update should fail from an invalid max HTLC field, which is
@@ -3297,11 +3122,9 @@ func TestOptionalFieldsChannelUpdateValidation(t *testing.T) {
t.Fatalf("unable to sign channel update: %v", err)
}
- select {
- case err = <-processRemoteAnnouncement(ctx, chanUpdAnn, nodePeer):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process remote announcement")
- }
+ err = mustProcess(t, processRemoteAnnouncement(
+ ctx, chanUpdAnn, nodePeer,
+ ))
if err == nil || !strings.Contains(err.Error(), "invalid max htlc") {
t.Fatalf("expected chan update to error, instead got %v", err)
}
@@ -3314,11 +3137,9 @@ func TestOptionalFieldsChannelUpdateValidation(t *testing.T) {
t.Fatalf("unable to sign channel update: %v", err)
}
- select {
- case err = <-processRemoteAnnouncement(ctx, chanUpdAnn, nodePeer):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process remote announcement")
- }
+ err = mustProcess(t, processRemoteAnnouncement(
+ ctx, chanUpdAnn, nodePeer,
+ ))
if err == nil || !strings.Contains(err.Error(), "invalid max htlc") {
t.Fatalf("expected chan update to error, instead got %v", err)
}
@@ -3330,11 +3151,9 @@ func TestOptionalFieldsChannelUpdateValidation(t *testing.T) {
t.Fatalf("unable to sign channel update: %v", err)
}
- select {
- case err = <-processRemoteAnnouncement(ctx, chanUpdAnn, nodePeer):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process remote announcement")
- }
+ err = mustProcess(t, processRemoteAnnouncement(
+ ctx, chanUpdAnn, nodePeer,
+ ))
require.ErrorContains(t, err, "max htlc flag not set")
// The final update should succeed.
@@ -3347,11 +3166,9 @@ func TestOptionalFieldsChannelUpdateValidation(t *testing.T) {
t.Fatalf("unable to sign channel update: %v", err)
}
- select {
- case err = <-processRemoteAnnouncement(ctx, chanUpdAnn, nodePeer):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process remote announcement")
- }
+ err = mustProcess(t, processRemoteAnnouncement(
+ ctx, chanUpdAnn, nodePeer,
+ ))
require.NoError(t, err, "expected update to be processed")
}
@@ -3417,11 +3234,9 @@ func TestSendChannelUpdateReliably(t *testing.T) {
// Process the channel announcement for which we'll send a channel
// update for.
- select {
- case err = <-tCtx.gossiper.ProcessLocalAnnouncement(batch.chanAnn):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process local channel announcement")
- }
+ err = mustProcess(t, tCtx.gossiper.ProcessLocalAnnouncement(
+ batch.chanAnn,
+ ))
require.NoError(t, err, "unable to process local channel announcement")
// It should not be broadcast due to not having an announcement proof.
@@ -3432,11 +3247,9 @@ func TestSendChannelUpdateReliably(t *testing.T) {
}
// Now, we'll process the channel update.
- select {
- case err = <-tCtx.gossiper.ProcessLocalAnnouncement(batch.chanUpdAnn1):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process local channel update")
- }
+ err = mustProcess(t, tCtx.gossiper.ProcessLocalAnnouncement(
+ batch.chanUpdAnn1,
+ ))
require.NoError(t, err, "unable to process local channel update")
// It should also not be broadcast due to the announcement not having an
@@ -3490,13 +3303,9 @@ func TestSendChannelUpdateReliably(t *testing.T) {
}
// With the new update created, we'll go ahead and process it.
- select {
- case err = <-tCtx.gossiper.ProcessLocalAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessLocalAnnouncement(
batch.chanUpdAnn1,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process local channel update")
- }
+ ))
require.NoError(t, err, "unable to process local channel update")
// It should also not be broadcast due to the announcement not having an
@@ -3529,13 +3338,9 @@ func TestSendChannelUpdateReliably(t *testing.T) {
// We'll then exchange proofs with the remote peer in order to announce
// the channel.
- select {
- case err = <-tCtx.gossiper.ProcessLocalAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessLocalAnnouncement(
batch.localProofAnn,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process local channel proof")
- }
+ ))
require.NoError(t, err, "unable to process local channel proof")
// No messages should be broadcast as we don't have the full proof yet.
@@ -3548,13 +3353,9 @@ func TestSendChannelUpdateReliably(t *testing.T) {
// Our proof should be sent to the remote peer however.
assertMsgSent(batch.localProofAnn)
- select {
- case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, batch.remoteProofAnn, remotePeer,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process remote channel proof")
- }
+ ))
require.NoError(t, err, "unable to process remote channel proof")
// Now that we've constructed our full proof, we can assert that the
@@ -3582,13 +3383,9 @@ func TestSendChannelUpdateReliably(t *testing.T) {
// Process the new channel update. It should not be sent to the peer
// directly since the reliable sender only applies when the channel is
// not announced.
- select {
- case err = <-tCtx.gossiper.ProcessLocalAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessLocalAnnouncement(
newChannelUpdate,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process local channel update")
- }
+ ))
require.NoError(t, err, "unable to process local channel update")
select {
case <-tCtx.broadcastedMessage:
@@ -3659,14 +3456,9 @@ func sendLocalMsg(t *testing.T, ctx *testCtx, msg lnwire.Message,
t.Helper()
- var err error
- select {
- case err = <-ctx.gossiper.ProcessLocalAnnouncement(
+ err := mustProcess(t, ctx.gossiper.ProcessLocalAnnouncement(
msg, optionalMsgFields...,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process local announcement")
- }
+ ))
require.NoError(t, err, "unable to process channel msg")
}
@@ -3675,16 +3467,29 @@ func sendRemoteMsg(t *testing.T, ctx *testCtx, msg lnwire.Message,
t.Helper()
- select {
- case err := <-ctx.gossiper.ProcessRemoteAnnouncement(
+ err := mustProcess(t, ctx.gossiper.ProcessRemoteAnnouncement(
t.Context(), msg, remotePeer,
- ):
- if err != nil {
- t.Fatalf("unable to process channel msg: %v", err)
- }
- case <-time.After(2 * time.Second):
- t.Fatal("did not process local announcement")
+ ))
+ if err != nil {
+ t.Fatalf("unable to process channel msg: %v", err)
+ }
+}
+
+// mustProcess awaits a gossip future with a 2-second deadline, failing
+// the test immediately if the deadline is exceeded.
+func mustProcess(t *testing.T, f actor.Future[error]) error {
+ t.Helper()
+
+ ctx, cancel := context.WithTimeout(t.Context(), 2*time.Second)
+ defer cancel()
+
+ err := AwaitGossipResult(ctx, f)
+ if errors.Is(err, context.DeadlineExceeded) {
+ t.Fatal("gossip message was not processed within deadline")
+ return nil
}
+
+ return err
}
func assertBroadcastMsg(t *testing.T, ctx *testCtx,
@@ -4094,7 +3899,7 @@ func TestBroadcastAnnsAfterGraphSynced(t *testing.T) {
nodePeer := &mockPeer{
remoteKeyPriv1.PubKey(), nil, nil, atomic.Bool{},
}
- var errChan chan error
+ var errChan actor.Future[error]
if isRemote {
errChan = tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, msg, nodePeer,
@@ -4103,14 +3908,10 @@ func TestBroadcastAnnsAfterGraphSynced(t *testing.T) {
errChan = tCtx.gossiper.ProcessLocalAnnouncement(msg)
}
- select {
- case err := <-errChan:
- if err != nil {
- t.Fatalf("unable to process gossip message: %v",
- err)
- }
- case <-time.After(2 * time.Second):
- t.Fatal("gossip message not processed")
+ err := mustProcess(t, errChan)
+ if err != nil {
+ t.Fatalf("unable to process gossip message: %v",
+ err)
}
select {
@@ -4196,35 +3997,23 @@ func TestRateLimitDeDup(t *testing.T) {
nodePeer1 := &mockPeer{
remoteKeyPriv1.PubKey(), nil, nil, atomic.Bool{},
}
- select {
- case err := <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, batch.chanAnn, nodePeer1,
- ):
- require.NoError(t, err)
- case <-time.After(time.Second):
- t.Fatal("remote announcement not processed")
- }
+ ))
+ require.NoError(t, err)
- select {
- case err := <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, batch.chanUpdAnn1, nodePeer1,
- ):
- require.NoError(t, err)
- case <-time.After(time.Second):
- t.Fatal("remote announcement not processed")
- }
+ ))
+ require.NoError(t, err)
nodePeer2 := &mockPeer{
remoteKeyPriv2.PubKey(), nil, nil, atomic.Bool{},
}
- select {
- case err := <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, batch.chanUpdAnn2, nodePeer2,
- ):
- require.NoError(t, err)
- case <-time.After(time.Second):
- t.Fatal("remote announcement not processed")
- }
+ ))
+ require.NoError(t, err)
timeout := time.After(2 * trickleDelay)
for i := 0; i < 3; i++ {
@@ -4321,14 +4110,10 @@ func TestRateLimitDeDup(t *testing.T) {
}
processUpdate := func(msg lnwire.Message, peer lnpeer.Peer) {
- select {
- case err := <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err := mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, msg, peer,
- ):
- require.NoError(t, err)
- case <-time.After(time.Second):
- t.Fatal("remote announcement not processed")
- }
+ ))
+ require.NoError(t, err)
}
// Show that the last update was broadcast.
@@ -4374,35 +4159,23 @@ func TestRateLimitChannelUpdates(t *testing.T) {
nodePeer1 := &mockPeer{
remoteKeyPriv1.PubKey(), nil, nil, atomic.Bool{},
}
- select {
- case err := <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, batch.chanAnn, nodePeer1,
- ):
- require.NoError(t, err)
- case <-time.After(time.Second):
- t.Fatal("remote announcement not processed")
- }
+ ))
+ require.NoError(t, err)
- select {
- case err := <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, batch.chanUpdAnn1, nodePeer1,
- ):
- require.NoError(t, err)
- case <-time.After(time.Second):
- t.Fatal("remote announcement not processed")
- }
+ ))
+ require.NoError(t, err)
nodePeer2 := &mockPeer{
remoteKeyPriv2.PubKey(), nil, nil, atomic.Bool{},
}
- select {
- case err := <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, batch.chanUpdAnn2, nodePeer2,
- ):
- require.NoError(t, err)
- case <-time.After(time.Second):
- t.Fatal("remote announcement not processed")
- }
+ ))
+ require.NoError(t, err)
timeout := time.After(2 * trickleDelay)
for i := 0; i < 3; i++ {
@@ -4424,14 +4197,10 @@ func TestRateLimitChannelUpdates(t *testing.T) {
t.Helper()
- select {
- case err := <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err := mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, update, peer,
- ):
- require.NoError(t, err)
- case <-time.After(time.Second):
- t.Fatal("remote announcement not processed")
- }
+ ))
+ require.NoError(t, err)
select {
case <-tCtx.broadcastedMessage:
@@ -4521,13 +4290,9 @@ func TestIgnoreOwnAnnouncement(t *testing.T) {
remotePeer := &mockPeer{remoteKey, nil, nil, atomic.Bool{}}
// Try to let the remote peer tell us about the channel we are part of.
- select {
- case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, batch.chanAnn, remotePeer,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process remote announcement")
- }
+ ))
// It should be ignored, since the gossiper only cares about local
// announcements for its own channels.
if err == nil || !strings.Contains(err.Error(), "ignoring") {
@@ -4537,11 +4302,9 @@ func TestIgnoreOwnAnnouncement(t *testing.T) {
// Now do the local channelannouncement, node announcement, and channel
// update. No messages should be broadcast yet, since we don't have
// the announcement signatures.
- select {
- case err = <-tCtx.gossiper.ProcessLocalAnnouncement(batch.chanAnn):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process local announcement")
- }
+ err = mustProcess(t, tCtx.gossiper.ProcessLocalAnnouncement(
+ batch.chanAnn,
+ ))
require.NoError(t, err, "unable to process channel ann")
select {
case <-tCtx.broadcastedMessage:
@@ -4549,11 +4312,9 @@ func TestIgnoreOwnAnnouncement(t *testing.T) {
case <-time.After(2 * trickleDelay):
}
- select {
- case err = <-tCtx.gossiper.ProcessLocalAnnouncement(batch.chanUpdAnn1):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process local announcement")
- }
+ err = mustProcess(t, tCtx.gossiper.ProcessLocalAnnouncement(
+ batch.chanUpdAnn1,
+ ))
require.NoError(t, err, "unable to process channel update")
select {
case <-tCtx.broadcastedMessage:
@@ -4561,11 +4322,9 @@ func TestIgnoreOwnAnnouncement(t *testing.T) {
case <-time.After(2 * trickleDelay):
}
- select {
- case err = <-tCtx.gossiper.ProcessLocalAnnouncement(batch.nodeAnn1):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process local announcement")
- }
+ err = mustProcess(t, tCtx.gossiper.ProcessLocalAnnouncement(
+ batch.nodeAnn1,
+ ))
require.NoError(t, err, "unable to process node ann")
select {
case <-tCtx.broadcastedMessage:
@@ -4574,13 +4333,9 @@ func TestIgnoreOwnAnnouncement(t *testing.T) {
}
// We should accept the remote's channel update and node announcement.
- select {
- case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, batch.chanUpdAnn2, remotePeer,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process remote announcement")
- }
+ ))
require.NoError(t, err, "unable to process channel update")
select {
case <-tCtx.broadcastedMessage:
@@ -4588,13 +4343,9 @@ func TestIgnoreOwnAnnouncement(t *testing.T) {
case <-time.After(2 * trickleDelay):
}
- select {
- case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, batch.nodeAnn2, remotePeer,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process remote announcement")
- }
+ ))
require.NoError(t, err, "unable to process node ann")
select {
case <-tCtx.broadcastedMessage:
@@ -4604,13 +4355,9 @@ func TestIgnoreOwnAnnouncement(t *testing.T) {
// Now we exchange the proofs, the messages will be broadcasted to the
// network.
- select {
- case err = <-tCtx.gossiper.ProcessLocalAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessLocalAnnouncement(
batch.localProofAnn,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process remote announcement")
- }
+ ))
require.NoError(t, err, "unable to process local proof")
select {
@@ -4619,13 +4366,9 @@ func TestIgnoreOwnAnnouncement(t *testing.T) {
case <-time.After(2 * trickleDelay):
}
- select {
- case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, batch.remoteProofAnn, remotePeer,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process remote announcement")
- }
+ ))
require.NoError(t, err, "unable to process remote proof")
for i := 0; i < 5; i++ {
@@ -4638,13 +4381,9 @@ func TestIgnoreOwnAnnouncement(t *testing.T) {
// Finally, we again check that we'll ignore the remote giving us
// announcements about our own channel.
- select {
- case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, batch.chanAnn, remotePeer,
- ):
- case <-time.After(2 * time.Second):
- t.Fatal("did not process remote announcement")
- }
+ ))
if err == nil || !strings.Contains(err.Error(), "ignoring") {
t.Fatalf("expected gossiper to ignore announcement, got: %v", err)
}
@@ -4675,26 +4414,18 @@ func TestRejectCacheChannelAnn(t *testing.T) {
tCtx.router.queueValidationFail(chanID)
// If we process the batch the first time we should get an error.
- select {
- case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, batch.chanAnn, remotePeer,
- ):
- require.NotNil(t, err)
- case <-time.After(2 * time.Second):
- t.Fatal("did not process remote announcement")
- }
+ ))
+ require.NotNil(t, err)
// If we process it a *second* time, then we should get an error saying
// we rejected it already.
- select {
- case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, batch.chanAnn, remotePeer,
- ):
- errStr := err.Error()
- require.Contains(t, errStr, "recently rejected")
- case <-time.After(2 * time.Second):
- t.Fatal("did not process remote announcement")
- }
+ ))
+ errStr := err.Error()
+ require.Contains(t, errStr, "recently rejected")
}
// TestFutureMsgCacheEviction checks that when the cache's capacity is reached,
@@ -4759,15 +4490,10 @@ func TestChanAnnBanningNonChanPeer(t *testing.T) {
)
require.NoError(t, err, "can't create channel announcement")
- select {
- case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, ca, nodePeer1,
- ):
- require.ErrorIs(t, err, ErrInvalidFundingOutput)
-
- case <-time.After(2 * time.Second):
- t.Fatalf("remote announcement not processed")
- }
+ ))
+ require.ErrorIs(t, err, ErrInvalidFundingOutput)
}
// The peer should be banned now.
@@ -4783,16 +4509,10 @@ func TestChanAnnBanningNonChanPeer(t *testing.T) {
)
require.NoError(t, err, "can't create channel announcement")
- select {
- case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, ca, nodePeer2,
- ):
-
- require.ErrorIs(t, err, ErrChannelSpent)
-
- case <-time.After(2 * time.Second):
- t.Fatalf("remote announcement not processed")
- }
+ ))
+ require.ErrorIs(t, err, ErrChannelSpent)
// Check that the announcement's scid is marked as closed.
isClosed, err := tCtx.gossiper.cfg.ScidCloser.IsClosedScid(
@@ -4816,16 +4536,11 @@ func TestChanAnnBanningNonChanPeer(t *testing.T) {
// here.
_ = tCtx.router.MarkEdgeLive(lnwire.GossipVersion1, ca.ShortChannelID)
- select {
- case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, ca, nodePeer2,
- ):
-
- require.ErrorContains(t, err, "ignoring closed channel")
+ ))
+ require.ErrorContains(t, err, "ignoring closed channel")
- case <-time.After(2 * time.Second):
- t.Fatalf("remote announcement not processed")
- }
}
// TestChanAnnBanningChanPeer asserts that channel peers that are banned don't
@@ -4850,15 +4565,11 @@ func TestChanAnnBanningChanPeer(t *testing.T) {
)
require.NoError(t, err, "can't create channel announcement")
- select {
- case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
ctx, ca, nodePeer,
- ):
- require.ErrorIs(t, err, ErrInvalidFundingOutput)
+ ))
+ require.ErrorIs(t, err, ErrInvalidFundingOutput)
- case <-time.After(2 * time.Second):
- t.Fatalf("remote announcement not processed")
- }
}
// The peer should be banned now.
@@ -4915,13 +4626,13 @@ func assertChanChainRejection(t *testing.T, ctx *testCtx,
t.Helper()
nodePeer := &mockPeer{bitcoinKeyPub2, nil, nil, atomic.Bool{}}
- errChan := make(chan error, 1)
+ errPromise := actor.NewPromise[error]()
nMsg := &networkMsg{
- msg: edge,
- isRemote: true,
- peer: nodePeer,
- source: nodePeer.IdentityKey(),
- err: errChan,
+ msg: edge,
+ isRemote: true,
+ peer: nodePeer,
+ source: nodePeer.IdentityKey(),
+ errPromise: errPromise,
}
_, added := ctx.gossiper.handleChanAnnouncement(
@@ -4929,12 +4640,8 @@ func assertChanChainRejection(t *testing.T, ctx *testCtx,
)
require.False(t, added)
- select {
- case err := <-errChan:
- require.ErrorIs(t, err, expectedErr)
- case <-time.After(2 * time.Second):
- t.Fatal("channel announcement not processed")
- }
+ err := mustProcess(t, errPromise.Future())
+ require.ErrorIs(t, err, expectedErr)
// This channel should now be present in the zombie channel index.
isZombie, err := ctx.router.IsZombieEdge(edge.ShortChannelID)
@@ -4950,14 +4657,14 @@ func TestRecoverGossipPanic(t *testing.T) {
testCases := []struct {
name string
- setupMsg func() (*networkMsg, chan error)
+ setupMsg func() (*networkMsg, actor.Future[error])
checkError bool
}{
{
name: "panic with full message context",
- setupMsg: func() (*networkMsg, chan error) {
- errChan := make(chan error, 1)
- return &networkMsg{
+ setupMsg: func() (*networkMsg, actor.Future[error]) {
+ promise := actor.NewPromise[error]()
+ nMsg := &networkMsg{
msg: &lnwire.ChannelUpdate1{
Timestamp: testTimestamp,
},
@@ -4965,32 +4672,36 @@ func TestRecoverGossipPanic(t *testing.T) {
remoteKeyPub1, nil, nil,
atomic.Bool{},
},
- err: errChan,
- }, errChan
+ errPromise: promise,
+ }
+
+ return nMsg, promise.Future()
},
checkError: true,
},
{
name: "panic with nil message",
- setupMsg: func() (*networkMsg, chan error) {
- errChan := make(chan error, 1)
- return &networkMsg{
- msg: nil,
- peer: nil,
- err: errChan,
- }, errChan
+ setupMsg: func() (*networkMsg, actor.Future[error]) {
+ promise := actor.NewPromise[error]()
+ nMsg := &networkMsg{
+ msg: nil,
+ peer: nil,
+ errPromise: promise,
+ }
+
+ return nMsg, promise.Future()
},
checkError: true,
},
{
- name: "panic with nil error channel",
- setupMsg: func() (*networkMsg, chan error) {
+ name: "panic with nil error promise",
+ setupMsg: func() (*networkMsg, actor.Future[error]) {
return &networkMsg{
msg: &lnwire.ChannelUpdate1{
Timestamp: testTimestamp,
},
- peer: nil,
- err: nil,
+ peer: nil,
+ errPromise: nil,
}, nil
},
checkError: false,
@@ -5042,18 +4753,10 @@ func TestRecoverGossipPanic(t *testing.T) {
"error but errChan is nil")
}
if tc.checkError && errChan != nil {
- select {
- case err := <-errChan:
- require.Error(t, err)
- require.Contains(
- t, err.Error(), "panic while",
- )
- require.Contains(
- t, err.Error(), "test panic",
- )
- case <-time.After(time.Second):
- t.Fatal("timeout waiting for error")
- }
+ err := mustProcess(t, errChan)
+ require.Error(t, err)
+ require.Contains(t, err.Error(), "panic while")
+ require.Contains(t, err.Error(), "test panic")
}
})
}
@@ -5068,13 +4771,13 @@ func TestRecoverGossipPanicBlockedErrorChannel(t *testing.T) {
ctx, err := createTestCtx(t, proofMatureDelta, false)
require.NoError(t, err)
- // Create an UNBUFFERED channel and don't read from it.
- errChan := make(chan error)
-
+ // The Promise-based design means Complete() is always non-blocking,
+ // so panic recovery never hangs regardless of whether the caller
+ // awaits the result.
nMsg := &networkMsg{
- msg: &lnwire.ChannelUpdate1{Timestamp: testTimestamp},
- peer: &mockPeer{remoteKeyPub1, nil, nil, atomic.Bool{}},
- err: errChan,
+ msg: &lnwire.ChannelUpdate1{Timestamp: testTimestamp},
+ peer: &mockPeer{remoteKeyPub1, nil, nil, atomic.Bool{}},
+ errPromise: actor.NewPromise[error](),
}
// Initialize a proper job so CompleteJob has a slot to return.
@@ -5144,13 +4847,13 @@ func TestRecoverGossipPanicSignalsDependents(t *testing.T) {
// Now simulate the parent job panicking and recovering.
// The recovery should call SignalDependents.
- errChan := make(chan error, 1)
+ errPromise := actor.NewPromise[error]()
nMsg := &networkMsg{
msg: chanAnn,
peer: &mockPeer{
remoteKeyPub1, nil, nil, atomic.Bool{},
},
- err: errChan,
+ errPromise: errPromise,
}
panicked := make(chan struct{})
@@ -5170,15 +4873,11 @@ func TestRecoverGossipPanicSignalsDependents(t *testing.T) {
t.Fatal("timeout waiting for panic recovery")
}
- // Verify error was sent back on the parent's error channel.
- select {
- case err := <-errChan:
- require.Error(t, err)
- require.Contains(t, err.Error(), "panic while")
- require.Contains(t, err.Error(), "parent job panic")
- case <-time.After(time.Second):
- t.Fatal("timeout waiting for error on parent")
- }
+ // Verify error was sent back on the parent's error promise.
+ err = mustProcess(t, errPromise.Future())
+ require.Error(t, err)
+ require.Contains(t, err.Error(), "panic while")
+ require.Contains(t, err.Error(), "parent job panic")
// The child job should now be unblocked because SignalDependents
// was called during panic recovery.
@@ -5212,13 +4911,13 @@ func TestRecoverGossipPanicNilJobID(t *testing.T) {
ShortChannelID: lnwire.NewShortChanIDFromInt(12345),
}
- errChan := make(chan error, 1)
+ errPromise := actor.NewPromise[error]()
nMsg := &networkMsg{
msg: annSigs,
peer: &mockPeer{
remoteKeyPub1, nil, nil, atomic.Bool{},
},
- err: errChan,
+ errPromise: errPromise,
}
// Call finalizeGossipProcessing with nil jobID (simulating the
@@ -5241,14 +4940,10 @@ func TestRecoverGossipPanicNilJobID(t *testing.T) {
}
// Verify error was sent back.
- select {
- case err := <-errChan:
- require.Error(t, err)
- require.Contains(t, err.Error(), "panic while")
- require.Contains(t, err.Error(), "announce signatures panic")
- case <-time.After(time.Second):
- t.Fatal("timeout waiting for error")
- }
+ err = mustProcess(t, errPromise.Future())
+ require.Error(t, err)
+ require.Contains(t, err.Error(), "panic while")
+ require.Contains(t, err.Error(), "announce signatures panic")
}
// TestGossiperShutdownWrongChainAnnouncement tests that the gossiper can shut
@@ -5340,14 +5035,10 @@ func TestGossipSyncerRace(t *testing.T) {
TimestampRange: 3600,
}
- select {
- case err := <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err := mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
t.Context(), gossipTimestampRange, nodePeer,
- ):
- errCh <- err
- case <-time.After(2 * time.Second):
- errCh <- fmt.Errorf("gossip message not processed")
- }
+ ))
+ errCh <- err
}()
// Send a channel announcement from the remote peer, which will be
@@ -5355,14 +5046,10 @@ func TestGossipSyncerRace(t *testing.T) {
ca, err := tCtx.createRemoteChannelAnnouncement(0)
require.NoError(t, err)
- select {
- case err := <-tCtx.gossiper.ProcessRemoteAnnouncement(
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
t.Context(), ca, nodePeer,
- ):
- require.NoError(t, err)
- case <-time.After(2 * time.Second):
- t.Fatal("remote announcement not processed")
- }
+ ))
+ require.NoError(t, err)
// After the trickle delay, the channel announcement is flushed to the
// network. At the same time, the peer sends a GossipTimestampRange
@@ -5381,3 +5068,145 @@ func TestGossipSyncerRace(t *testing.T) {
t.Fatal("timeout waiting for gossip message processing")
}
}
+
+// TestPrematureAnnouncementProcessing checks that a channel announcement
+// carrying a future block height is correctly deferred via isPremature and
+// then re-processed once the target block arrives — without deadlocking the
+// gossiper. This is a regression test for the Network Isolation Attack where
+// a premature announcement could block the gossiper by sending to an already-
+// full chan error twice. actor.Promise.Complete is idempotent via sync.Once,
+// so the second completion is a safe no-op.
+func TestPrematureAnnouncementProcessing(t *testing.T) {
+ t.Parallel()
+
+ // Start the gossiper at block height 100.
+ const startHeight = 100
+ tCtx, err := createTestCtx(t, startHeight, false)
+ require.NoError(t, err)
+
+ nodePeer := &mockPeer{remoteKeyPriv1.PubKey(), nil, nil, atomic.Bool{}}
+
+ // Create a channel announcement at a future block height (200 > 100).
+ // The default fundingTxPrepTypeGood option pre-registers chain mock
+ // expectations for height 200, which will be consumed when the
+ // announcement is re-processed after the block arrives.
+ futureHeight := uint32(200)
+ prematureAnn, err := tCtx.createRemoteChannelAnnouncement(futureHeight)
+ require.NoError(t, err)
+
+ // Submit the premature announcement. The gossiper should accept it
+ // immediately with a nil error (deferred to future block), not block.
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
+ t.Context(), prematureAnn, nodePeer,
+ ))
+ require.NoError(t, err)
+
+ // Advance the block height to 200. This triggers resendFutureMessages,
+ // which re-queues the cached announcement copy into the processing
+ // pipeline. The copy carries a fresh actor.Promise whose Complete call
+ // is idempotent — unlike chan error, a second completion never blocks.
+ tCtx.notifier.notifyBlock(chainhash.Hash{}, futureHeight)
+
+ // Wait for the announcement to be broadcast. This confirms the gossiper
+ // re-processed the deferred announcement and remains fully operational.
+ select {
+ case <-tCtx.broadcastedMessage:
+ case <-time.After(2 * trickleDelay):
+ t.Fatal("premature announcement was not " +
+ "broadcast after block height advanced")
+ }
+
+ // Verify the gossiper is still live by processing a second normal
+ // announcement at the current block height. This would time out if
+ // the gossiper's networkHandler goroutine were blocked.
+ normalAnn, err := tCtx.createRemoteChannelAnnouncement(startHeight)
+ require.NoError(t, err)
+
+ err = mustProcess(t, tCtx.gossiper.ProcessRemoteAnnouncement(
+ t.Context(), normalAnn, nodePeer,
+ ))
+ require.NoError(t, err)
+}
+
+// TestProcessRemoteAnnouncementPeerQuit verifies that
+// ProcessRemoteAnnouncement completes the returned future with ErrPeerQuitting
+// when the peer's quit channel is closed before the message can be enqueued.
+func TestProcessRemoteAnnouncementPeerQuit(t *testing.T) {
+ t.Parallel()
+
+ // Construct a gossiper without starting it so that nobody reads from
+ // networkMsgs. This forces the send in the select to block, making the
+ // peer quit signal the only ready case.
+ gossiper := New(Config{
+ ChainParams: &chaincfg.MainNetParams,
+ }, selfKeyDesc)
+
+ // Create a peer whose quit channel is already closed.
+ quitChan := make(chan struct{})
+ close(quitChan)
+ peer := &mockPeer{
+ pk: remoteKeyPriv1.PubKey(),
+ quit: quitChan,
+ }
+
+ f := gossiper.ProcessRemoteAnnouncement(
+ t.Context(), &lnwire.ChannelUpdate1{}, peer,
+ )
+
+ err := mustProcess(t, f)
+ require.ErrorIs(t, err, ErrPeerQuitting)
+}
+
+// TestProcessRemoteAnnouncementCtxCancel verifies that
+// ProcessRemoteAnnouncement completes the returned future with the context
+// error when the context is cancelled before the message can be enqueued.
+func TestProcessRemoteAnnouncementCtxCancel(t *testing.T) {
+ t.Parallel()
+
+ gossiper := New(Config{
+ ChainParams: &chaincfg.MainNetParams,
+ }, selfKeyDesc)
+
+ peer := &mockPeer{
+ pk: remoteKeyPriv1.PubKey(),
+ quit: make(chan struct{}),
+ }
+
+ // Cancel the context before calling ProcessRemoteAnnouncement.
+ ctx, cancel := context.WithCancel(t.Context())
+ cancel()
+
+ f := gossiper.ProcessRemoteAnnouncement(
+ ctx, &lnwire.ChannelUpdate1{}, peer,
+ )
+
+ err := mustProcess(t, f)
+ require.ErrorIs(t, err, context.Canceled)
+}
+
+// TestProcessRemoteAnnouncementGossiperQuit verifies that
+// ProcessRemoteAnnouncement completes the returned future with
+// ErrGossiperShuttingDown when the gossiper's quit channel is closed before
+// the message can be enqueued.
+func TestProcessRemoteAnnouncementGossiperQuit(t *testing.T) {
+ t.Parallel()
+
+ gossiper := New(Config{
+ ChainParams: &chaincfg.MainNetParams,
+ }, selfKeyDesc)
+
+ // Close the gossiper's quit channel to simulate shutdown.
+ close(gossiper.quit)
+
+ peer := &mockPeer{
+ pk: remoteKeyPriv1.PubKey(),
+ quit: make(chan struct{}),
+ }
+
+ f := gossiper.ProcessRemoteAnnouncement(
+ t.Context(), &lnwire.ChannelUpdate1{}, peer,
+ )
+
+ err := mustProcess(t, f)
+ require.ErrorIs(t, err, ErrGossiperShuttingDown)
+}
diff --git a/discovery/syncer.go b/discovery/syncer.go
index 834106b..7ffe7f6 100644
--- a/discovery/syncer.go
+++ b/discovery/syncer.go
@@ -13,10 +13,12 @@ import (
"time"
"github.com/btcsuite/btcd/chaincfg/chainhash"
+ "github.com/lightningnetwork/lnd/actor"
"github.com/lightningnetwork/lnd/fn/v2"
"github.com/lightningnetwork/lnd/graph"
graphdb "github.com/lightningnetwork/lnd/graph/db"
"github.com/lightningnetwork/lnd/lnpeer"
+ "github.com/lightningnetwork/lnd/lnutils"
"github.com/lightningnetwork/lnd/lnwire"
"golang.org/x/time/rate"
)
@@ -210,7 +212,7 @@ var (
// syncTransitionReq encapsulates a request for a gossip syncer sync transition.
type syncTransitionReq struct {
newSyncType SyncerType
- errChan chan error
+ errPromise actor.Promise[error]
}
// historicalSyncReq encapsulates a request for a gossip syncer to perform a
@@ -699,7 +701,10 @@ func (g *GossipSyncer) channelGraphSyncer(ctx context.Context) {
case syncerIdle:
select {
case req := <-g.syncTransitionReqs:
- req.errChan <- g.handleSyncTransition(ctx, req)
+ completeGossipResult(
+ req.errPromise,
+ g.handleSyncTransition(ctx, req),
+ )
case req := <-g.historicalSyncReqs:
g.handleHistoricalSync(req)
@@ -1776,11 +1781,12 @@ func (g *GossipSyncer) ResetSyncedSignal() chan struct{} {
// NOTE: This can only be done once the gossip syncer has reached its final
// chansSynced state.
func (g *GossipSyncer) ProcessSyncTransition(newSyncType SyncerType) error {
- errChan := make(chan error, 1)
+ promise := actor.NewPromise[error]()
+
select {
case g.syncTransitionReqs <- &syncTransitionReq{
newSyncType: newSyncType,
- errChan: errChan,
+ errPromise: promise,
}:
case <-time.After(syncTransitionTimeout):
return ErrSyncTransitionTimeout
@@ -1788,12 +1794,25 @@ func (g *GossipSyncer) ProcessSyncTransition(newSyncType SyncerType) error {
return ErrGossipSyncerExiting
}
- select {
- case err := <-errChan:
- return err
- case <-g.cg.Done():
+ // Derive a context from the syncer's quit channel so the await exits
+ // only when the syncer itself shuts down. This matches the prior
+ // errChan-based behavior, which had no upper bound on the time spent
+ // waiting for the syncer to process the transition request once it had
+ // been accepted onto the queue. The syncTransitionTimeout above bounds
+ // only the enqueue step, as it did before this migration.
+ quitCtx, quitCancel := lnutils.ContextFromQuit(g.cg.Done())
+ defer quitCancel()
+
+ err := AwaitGossipResult(quitCtx, promise.Future())
+
+ // Re-map the bridge context cancellation back to the historical
+ // sentinel so any caller (or third-party fork) using errors.Is to
+ // detect syncer shutdown continues to match.
+ if errors.Is(err, context.Canceled) {
return ErrGossipSyncerExiting
}
+
+ return err
}
// handleSyncTransition handles a new sync type transition request.
diff --git a/docs/release-notes/release-notes-0.21.0.md b/docs/release-notes/release-notes-0.21.0.md
index 608aada..2015e06 100644
--- a/docs/release-notes/release-notes-0.21.0.md
+++ b/docs/release-notes/release-notes-0.21.0.md
@@ -383,6 +383,14 @@
to accommodate buried activation (and modified RPC `getdeploymentinfo`
response) beginning in Bitcoin Core v32.
+* [Migrated gossip result handling from `chan error` to
+ `actor.Future[error]`](https://github.com/lightningnetwork/lnd/pull/10589).
+ The three buffered-channel patterns in the discovery package are replaced
+ with idempotent promises, eliminating a class of latent deadlock bugs when a
+ deferred message copy was re-enqueued and processed a second time. A new
+ `lnutils.ContextFromQuit` helper bridges the existing `quit` channels to
+ `context.Context`, so all gossip awaits now respect shutdown uniformly.
+
## Tooling and Documentation
* [Added missing `lncli:` tags](https://github.com/lightningnetwork/lnd/pull/10658)
diff --git a/funding/manager.go b/funding/manager.go
index d5932cb..f64c303 100644
--- a/funding/manager.go
+++ b/funding/manager.go
@@ -2,6 +2,7 @@ package funding
import (
"bytes"
+ "context"
"encoding/binary"
"errors"
"fmt"
@@ -18,6 +19,7 @@ import (
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
+ "github.com/lightningnetwork/lnd/actor"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/chanacceptor"
"github.com/lightningnetwork/lnd/channeldb"
@@ -406,7 +408,8 @@ type Config struct {
// any information within the graph that is not included in the gossip
// message.
SendAnnouncement func(msg lnwire.Message,
- optionalFields ...discovery.OptionalMsgField) chan error
+ optionalFields ...discovery.OptionalMsgField,
+ ) actor.Future[error]
// NotifyWhenOnline allows the FundingManager to register with a
// subsystem that will notify it when the peer comes online. This is
@@ -3687,6 +3690,30 @@ func (f *Manager) extractAnnounceParams(c *channeldb.OpenChannel) (
return fwdMinHTLC, fwdMaxHTLC
}
+// mapGossipError inspects a gossip result error and translates shutdown
+// signals into ErrFundingManagerShuttingDown. Graph-rejected errors (outdated,
+// ignored) are logged at debug level and treated as non-fatal (nil is
+// returned). All other non-nil errors are returned as-is for the caller to
+// handle.
+func mapGossipError(err error, msgType string) error {
+ if err == nil {
+ return nil
+ }
+
+ if errors.Is(err, context.Canceled) ||
+ errors.Is(err, discovery.ErrGossiperShuttingDown) {
+
+ return ErrFundingManagerShuttingDown
+ }
+
+ if graph.IsError(err, graph.ErrOutdated, graph.ErrIgnored) {
+ log.Debugf("Graph rejected %s: %v", msgType, err)
+ return nil
+ }
+
+ return err
+}
+
// addToGraph sends a ChannelAnnouncement and a ChannelUpdate to the
// gossiper so that the channel is added to the graph builder's internal graph.
// These announcement messages are NOT broadcasted to the greater network,
@@ -3716,48 +3743,34 @@ func (f *Manager) addToGraph(completeChan *channeldb.OpenChannel,
"announcement: %v", err)
}
+ // Create a context tied to the manager's quit channel so that both
+ // gossip awaits below respect shutdown.
+ ctx, cancel := lnutils.ContextFromQuit(f.quit)
+ defer cancel()
+
// Send ChannelAnnouncement and ChannelUpdate to the gossiper to add
// to the Router's topology.
- errChan := f.cfg.SendAnnouncement(
- ann.chanAnn, discovery.ChannelCapacity(completeChan.Capacity),
- discovery.ChannelPoint(completeChan.FundingOutpoint),
- discovery.TapscriptRoot(completeChan.TapscriptRoot),
- )
- select {
- case err := <-errChan:
- if err != nil {
- if graph.IsError(err, graph.ErrOutdated,
- graph.ErrIgnored) {
-
- log.Debugf("Graph rejected "+
- "ChannelAnnouncement: %v", err)
- } else {
- return fmt.Errorf("error sending channel "+
- "announcement: %v", err)
- }
- }
- case <-f.quit:
- return ErrFundingManagerShuttingDown
+ err = mapGossipError(discovery.AwaitGossipResult(ctx,
+ f.cfg.SendAnnouncement(
+ ann.chanAnn,
+ discovery.ChannelCapacity(completeChan.Capacity),
+ discovery.ChannelPoint(completeChan.FundingOutpoint),
+ discovery.TapscriptRoot(completeChan.TapscriptRoot),
+ ),
+ ), "ChannelAnnouncement")
+ if err != nil {
+ return fmt.Errorf("error sending channel announcement: %w",
+ err)
}
- errChan = f.cfg.SendAnnouncement(
- ann.chanUpdateAnn, discovery.RemoteAlias(peerAlias),
- )
- select {
- case err := <-errChan:
- if err != nil {
- if graph.IsError(err, graph.ErrOutdated,
- graph.ErrIgnored) {
-
- log.Debugf("Graph rejected "+
- "ChannelUpdate: %v", err)
- } else {
- return fmt.Errorf("error sending channel "+
- "update: %v", err)
- }
- }
- case <-f.quit:
- return ErrFundingManagerShuttingDown
+ err = mapGossipError(discovery.AwaitGossipResult(ctx,
+ f.cfg.SendAnnouncement(
+ ann.chanUpdateAnn,
+ discovery.RemoteAlias(peerAlias),
+ ),
+ ), "ChannelUpdate")
+ if err != nil {
+ return fmt.Errorf("error sending channel update: %w", err)
}
return nil
@@ -4701,28 +4714,21 @@ func (f *Manager) announceChannel(localIDKey, remoteIDKey *btcec.PublicKey,
return err
}
+ // Create a context tied to the manager's quit channel so that both
+ // gossip awaits below respect shutdown.
+ ctx, cancel := lnutils.ContextFromQuit(f.quit)
+ defer cancel()
+
// We only send the channel proof announcement and the node announcement
// because addToGraph previously sent the ChannelAnnouncement and
// the ChannelUpdate announcement messages. The channel proof and node
// announcements are broadcast to the greater network.
- errChan := f.cfg.SendAnnouncement(ann.chanProof)
- select {
- case err := <-errChan:
- if err != nil {
- if graph.IsError(err, graph.ErrOutdated,
- graph.ErrIgnored) {
-
- log.Debugf("Graph rejected "+
- "AnnounceSignatures: %v", err)
- } else {
- log.Errorf("Unable to send channel "+
- "proof: %v", err)
- return err
- }
- }
-
- case <-f.quit:
- return ErrFundingManagerShuttingDown
+ err = mapGossipError(discovery.AwaitGossipResult(
+ ctx, f.cfg.SendAnnouncement(ann.chanProof),
+ ), "AnnounceSignatures")
+ if err != nil {
+ log.Errorf("Unable to send channel proof: %v", err)
+ return err
}
// Now that the channel is announced to the network, we will also
@@ -4735,24 +4741,12 @@ func (f *Manager) announceChannel(localIDKey, remoteIDKey *btcec.PublicKey,
return err
}
- errChan = f.cfg.SendAnnouncement(&nodeAnn)
- select {
- case err := <-errChan:
- if err != nil {
- if graph.IsError(err, graph.ErrOutdated,
- graph.ErrIgnored) {
-
- log.Debugf("Graph rejected "+
- "NodeAnnouncement1: %v", err)
- } else {
- log.Errorf("Unable to send node "+
- "announcement: %v", err)
- return err
- }
- }
-
- case <-f.quit:
- return ErrFundingManagerShuttingDown
+ err = mapGossipError(discovery.AwaitGossipResult(
+ ctx, f.cfg.SendAnnouncement(&nodeAnn),
+ ), "NodeAnnouncement")
+ if err != nil {
+ log.Errorf("Unable to send node announcement: %v", err)
+ return err
}
return nil
diff --git a/funding/manager_test.go b/funding/manager_test.go
index 26563c8..fdd448d 100644
--- a/funding/manager_test.go
+++ b/funding/manager_test.go
@@ -2,6 +2,7 @@ package funding
import (
"bytes"
+ "context"
"encoding/hex"
"errors"
"fmt"
@@ -21,6 +22,7 @@ import (
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcwallet/wallet"
+ "github.com/lightningnetwork/lnd/actor"
"github.com/lightningnetwork/lnd/aliasmgr"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/chainreg"
@@ -29,6 +31,7 @@ import (
"github.com/lightningnetwork/lnd/channelnotifier"
"github.com/lightningnetwork/lnd/discovery"
"github.com/lightningnetwork/lnd/fn/v2"
+ "github.com/lightningnetwork/lnd/graph"
"github.com/lightningnetwork/lnd/graph/db/models"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain"
@@ -474,16 +477,18 @@ func createTestFundingManager(t *testing.T, privKey *btcec.PrivateKey,
return testSig, nil
},
SendAnnouncement: func(msg lnwire.Message,
- _ ...discovery.OptionalMsgField) chan error {
+ _ ...discovery.OptionalMsgField) actor.Future[error] {
- errChan := make(chan error, 1)
+ promise := actor.NewPromise[error]()
+ var sendErr error
select {
case sentAnnouncements <- msg:
- errChan <- nil
case <-shutdownChan:
- errChan <- fmt.Errorf("shutting down")
+ sendErr = fmt.Errorf("shutting down")
}
- return errChan
+ actor.CompleteWith(promise, sendErr)
+
+ return promise.Future()
},
CurrentNodeAnnouncement: func() (lnwire.NodeAnnouncement1,
error) {
@@ -649,16 +654,18 @@ func recreateAliceFundingManager(t *testing.T, alice *testNode) {
return testSig, nil
},
SendAnnouncement: func(msg lnwire.Message,
- _ ...discovery.OptionalMsgField) chan error {
+ _ ...discovery.OptionalMsgField) actor.Future[error] {
- errChan := make(chan error, 1)
+ promise := actor.NewPromise[error]()
+ var sendErr error
select {
case aliceAnnounceChan <- msg:
- errChan <- nil
case <-shutdownChan:
- errChan <- fmt.Errorf("shutting down")
+ sendErr = fmt.Errorf("shutting down")
}
- return errChan
+ actor.CompleteWith(promise, sendErr)
+
+ return promise.Future()
},
CurrentNodeAnnouncement: func() (lnwire.NodeAnnouncement1,
error) {
@@ -1980,11 +1987,15 @@ func TestFundingManagerRestartBehavior(t *testing.T) {
// Intentionally make the channel announcements fail
alice.fundingMgr.cfg.SendAnnouncement = func(msg lnwire.Message,
- _ ...discovery.OptionalMsgField) chan error {
+ _ ...discovery.OptionalMsgField) actor.Future[error] {
- errChan := make(chan error, 1)
- errChan <- fmt.Errorf("intentional error in SendAnnouncement")
- return errChan
+ promise := actor.NewPromise[error]()
+ actor.CompleteWith(
+ promise,
+ fmt.Errorf("intentional error in SendAnnouncement"),
+ )
+
+ return promise.Future()
}
channelReadyAlice, ok := assertFundingMsgSent(
@@ -5103,3 +5114,66 @@ func TestFundingManagerCoinbase(t *testing.T) {
// channel.
assertHandleChannelReady(t, alice, bob)
}
+
+// TestMapGossipError verifies that mapGossipError correctly translates gossip
+// result errors into funding manager errors.
+func TestMapGossipError(t *testing.T) {
+ t.Parallel()
+
+ tests := []struct {
+ name string
+ inErr error
+ wantErr error
+ }{
+ {
+ name: "nil error",
+ inErr: nil,
+ wantErr: nil,
+ },
+ {
+ name: "context canceled maps to shutdown",
+ inErr: context.Canceled,
+ wantErr: ErrFundingManagerShuttingDown,
+ },
+ {
+ name: "gossiper shutting down maps to shutdown",
+ inErr: discovery.ErrGossiperShuttingDown,
+ wantErr: ErrFundingManagerShuttingDown,
+ },
+ {
+ name: "graph outdated treated as non-fatal",
+ inErr: graph.NewErrf(graph.ErrOutdated, "outdated"),
+ wantErr: nil,
+ },
+ {
+ name: "graph ignored treated as non-fatal",
+ inErr: graph.NewErrf(graph.ErrIgnored, "ignored"),
+ wantErr: nil,
+ },
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ t.Parallel()
+
+ got := mapGossipError(tc.inErr, "TestMsg")
+
+ if tc.wantErr == nil {
+ require.NoError(t, got)
+ return
+ }
+
+ require.Error(t, got)
+ require.ErrorIs(t, got, tc.wantErr)
+ })
+ }
+
+ // Verify that unrecognized errors pass through unchanged.
+ t.Run("other errors passed through", func(t *testing.T) {
+ t.Parallel()
+
+ sentinel := errors.New("unexpected failure")
+ got := mapGossipError(sentinel, "TestMsg")
+ require.ErrorIs(t, got, sentinel)
+ })
+}
diff --git a/peer/brontide.go b/peer/brontide.go
index e97568b..93d0f71 100644
--- a/peer/brontide.go
+++ b/peer/brontide.go
@@ -2114,13 +2114,11 @@ func newDiscMsgStream(p *Brontide) *msgStream {
// deleted.
p.log.Debugf("Processing remote msg %T", msg)
- // TODO(ziggie): ProcessRemoteAnnouncement returns an error
- // channel, but we cannot rely on it being written to.
- // Because some messages might never be processed (e.g.
- // premature channel updates). We should change the design here
- // and use the actor model pattern as soon as it is available.
- // So for now we should NOT use the error channel.
- // See https://github.com/lightningnetwork/lnd/pull/9820.
+ // The returned Future[error] is intentionally not awaited
+ // here. Remote gossip messages are fire-and-forget from the
+ // peer's perspective: the gossiper processes them
+ // asynchronously, and an unawaited Future carries no cost
+ // (no goroutine, no channel leak).
p.cfg.AuthGossiper.ProcessRemoteAnnouncement(ctx, msg, p)
}
diff --git a/server.go b/server.go
index b0e1f02..2658db3 100644
--- a/server.go
+++ b/server.go
@@ -5342,15 +5342,14 @@ func (s *server) applyChannelUpdate(update *lnwire.ChannelUpdate1,
}
}
- errChan := s.authGossiper.ProcessLocalAnnouncement(
+ fut := s.authGossiper.ProcessLocalAnnouncement(
update, discovery.RemoteAlias(peerAlias),
)
- select {
- case err := <-errChan:
- return err
- case <-s.quit:
- return ErrServerShuttingDown
- }
+
+ ctx, cancel := lnutils.ContextFromQuit(s.quit)
+ defer cancel()
+
+ return discovery.AwaitGossipResult(ctx, fut)
}
// SendCustomMessage sends a custom message to the peer with the specified
Why this scored 64/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.