lnrpc/peers: handle alias changes in updateNodeAnnouncement

This commit is contained in:
positiveblue 2022-01-08 12:16:42 -08:00
parent e4e0935816
commit ce4813940d
No known key found for this signature in database
GPG key ID: 4FFF2510928804DC
4 changed files with 165 additions and 6 deletions

View file

@ -10,6 +10,7 @@ import (
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/netann"
"google.golang.org/grpc"
"gopkg.in/macaroon-bakery.v2/bakery"
@ -168,7 +169,7 @@ func (s *Server) UpdateNodeAnnouncement(_ context.Context,
resp := &NodeAnnouncementUpdateResponse{}
nodeModifiers := make([]netann.NodeAnnModifier, 0)
_, err := s.cfg.GetNodeAnnouncement()
currentNodeAnn, err := s.cfg.GetNodeAnnouncement()
if err != nil {
return nil, fmt.Errorf("unable to get current node "+
"announcement: %v", err)
@ -178,7 +179,24 @@ func (s *Server) UpdateNodeAnnouncement(_ context.Context,
// TODO(positiveblue): apply color modifications
// TODO(positiveblue): apply alias modifications
if req.Alias != "" {
alias, err := lnwire.NewNodeAlias(req.Alias)
if err != nil {
return nil, fmt.Errorf("invalid alias value: %v", err)
}
if alias != currentNodeAnn.Alias {
resp.Ops = append(resp.Ops, &lnrpc.Op{
Entity: "alias",
Actions: []string{
fmt.Sprintf("changed to %v", alias),
},
})
nodeModifiers = append(
nodeModifiers,
netann.NodeAnnSetAlias(alias),
)
}
}
// TODO(positiveblue): apply addresses modifications

View file

@ -16,6 +16,7 @@ import (
"github.com/go-errors/errors"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnrpc/peersrpc"
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
"github.com/lightningnetwork/lnd/lntest"
@ -1856,3 +1857,33 @@ func assertAnchorOutputLost(t *harnessTest, node *lntest.HarnessNode,
}, defaultTimeout)
require.NoError(t.t, err, "anchor doesn't show as being lost")
}
// assertNodeAnnouncement compares that two node announcements match.
func assertNodeAnnouncement(t *harnessTest, n1, n2 *lnrpc.NodeUpdate) {
// Alias should match.
require.Equal(t.t, n1.Alias, n2.Alias, "alias don't match")
}
// assertUpdateNodeAnnouncementResponse is a helper function to assert
// the response expected values.
func assertUpdateNodeAnnouncementResponse(t *harnessTest,
response *peersrpc.NodeAnnouncementUpdateResponse,
expectedOps map[string]int) {
require.Equal(
t.t, len(response.Ops), len(expectedOps),
"unexpected number of Ops updating dave's node announcement",
)
ops := make(map[string]int, len(response.Ops))
for _, op := range response.Ops {
ops[op.Entity] = len(op.Actions)
}
for k, v := range expectedOps {
if v != ops[k] {
t.Fatalf("unexpected number of actions for operation "+
"%s: got %d wanted %d", k, ops[k], v)
}
}
}

View file

@ -5,6 +5,7 @@ import (
"context"
"fmt"
"io"
"strings"
"testing"
"time"
@ -769,16 +770,80 @@ func subscribeGraphNotifications(ctxb context.Context, t *harnessTest,
}
}
// waitForNodeAnnUpdates monitors the nodeAnnUpdates until we get one for
// the expected node and asserts that has the expected information.
func waitForNodeAnnUpdates(graphSub graphSubscription, nodePubKey string,
expectedUpdate *lnrpc.NodeUpdate, t *harnessTest) {
for {
select {
case graphUpdate := <-graphSub.updateChan:
for _, update := range graphUpdate.NodeUpdates {
if update.IdentityKey == nodePubKey {
assertNodeAnnouncement(
t, update, expectedUpdate,
)
return
}
}
case err := <-graphSub.errChan:
t.Fatalf("unable to recv graph update: %v", err)
case <-time.After(defaultTimeout):
t.Fatalf("did not receive node ann update")
}
}
}
// testUpdateNodeAnnouncement ensures that the RPC endpoint validates
// the requests correctly and that the new node announcement is brodcasted
// with the right information after updating our node.
func testUpdateNodeAnnouncement(net *lntest.NetworkHarness, t *harnessTest) {
ctxb := context.Background()
// context timeout for the whole test.
ctxt, cancel := context.WithTimeout(
context.Background(), defaultTimeout,
)
defer cancel()
// Launch notification clients for alice, such that we can
// get notified when there are updates in the graph.
aliceSub := subscribeGraphNotifications(ctxt, t, net.Alice)
defer close(aliceSub.quit)
var lndArgs []string
dave := net.NewNode(t.t, "Dave", lndArgs)
defer shutdownAndAssert(net, t, dave)
// Get dave default information so we can compare
// it lately with the brodcasted updates.
nodeInfoReq := &lnrpc.GetInfoRequest{}
resp, err := dave.GetInfo(ctxt, nodeInfoReq)
require.NoError(t.t, err, "unable to get dave's information")
defaultDaveNodeAnn := &lnrpc.NodeUpdate{
Alias: resp.Alias,
}
// Dave must have an open channel before he can send a node
// announcement, so we open a channel with Bob.
net.ConnectNodes(t.t, net.Bob, dave)
// Go ahead and open a channel between Bob and Dave. This
// ensures that Alice receives the node announcement from Bob as part of
// the announcement broadcast.
chanPoint := openChannelAndAssert(
t, net, net.Bob, dave,
lntest.OpenChannelParams{
Amt: 1000000,
},
)
require.NoError(t.t, err, "unexpected error opening a channel")
// Wait for Alice to receive dave's node announcement with the default
// values.
waitForNodeAnnUpdates(
aliceSub, dave.PubKeyStr, defaultDaveNodeAnn, t,
)
// We cannot differentiate between requests with Alias = "" and requests
// that do not provide that field. If a user sets Alias = "" in the request
// the field will simply be ignored. The request must fail because no
@ -786,9 +851,46 @@ func testUpdateNodeAnnouncement(net *lntest.NetworkHarness, t *harnessTest) {
invalidNodeAnnReq := &peersrpc.NodeAnnouncementUpdateRequest{
Alias: "",
}
ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout)
defer cancel()
_, err := dave.UpdateNodeAnnouncement(ctxt, invalidNodeAnnReq)
_, err = dave.UpdateNodeAnnouncement(ctxt, invalidNodeAnnReq)
require.Error(t.t, err, "requests without modifiers should field")
// Alias too long.
invalidNodeAnnReq = &peersrpc.NodeAnnouncementUpdateRequest{
Alias: strings.Repeat("a", 50),
}
_, err = dave.UpdateNodeAnnouncement(ctxt, invalidNodeAnnReq)
require.Error(t.t, err, "failed to validate an invalid alias for an "+
"update node announcement request")
// Update Node.
newAlias := "new-alias"
nodeAnnReq := &peersrpc.NodeAnnouncementUpdateRequest{
Alias: newAlias,
}
response, err := dave.UpdateNodeAnnouncement(ctxt, nodeAnnReq)
require.NoError(t.t, err, "unable to update dave's node announcement")
expectedOps := map[string]int{
"alias": 1,
}
assertUpdateNodeAnnouncementResponse(t, response, expectedOps)
// After updating the node we expect the update to contain
// the requested color, requested alias and the new added addresses.
newDaveNodeAnn := &lnrpc.NodeUpdate{
Alias: newAlias,
}
// We'll then wait for Alice to receive dave's node announcement
// with the new values.
waitForNodeAnnUpdates(
aliceSub, dave.PubKeyStr, newDaveNodeAnn, t,
)
// Close the channel between Bob and Dave.
closeChannelAndAssert(t, net, net.Bob, chanPoint, false)
}

View file

@ -13,6 +13,14 @@ import (
// lnwire.NodeAnnouncement.
type NodeAnnModifier func(*lnwire.NodeAnnouncement)
// NodeAnnSetAlias is a functional option that sets the alias of the
// given node announcment.
func NodeAnnSetAlias(alias lnwire.NodeAlias) func(*lnwire.NodeAnnouncement) {
return func(nodeAnn *lnwire.NodeAnnouncement) {
nodeAnn.Alias = alias
}
}
// NodeAnnSetAddrs is a functional option that allows updating the addresses of
// the given node announcement.
func NodeAnnSetAddrs(addrs []net.Addr) func(*lnwire.NodeAnnouncement) {