diff --git a/discovery/gossiper.go b/discovery/gossiper.go index f7154657..cc290704 100644 --- a/discovery/gossiper.go +++ b/discovery/gossiper.go @@ -1293,8 +1293,22 @@ func (d *AuthenticatedGossiper) processChanPolicyUpdate( } // We'll avoid broadcasting any updates for private channels to - // avoid directly giving away their existence. + // avoid directly giving away their existence. Instead, we'll + // send the update directly to the remote party. if edgeInfo.info.AuthProof == nil { + remotePubKey := remotePubFromChanInfo( + edgeInfo.info, chanUpdate.ChannelFlags, + ) + err := d.reliableSender.sendMessage( + chanUpdate, remotePubKey, + ) + if err != nil { + log.Errorf("Unable to reliably send %v for "+ + "channel=%v to peer=%x: %v", + chanUpdate.MsgType(), + chanUpdate.ShortChannelID, + remotePubKey, err) + } continue } @@ -1928,13 +1942,9 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement( // so we'll try sending the update directly to the remote peer. if !nMsg.isRemote && chanInfo.AuthProof == nil { // Get our peer's public key. - var remotePubKey [33]byte - switch { - case msg.ChannelFlags&lnwire.ChanUpdateDirection == 0: - remotePubKey = chanInfo.NodeKey2Bytes - case msg.ChannelFlags&lnwire.ChanUpdateDirection == 1: - remotePubKey = chanInfo.NodeKey1Bytes - } + remotePubKey := remotePubFromChanInfo( + chanInfo, msg.ChannelFlags, + ) // Now, we'll attempt to send the channel update message // reliably to the remote peer in the background, so diff --git a/discovery/gossiper_test.go b/discovery/gossiper_test.go index fcccb3ca..02c26f4d 100644 --- a/discovery/gossiper_test.go +++ b/discovery/gossiper_test.go @@ -3351,6 +3351,24 @@ func TestPropagateChanPolicyUpdate(t *testing.T) { sentMsgs := make(chan lnwire.Message, 10) remotePeer := &mockPeer{remoteKey, sentMsgs, ctx.gossiper.quit} + // The forced code path for sending the private ChannelUpdate to the + // remote peer will be hit, forcing it to request a notification that + // the remote peer is active. We'll ensure that it targets the proper + // pubkey, and hand it our mock peer above. + notifyErr := make(chan error, 1) + ctx.gossiper.reliableSender.cfg.NotifyWhenOnline = func( + targetPub *btcec.PublicKey, peerChan chan<- lnpeer.Peer) { + + if !targetPub.IsEqual(remoteKey) { + notifyErr <- fmt.Errorf("reliableSender attempted to send the "+ + "message to the wrong peer: expected %x got %x", + remoteKey.SerializeCompressed(), + targetPub.SerializeCompressed()) + } + + peerChan <- remotePeer + } + // With our channel announcements created, we'll now send them all to // the gossiper in order for it to process. However, we'll hold back // the channel ann proof from the first channel in order to have it be @@ -3374,12 +3392,16 @@ func TestPropagateChanPolicyUpdate(t *testing.T) { sendRemoteMsg(t, ctx, batch.remoteProofAnn, remotePeer) } - // Drain out any broadcast messages we might not have read up to this - // point. + // Drain out any broadcast or direct messages we might not have read up + // to this point. We'll also check out notifyErr to detect if the + // reliable sender had an issue sending to the remote peer. out: for { select { case <-ctx.broadcastedMessage: + case <-sentMsgs: + case err := <-notifyErr: + t.Fatal(err) default: break out } @@ -3419,6 +3441,45 @@ out: return nil }) } + + // Finally the ChannelUpdate should have been sent directly to the + // remote peer via the reliable sender. + select { + case msg := <-sentMsgs: + upd, ok := msg.(*lnwire.ChannelUpdate) + if !ok { + t.Fatalf("channel update not "+ + "broadcast, instead %T was", msg) + } + if upd.TimeLockDelta != newTimeLockDelta { + t.Fatalf("wrong delta: expected %v, "+ + "got %v", newTimeLockDelta, + upd.TimeLockDelta) + } + if upd.ShortChannelID != firstChanID { + t.Fatalf("private channel upd " + + "broadcast") + } + case <-time.After(time.Second * 5): + t.Fatalf("message not sent directly to peer") + } + + // At this point, no other ChannelUpdate messages should be broadcast + // as we sent the two public ones to the network, and the private one + // was sent directly to the peer. + for { + select { + case msg := <-ctx.broadcastedMessage: + if upd, ok := msg.msg.(*lnwire.ChannelUpdate); ok { + if upd.ShortChannelID == firstChanID { + t.Fatalf("chan update msg received: %v", + spew.Sdump(msg)) + } + } + default: + return + } + } } func assertMessage(t *testing.T, expected, got lnwire.Message) { diff --git a/discovery/utils.go b/discovery/utils.go index 3c0fdc17..148e667b 100644 --- a/discovery/utils.go +++ b/discovery/utils.go @@ -146,3 +146,19 @@ func SignAnnouncement(signer lnwallet.MessageSigner, pubKey *btcec.PublicKey, return signer.SignMessage(pubKey, data) } + +// remotePubFromChanInfo returns the public key of the remote peer given a +// ChannelEdgeInfo that describe a channel we have with them. +func remotePubFromChanInfo(chanInfo *channeldb.ChannelEdgeInfo, + chanFlags lnwire.ChanUpdateChanFlags) [33]byte { + + var remotePubKey [33]byte + switch { + case chanFlags&lnwire.ChanUpdateDirection == 0: + remotePubKey = chanInfo.NodeKey2Bytes + case chanFlags&lnwire.ChanUpdateDirection == 1: + remotePubKey = chanInfo.NodeKey1Bytes + } + + return remotePubKey +}