funding: fix race in itest for zero-conf funding

Fix a race condition where forwarding through a public zero-conf channel
could fail with UnknownNextPeer when using the confirmed SCID. The issue
occurred because ReportShortChanID (which updates the switch's baseIndex
to handle the confirmed SCID) was called AFTER addToGraph (which announces
the confirmed SCID to the network).

With slow backends like postgres, addToGraph takes significant time,
creating a window where other nodes learn about the confirmed SCID from
gossip and attempt to route through it, but the receiving node's switch
hasn't been updated yet to handle forwards using the confirmed SCID.

The fix reorders operations to call ReportShortChanID before addToGraph,
ensuring the switch is ready to handle the confirmed SCID before it's
announced to the network. Forwards using either the alias or confirmed
SCID will work since getLinkByMapping uses baseIndex to map both to the
same link in forwardingIndex.

Fixes flaky test: zero_conf-channel_policy_update_public_zero_conf
This commit is contained in:
ziggie committed 2025-12-10 19:44:18 +01:00
1 parent cb3991ea6e
commit fe9a3e103b
2 files changed
+26 -21

No files matched your search

+16 -13
View File
@@ -3957,6 +3957,22 @@ func (f *Manager) waitForZeroConfChannel(c *channeldb.OpenChannel) error {
// Six confirmations have been reached. If this channel is public,
// we'll delete some of the alias mappings the gossiper uses.
//
// Tell the Switch to refresh the relevant ChannelLink so that forwards
// under the confirmed SCID are possible. We do this BEFORE updating the
// graph to avoid a race where other nodes learn about the confirmed
// SCID from gossip before our switch is ready to handle forwards using
// it. This is especially important for integration tests.
err = f.cfg.ReportShortChanID(c.FundingOutpoint)
if err != nil {
// This should only fail if the link is not found in the
// Switch's linkIndex map. If this is the case, then the peer
// has gone offline and the next time the link is loaded, it
// will have a refreshed state. Just log an error here.
log.Errorf("unable to report scid for zero-conf channel "+
"channel: %v", err)
}
isPublic := c.ChannelFlags&lnwire.FFAnnounceChannel != 0
if isPublic {
err = f.cfg.AliasManager.DeleteSixConfs(c.ShortChannelID)
@@ -3985,19 +4001,6 @@ func (f *Manager) waitForZeroConfChannel(c *channeldb.OpenChannel) error {
}
}
// Since we have now marked down the confirmed SCID, we'll also need to
// tell the Switch to refresh the relevant ChannelLink so that forwards
// under the confirmed SCID are possible if this is a public channel.
err = f.cfg.ReportShortChanID(c.FundingOutpoint)
if err != nil {
// This should only fail if the link is not found in the
// Switch's linkIndex map. If this is the case, then the peer
// has gone offline and the next time the link is loaded, it
// will have a refreshed state. Just log an error here.
log.Errorf("unable to report scid for zero-conf channel "+
"channel: %v", err)
}
// Update the confirmed transaction's label.
f.makeLabelForTx(c)
+10 -8
View File
@@ -4726,14 +4726,9 @@ func testZeroConf(t *testing.T, chanType *lnwire.ChannelType) {
assertConfirmationHeight(t, alice, chanID, 1)
assertConfirmationHeight(t, bob, chanID, 1)
// For taproot channels, we don't expect them to be announced atm.
if !isTaprootChanType(chanType) {
assertChannelAnnouncements(
t, alice, bob, fundingAmt, nil, nil, nil, nil,
)
}
// Both Alice and Bob should send on reportScidChan.
// Both Alice and Bob should call ReportShortChanID first (before
// sending announcements) to avoid a race where other nodes learn about
// the confirmed SCID before the switch is ready.
select {
case <-alice.reportScidChan:
case <-time.After(time.Second * 5):
@@ -4746,6 +4741,13 @@ func testZeroConf(t *testing.T, chanType *lnwire.ChannelType) {
t.Fatalf("did not call ReportShortChanID in time")
}
// For taproot channels, we don't expect them to be announced atm.
if !isTaprootChanType(chanType) {
assertChannelAnnouncements(
t, alice, bob, fundingAmt, nil, nil, nil, nil,
)
}
// Send along the 6-confirmation channel so that announcement sigs can
// be exchanged.
alice.mockNotifier.sixConfChannel <- &chainntnfs.TxConfirmation{