Skip to content

Commit 2e35dd3

Browse files
committed
multi: send signature announcement once
This change aligns our behavior with latest protocol guidelines. That is, when we receive a signature announcement when we already have the full proof we reply with our signature announcement once per (re)connection. see: lightning/bolts#1256
1 parent 228ce5f commit 2e35dd3

File tree

9 files changed

+189
-13
lines changed

9 files changed

+189
-13
lines changed

discovery/gossiper.go

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3483,12 +3483,22 @@ func (d *AuthenticatedGossiper) handleAnnSig(ctx context.Context,
34833483
if chanInfo.AuthProof != nil {
34843484
// If we already have the fully assembled proof, then the peer
34853485
// sending us their proof has probably not received our local
3486-
// proof yet. So be kind and send them our proof.
3486+
// proof yet. So be kind and send them our proof, but only if we
3487+
// haven't done so since (re)connecting.
34873488
if nMsg.isRemote {
3488-
peerID := nMsg.source.SerializeCompressed()
3489-
log.Debugf("Got AnnounceSignatures for channel with " +
3490-
"full proof.")
3489+
// If we already sent our proof to this peer once since
3490+
// (re)connecting, then we can skip sending it again.
3491+
if nMsg.peer.HasSentProof(ann.ChannelID) {
3492+
log.Debugf("Skipping sending announcement " +
3493+
"signatures since we already did " +
3494+
"during this connection.")
3495+
3496+
nMsg.err <- nil
3497+
3498+
return nil, true
3499+
}
34913500

3501+
peerID := nMsg.source.SerializeCompressed()
34923502
d.wg.Add(1)
34933503
go func() {
34943504
defer d.wg.Done()
@@ -3514,7 +3524,6 @@ func (d *AuthenticatedGossiper) handleAnnSig(ctx context.Context,
35143524
remoteNSB,
35153525
remoteBSB, nil,
35163526
)
3517-
35183527
if err != nil {
35193528
log.Errorf("Failed to generate "+
35203529
"announcement signature: %v",
@@ -3530,6 +3539,8 @@ func (d *AuthenticatedGossiper) handleAnnSig(ctx context.Context,
35303539
return
35313540
}
35323541

3542+
nMsg.peer.RecordProofSent(ann.ChannelID)
3543+
35333544
log.Debugf("Signature announcement sent to "+
35343545
"peer=%x for chanID=%v", peerID,
35353546
ann.ChannelID)

discovery/gossiper_test.go

Lines changed: 77 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1774,16 +1774,23 @@ out:
17741774
}
17751775
}
17761776

1777-
// TestSignatureAnnouncementFullProofWhenRemoteProof tests that if a remote
1777+
// TestSignatureAnnouncementResendWhenRemoteProof tests that if a remote
17781778
// proof is received when we already have the full proof, the gossiper will send
1779-
// the full proof (ChannelAnnouncement) to the remote peer.
1780-
func TestSignatureAnnouncementFullProofWhenRemoteProof(t *testing.T) {
1779+
// our signature announcement max once per connection to the remote peer.
1780+
func TestSignatureAnnouncementResendWhenRemoteProof(t *testing.T) {
17811781
t.Parallel()
17821782
ctx := context.Background()
17831783

17841784
tCtx, err := createTestCtx(t, proofMatureDelta, false)
17851785
require.NoError(t, err, "can't create context")
17861786

1787+
// We'll create our test sync manager to have one active syncer.
1788+
syncMgr := newTestSyncManager(1)
1789+
syncMgr.Start()
1790+
defer syncMgr.Stop()
1791+
1792+
tCtx.gossiper.syncMgr = syncMgr
1793+
17871794
batch, err := tCtx.createLocalAnnouncements(0)
17881795
require.NoError(t, err, "can't generate announcements")
17891796

@@ -1797,8 +1804,16 @@ func TestSignatureAnnouncementFullProofWhenRemoteProof(t *testing.T) {
17971804
remoteKey, sentToPeer, tCtx.gossiper.quit, false,
17981805
)
17991806

1807+
// We create an active syncer for our remote peer.
1808+
err = syncMgr.InitSyncState(remotePeer)
1809+
require.NoError(t, err, "failed to init sync state")
1810+
remoteSyncer := assertSyncerExistence(t, syncMgr, remotePeer)
1811+
assertTransitionToChansSynced(t, remoteSyncer, remotePeer)
1812+
assertActiveGossipTimestampRange(t, remotePeer)
1813+
assertSyncerStatus(t, remoteSyncer, chansSynced, ActiveSync)
1814+
18001815
// Override NotifyWhenOnline to return the remote peer which we expect
1801-
// meesages to be sent to.
1816+
// messages to be sent to.
18021817
tCtx.gossiper.reliableSender.cfg.NotifyWhenOnline = func(_ [33]byte,
18031818
peerChan chan<- lnpeer.Peer) {
18041819

@@ -1940,7 +1955,64 @@ func TestSignatureAnnouncementFullProofWhenRemoteProof(t *testing.T) {
19401955
}
19411956

19421957
// Now give the gossiper the remote proof yet again. This should
1943-
// trigger a send of the signature announcement.
1958+
// trigger a send of our signature announcement.
1959+
select {
1960+
case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
1961+
ctx, batch.remoteProofAnn, remotePeer,
1962+
):
1963+
case <-time.After(2 * time.Second):
1964+
t.Fatal("did not process local announcement")
1965+
}
1966+
require.NoError(t, err, "unable to process remote proof")
1967+
1968+
// We expect the gossiper to send this message to the remote peer.
1969+
select {
1970+
case msg := <-sentToPeer:
1971+
_, ok := msg.(*lnwire.AnnounceSignatures1)
1972+
if !ok {
1973+
t.Fatalf("expected AnnounceSignatures1, instead got "+
1974+
"%T", msg)
1975+
}
1976+
case <-time.After(2 * time.Second):
1977+
t.Fatal("did not send local proof to peer")
1978+
}
1979+
1980+
// Now give the gossiper the remote proof a 2nd time. This should _not_
1981+
// trigger a send of our signature announcement, since we already sent
1982+
// it once and we only send it once per connection.
1983+
select {
1984+
case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
1985+
ctx, batch.remoteProofAnn, remotePeer,
1986+
):
1987+
case <-time.After(2 * time.Second):
1988+
t.Fatal("did not process local announcement")
1989+
}
1990+
require.NoError(t, err, "unable to process remote proof")
1991+
1992+
// We expect the gossiper to _not_ send this message to the remote peer.
1993+
select {
1994+
case msg := <-sentToPeer:
1995+
_, ok := msg.(*lnwire.AnnounceSignatures1)
1996+
if ok {
1997+
t.Fatalf("got an AnnounceSignatures1 when none was "+
1998+
"expected %T", msg)
1999+
}
2000+
case <-time.After(2 * time.Second):
2001+
break
2002+
}
2003+
2004+
// We simulate the remote peer disconnecting and reconnecting by
2005+
// creating a new mock peer. This will reset the connection-specific
2006+
// state, such as whether we've already sent the proof to this peer.
2007+
remotePeer = newMockPeer(
2008+
remoteKey, sentToPeer, tCtx.gossiper.quit, false,
2009+
)
2010+
2011+
// Now give the gossiper the remote proof a 3rd time. This should
2012+
// trigger a send of our signature announcement, since the syncer
2013+
// was pruned and we now have a new syncer for the remote peer.
2014+
// This is to simulate the case where the remote peer disconnects and
2015+
// reconnects, and we have to send the signature announcement again.
19442016
select {
19452017
case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
19462018
ctx, batch.remoteProofAnn, remotePeer,

discovery/mock_test.go

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88

99
"github.com/btcsuite/btcd/btcec/v2"
1010
"github.com/btcsuite/btcd/wire"
11+
"github.com/lightningnetwork/lnd/fn/v2"
1112
"github.com/lightningnetwork/lnd/lnpeer"
1213
"github.com/lightningnetwork/lnd/lnwire"
1314
)
@@ -19,6 +20,11 @@ type mockPeer struct {
1920
sentMsgs chan lnwire.Message
2021
quit chan struct{}
2122
disconnected atomic.Bool
23+
24+
// The following fields are used to mock the announcement proof-related
25+
// methods.
26+
proofsSentMtx sync.Mutex
27+
proofSentToChan fn.Set[lnwire.ChannelID]
2228
}
2329

2430
var _ lnpeer.Peer = (*mockPeer)(nil)
@@ -27,9 +33,10 @@ func newMockPeer(pk *btcec.PublicKey, sentMsgs chan lnwire.Message,
2733
quit chan struct{}, disconnected bool) *mockPeer {
2834

2935
p := &mockPeer{
30-
pk: pk,
31-
sentMsgs: sentMsgs,
32-
quit: quit,
36+
pk: pk,
37+
sentMsgs: sentMsgs,
38+
quit: quit,
39+
proofSentToChan: fn.NewSet[lnwire.ChannelID](),
3340
}
3441
p.disconnected.Store(disconnected)
3542

@@ -93,6 +100,29 @@ func (p *mockPeer) Disconnect(err error) {
93100
p.disconnected.Store(true)
94101
}
95102

103+
func (p *mockPeer) RecordProofSent(chanID lnwire.ChannelID) {
104+
p.proofsSentMtx.Lock()
105+
defer p.proofsSentMtx.Unlock()
106+
107+
if p.proofSentToChan == nil {
108+
p.proofSentToChan = make(map[lnwire.ChannelID]struct{})
109+
}
110+
p.proofSentToChan[chanID] = struct{}{}
111+
}
112+
113+
func (p *mockPeer) HasSentProof(chanID lnwire.ChannelID) bool {
114+
p.proofsSentMtx.Lock()
115+
defer p.proofsSentMtx.Unlock()
116+
117+
if p.proofSentToChan == nil {
118+
return false
119+
}
120+
121+
_, ok := p.proofSentToChan[chanID]
122+
123+
return ok
124+
}
125+
96126
// mockMessageStore is an in-memory implementation of the MessageStore interface
97127
// used for the gossiper's unit tests.
98128
type mockMessageStore struct {

funding/manager_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,12 @@ func (n *testNode) RemovePendingChannel(_ lnwire.ChannelID) error {
370370
return nil
371371
}
372372

373+
func (n *testNode) RecordProofSent(chanID lnwire.ChannelID) {}
374+
375+
func (n *testNode) HasSentProof(chanID lnwire.ChannelID) bool {
376+
return false
377+
}
378+
373379
func createTestWallet(cdb *channeldb.ChannelStateDB, netParams *chaincfg.Params,
374380
notifier chainntnfs.ChainNotifier, wc lnwallet.WalletController,
375381
signer input.Signer, keyRing keychain.SecretKeyRing,

htlcswitch/link_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2128,6 +2128,12 @@ func (m *mockPeer) RemovePendingChannel(_ lnwire.ChannelID) error {
21282128
return nil
21292129
}
21302130

2131+
func (p *mockPeer) RecordProofSent(chanID lnwire.ChannelID) {}
2132+
2133+
func (p *mockPeer) HasSentProof(chanID lnwire.ChannelID) bool {
2134+
return false
2135+
}
2136+
21312137
type singleLinkTestHarness struct {
21322138
aliceSwitch *Switch
21332139
aliceLink ChannelLink

htlcswitch/mock.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,12 @@ func (s *mockServer) QuitSignal() <-chan struct{} {
321321
return s.quit
322322
}
323323

324+
func (s *mockServer) RecordProofSent(chanID lnwire.ChannelID) {}
325+
326+
func (s *mockServer) HasSentProof(chanID lnwire.ChannelID) bool {
327+
return false
328+
}
329+
324330
// mockHopIterator represents the test version of hop iterator which instead
325331
// of encrypting the path in onion blob just stores the path as a list of hops.
326332
type mockHopIterator struct {

lnpeer/mock_peer.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,9 @@ func (m *MockPeer) RemoteFeatures() *lnwire.FeatureVector {
8181
}
8282

8383
func (m *MockPeer) Disconnect(err error) {}
84+
85+
func (s *MockPeer) RecordProofSent(chanID lnwire.ChannelID) {}
86+
87+
func (s *MockPeer) HasSentProof(chanID lnwire.ChannelID) bool {
88+
return false
89+
}

lnpeer/peer.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,14 @@ type Peer interface {
4949
// point from all indexes associated with the peer.
5050
WipeChannel(*wire.OutPoint)
5151

52+
// RecordProofSent should be called when a proof has been sent for a
53+
// given channel ID.
54+
RecordProofSent(lnwire.ChannelID)
55+
56+
// HasSentProof should be called to check whether a proof has been sent
57+
// for a given channel ID.
58+
HasSentProof(lnwire.ChannelID) bool
59+
5260
// PubKey returns the serialized public key of the remote peer.
5361
PubKey() [33]byte
5462

peer/brontide.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,16 @@ type Brontide struct {
606606
// peer's chansync message with its own over and over again.
607607
resentChanSyncMsg map[lnwire.ChannelID]struct{}
608608

609+
// proofsSentMtx is a mutex that protects the proofSentToChan set.
610+
proofsSentMtx sync.Mutex
611+
612+
// proofSentToChan is used when we already have the fully assembled
613+
// proof for a channel and the peer sending us their proof has probably
614+
// not received our local proof yet. So we are kind and send them our
615+
// proof, but only if we haven't done so since (re)connecting. We keep
616+
// track of sends with this map, so we don't send it twice.
617+
proofSentToChan fn.Set[lnwire.ChannelID]
618+
609619
// channelEventClient is the channel event subscription client that's
610620
// used to assist retry enabling the channels. This client is only
611621
// created when the reenableTimeout is no greater than 1 minute. Once
@@ -675,6 +685,7 @@ func NewBrontide(cfg Config) *Brontide {
675685
linkFailures: make(chan linkFailureReport),
676686
chanCloseMsgs: make(chan *closeMsg),
677687
resentChanSyncMsg: make(map[lnwire.ChannelID]struct{}),
688+
proofSentToChan: fn.NewSet[lnwire.ChannelID](),
678689
startReady: make(chan struct{}),
679690
log: peerLog.WithPrefix(logPrefix),
680691
msgRouter: msgRouter,
@@ -4498,6 +4509,26 @@ func (p *Brontide) WipeChannel(chanPoint *wire.OutPoint) {
44984509
p.cfg.Switch.RemoveLink(chanID)
44994510
}
45004511

4512+
// RecordProofSent should be called when a proof has been sent for a given
4513+
// channel ID.
4514+
//
4515+
// NOTE: Part of the lnpeer.Peer interface.
4516+
func (p *Brontide) RecordProofSent(chanID lnwire.ChannelID) {
4517+
p.proofsSentMtx.Lock()
4518+
defer p.proofsSentMtx.Unlock()
4519+
p.proofSentToChan.Add(chanID)
4520+
}
4521+
4522+
// HasSentProof should be called to check whether a proof has been sent for a
4523+
// given channel ID.
4524+
//
4525+
// NOTE: Part of the lnpeer.Peer interface.
4526+
func (p *Brontide) HasSentProof(chanID lnwire.ChannelID) bool {
4527+
p.proofsSentMtx.Lock()
4528+
defer p.proofsSentMtx.Unlock()
4529+
return p.proofSentToChan.Contains(chanID)
4530+
}
4531+
45014532
// handleInitMsg handles the incoming init message which contains global and
45024533
// local feature vectors. If feature vectors are incompatible then disconnect.
45034534
func (p *Brontide) handleInitMsg(msg *lnwire.Init) error {

0 commit comments

Comments
 (0)