Skip to content

Commit 9402ec9

Browse files
committed
discovery: 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 511be4c commit 9402ec9

File tree

2 files changed

+197
-30
lines changed

2 files changed

+197
-30
lines changed

discovery/gossiper.go

Lines changed: 113 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,60 @@ func (c *cachedReject) Size() (uint64, error) {
473473
return 1, nil
474474
}
475475

476+
// sentAnnSigsTracker tracks which announcement signatures we've sent to which
477+
// peers.
478+
type sentAnnSigsTracker struct {
479+
mu sync.Mutex
480+
sigs map[route.Vertex]map[lnwire.ChannelID]struct{}
481+
}
482+
483+
// newSentAnnSigsTracker creates a new sentAnnSigsTracker.
484+
func newSentAnnSigsTracker() *sentAnnSigsTracker {
485+
return &sentAnnSigsTracker{
486+
sigs: make(map[route.Vertex]map[lnwire.ChannelID]struct{}),
487+
}
488+
}
489+
490+
// deletePeer deletes all records of sent announcement signatures for a given
491+
// peer.
492+
func (s *sentAnnSigsTracker) deletePeer(peer route.Vertex) {
493+
s.mu.Lock()
494+
defer s.mu.Unlock()
495+
delete(s.sigs, peer)
496+
}
497+
498+
// sentToPeer returns true if we have already sent the announcement signature
499+
// for a given channel to a peer.
500+
func (s *sentAnnSigsTracker) sentToPeer(peer route.Vertex,
501+
cid lnwire.ChannelID) bool {
502+
503+
s.mu.Lock()
504+
defer s.mu.Unlock()
505+
506+
sentChans, ok := s.sigs[peer]
507+
if !ok {
508+
return false
509+
}
510+
511+
_, ok = sentChans[cid]
512+
513+
return ok
514+
}
515+
516+
// addToPeer adds a record of sending an announcement signature for a given
517+
// channel to a peer.
518+
func (s *sentAnnSigsTracker) addToPeer(peer route.Vertex,
519+
cid lnwire.ChannelID) {
520+
521+
s.mu.Lock()
522+
defer s.mu.Unlock()
523+
524+
if _, ok := s.sigs[peer]; !ok {
525+
s.sigs[peer] = make(map[lnwire.ChannelID]struct{})
526+
}
527+
s.sigs[peer][cid] = struct{}{}
528+
}
529+
476530
// AuthenticatedGossiper is a subsystem which is responsible for receiving
477531
// announcements, validating them and applying the changes to router, syncing
478532
// lightning network with newly connected nodes, broadcasting announcements
@@ -568,6 +622,11 @@ type AuthenticatedGossiper struct {
568622
// vb is used to enforce job dependency ordering of gossip messages.
569623
vb *ValidationBarrier
570624

625+
// sentAnnSigs tracks which announcement signatures we've sent to which
626+
// peers. We'll use this to ensure we don't re-send the same signatures
627+
// to a peer during a single connection.
628+
sentAnnSigs *sentAnnSigsTracker
629+
571630
sync.Mutex
572631

573632
cancel fn.Option[context.CancelFunc]
@@ -595,6 +654,7 @@ func New(cfg Config, selfKeyDesc *keychain.KeyDescriptor) *AuthenticatedGossiper
595654
),
596655
chanUpdateRateLimiter: make(map[uint64][2]*rate.Limiter),
597656
banman: newBanman(cfg.BanThreshold),
657+
sentAnnSigs: newSentAnnSigsTracker(),
598658
}
599659

600660
gossiper.vb = NewValidationBarrier(1000, gossiper.quit)
@@ -1677,6 +1737,14 @@ func (d *AuthenticatedGossiper) handleNetworkMessages(ctx context.Context,
16771737
// queries. We'll allocate a new gossip syncer for it, and start any goroutines
16781738
// needed to handle new queries.
16791739
func (d *AuthenticatedGossiper) InitSyncState(syncPeer lnpeer.Peer) {
1740+
peerVertex := route.NewVertex(syncPeer.IdentityKey())
1741+
1742+
// We keep track if we already have sent an announcement signature to
1743+
// each peer during a connection, so we don't send it twice. If a peer
1744+
// dis- and reconnects we clear the record. If the peer connects for the
1745+
// first time this is a no-op.
1746+
d.sentAnnSigs.deletePeer(peerVertex)
1747+
16801748
d.syncMgr.InitSyncState(syncPeer)
16811749
}
16821750

@@ -3542,12 +3610,26 @@ func (d *AuthenticatedGossiper) handleAnnSig(ctx context.Context,
35423610
if chanInfo.AuthProof != nil {
35433611
// If we already have the fully assembled proof, then the peer
35443612
// sending us their proof has probably not received our local
3545-
// proof yet. So be kind and send them our proof.
3613+
// proof yet. So be kind and send them our proof, but only if we
3614+
// haven't done so since (re)connecting.
35463615
if nMsg.isRemote {
3547-
peerID := nMsg.source.SerializeCompressed()
3548-
log.Debugf("Got AnnounceSignatures for channel with " +
3549-
"full proof.")
3616+
peerVertex := route.NewVertex(nMsg.source)
3617+
3618+
if d.sentAnnSigs.sentToPeer(peerVertex, ann.ChannelID) {
3619+
log.Debugf("Skipping sending " +
3620+
"announcement signatures " +
3621+
"since we already did during " +
3622+
"this connection.")
35503623

3624+
nMsg.err <- nil
3625+
3626+
return nil, true
3627+
}
3628+
3629+
peerID := nMsg.source.SerializeCompressed()
3630+
peerIsFirstNode := bytes.Equal(
3631+
peerID, chanInfo.NodeKey1Bytes[:],
3632+
)
35513633
d.wg.Add(1)
35523634
go func() {
35533635
defer d.wg.Done()
@@ -3558,28 +3640,39 @@ func (d *AuthenticatedGossiper) handleAnnSig(ctx context.Context,
35583640
ann.ChannelID, peerID)
35593641

35603642
chanAP := chanInfo.AuthProof
3561-
var remoteNSB []byte
3562-
var remoteBSB []byte
3563-
if isFirstNode {
3564-
remoteNSB = chanAP.NodeSig2Bytes
3565-
remoteBSB = chanAP.BitcoinSig2Bytes
3643+
// Pick the local signatures that the peer does
3644+
// NOT have yet. If the peer is node1, they sent
3645+
// us node1/bitcoin1 already, so we respond with
3646+
// node2/bitcoin2. Otherwise we respond with
3647+
// node1/bitcoin1.
3648+
var localNSB, localBSB []byte
3649+
if peerIsFirstNode {
3650+
localNSB = chanAP.NodeSig2Bytes
3651+
localBSB = chanAP.BitcoinSig2Bytes
35663652
} else {
3567-
remoteNSB = chanAP.NodeSig1Bytes
3568-
remoteBSB = chanAP.BitcoinSig1Bytes
3653+
localNSB = chanAP.NodeSig1Bytes
3654+
localBSB = chanAP.BitcoinSig1Bytes
35693655
}
35703656

3657+
// Construct an AnnounceSignatures message from
3658+
// the raw signature material. This gives the
3659+
// peer our half of the proof so they can
3660+
// assemble the full ChannelAnnouncement.
35713661
sigAnn, err := lnwire.NewAnnSigFromWireECDSARaw(
35723662
ann.ChannelID, ann.ShortChannelID,
3573-
remoteNSB, remoteBSB, nil,
3663+
localNSB, localBSB, nil,
35743664
)
3575-
35763665
if err != nil {
35773666
log.Errorf("Failed to generate "+
35783667
"announcement signature: %v",
35793668
err)
35803669
return
35813670
}
35823671

3672+
// Send our half of the proof to the peer. If
3673+
// successful, we mark that we've sent the proof
3674+
// during this connection so we don't re-send it
3675+
// on subsequent half-proof messages.
35833676
err = nMsg.peer.SendMessage(false, sigAnn)
35843677
if err != nil {
35853678
log.Errorf("Failed sending signature "+
@@ -3588,6 +3681,13 @@ func (d *AuthenticatedGossiper) handleAnnSig(ctx context.Context,
35883681
return
35893682
}
35903683

3684+
// With the announcement sent, we'll now mark
3685+
// that we've sent the proof to the peer for
3686+
// this connection.
3687+
d.sentAnnSigs.addToPeer(
3688+
peerVertex, ann.ChannelID,
3689+
)
3690+
35913691
log.Debugf("Signature announcement sent to "+
35923692
"peer=%x for chanID=%v", peerID,
35933693
ann.ChannelID)

discovery/gossiper_test.go

Lines changed: 84 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1783,16 +1783,23 @@ out:
17831783
}
17841784
}
17851785

1786-
// TestSignatureAnnouncementFullProofWhenRemoteProof tests that if a remote
1786+
// TestSignatureAnnouncementResendWhenRemoteProof tests that if a remote
17871787
// proof is received when we already have the full proof, the gossiper will send
1788-
// the full proof (ChannelAnnouncement) to the remote peer.
1789-
func TestSignatureAnnouncementFullProofWhenRemoteProof(t *testing.T) {
1788+
// our signature announcement max once per connection to the remote peer.
1789+
func TestSignatureAnnouncementResendWhenRemoteProof(t *testing.T) {
17901790
t.Parallel()
17911791
ctx := t.Context()
17921792

17931793
tCtx, err := createTestCtx(t, proofMatureDelta, false)
17941794
require.NoError(t, err, "can't create context")
17951795

1796+
// We'll create our test sync manager to have one active syncer.
1797+
syncMgr := newTestSyncManager(1)
1798+
syncMgr.Start()
1799+
defer syncMgr.Stop()
1800+
1801+
tCtx.gossiper.syncMgr = syncMgr
1802+
17961803
batch, err := tCtx.createLocalAnnouncements(0)
17971804
require.NoError(t, err, "can't generate announcements")
17981805

@@ -1806,8 +1813,15 @@ func TestSignatureAnnouncementFullProofWhenRemoteProof(t *testing.T) {
18061813
remoteKey, sentToPeer, tCtx.gossiper.quit, false,
18071814
)
18081815

1816+
// We create an active syncer for our remote peer.
1817+
tCtx.gossiper.InitSyncState(remotePeer)
1818+
remoteSyncer := assertSyncerExistence(t, syncMgr, remotePeer)
1819+
assertTransitionToChansSynced(t, remoteSyncer, remotePeer)
1820+
assertActiveGossipTimestampRange(t, remotePeer)
1821+
assertSyncerStatus(t, remoteSyncer, chansSynced, ActiveSync)
1822+
18091823
// Override NotifyWhenOnline to return the remote peer which we expect
1810-
// meesages to be sent to.
1824+
// messages to be sent to.
18111825
tCtx.gossiper.reliableSender.cfg.NotifyWhenOnline = func(_ [33]byte,
18121826
peerChan chan<- lnpeer.Peer) {
18131827

@@ -1905,14 +1919,25 @@ func TestSignatureAnnouncementFullProofWhenRemoteProof(t *testing.T) {
19051919
}
19061920
require.NoError(t, err, "unable to process local proof")
19071921

1908-
select {
1909-
case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
1910-
ctx, batch.remoteProofAnn, remotePeer,
1911-
):
1912-
case <-time.After(2 * time.Second):
1913-
t.Fatal("did not process local announcement")
1922+
// Define helper to process a remote proof in a sub-test.
1923+
processRemoteProof := func(t *testing.T) {
1924+
t.Helper()
1925+
var err error
1926+
select {
1927+
case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
1928+
ctx, batch.remoteProofAnn, remotePeer,
1929+
):
1930+
case <-time.After(2 * time.Second):
1931+
t.Fatal("did not process local announcement")
1932+
}
1933+
require.NoError(t, err, "unable to process remote proof")
19141934
}
1915-
require.NoError(t, err, "unable to process remote proof")
1935+
1936+
// Now give the gossiper the remote proof. This should
1937+
// trigger a send of our signature announcement.
1938+
t.Run("process remote proof - first send", func(t *testing.T) {
1939+
processRemoteProof(t)
1940+
})
19161941

19171942
// We expect the gossiper to send this message to the remote peer.
19181943
select {
@@ -1949,15 +1974,57 @@ func TestSignatureAnnouncementFullProofWhenRemoteProof(t *testing.T) {
19491974
}
19501975

19511976
// Now give the gossiper the remote proof yet again. This should
1952-
// trigger a send of the signature announcement.
1977+
// trigger a send of our signature announcement.
1978+
t.Run("process remote proof - first resend", func(t *testing.T) {
1979+
processRemoteProof(t)
1980+
})
1981+
1982+
// We expect the gossiper to send this message to the remote peer.
19531983
select {
1954-
case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
1955-
ctx, batch.remoteProofAnn, remotePeer,
1956-
):
1984+
case msg := <-sentToPeer:
1985+
_, ok := msg.(*lnwire.AnnounceSignatures1)
1986+
if !ok {
1987+
t.Fatalf("expected AnnounceSignatures1, instead got "+
1988+
"%T", msg)
1989+
}
19571990
case <-time.After(2 * time.Second):
1958-
t.Fatal("did not process local announcement")
1991+
t.Fatal("did not send local proof to peer")
19591992
}
1960-
require.NoError(t, err, "unable to process remote proof")
1993+
1994+
// Now give the gossiper the remote proof yet again. This should
1995+
// **not** trigger a send of our signature announcement.
1996+
t.Run("process remote proof - second resend", func(t *testing.T) {
1997+
processRemoteProof(t)
1998+
})
1999+
2000+
// We expect the gossiper to _not_ send this message to the remote peer.
2001+
select {
2002+
case msg := <-sentToPeer:
2003+
_, ok := msg.(*lnwire.AnnounceSignatures1)
2004+
if ok {
2005+
t.Fatalf("got an AnnounceSignatures1 when none was "+
2006+
"expected %T", msg)
2007+
}
2008+
case <-time.After(2 * time.Second):
2009+
break
2010+
}
2011+
2012+
// We prune the syncer, simulating the remote peer having disconnected.
2013+
tCtx.gossiper.PruneSyncState(remotePeer.PubKey())
2014+
2015+
// We simulate the remote peer coming back online.
2016+
tCtx.gossiper.InitSyncState(remotePeer)
2017+
remoteSyncer1 := assertSyncerExistence(t, syncMgr, remotePeer)
2018+
assertTransitionToChansSynced(t, remoteSyncer1, remotePeer)
2019+
assertActiveGossipTimestampRange(t, remotePeer)
2020+
assertSyncerStatus(t, remoteSyncer1, chansSynced, ActiveSync)
2021+
2022+
// Now give the gossiper the remote proof yet again. This should trigger
2023+
// a send of our signature announcement, because we are now on a new
2024+
// connection.
2025+
t.Run("process remote proof - third resend", func(t *testing.T) {
2026+
processRemoteProof(t)
2027+
})
19612028

19622029
// We expect the gossiper to send this message to the remote peer.
19632030
select {

0 commit comments

Comments
 (0)