From e4e093581697a0c1e0f94c9f54a7f008ad0df5c8 Mon Sep 17 00:00:00 2001 From: positiveblue Date: Sun, 5 Dec 2021 20:24:06 -0800 Subject: [PATCH] lnrpc/peers: skeleton logic for `updateNodeAnnouncement` Basic logic for the endpoint: - Get the current nodeAnn information - Calculate modifications - Apply modifications - Return changes --- .golangci.yml | 1 + config.go | 2 + dev.Dockerfile | 2 +- lnrpc/peersrpc/config_active.go | 25 +++- lnrpc/peersrpc/peers.pb.gw.go | 167 +++++++++++++++++++++++++ lnrpc/peersrpc/peers.swagger.json | 63 +++++++++- lnrpc/peersrpc/peers.yaml | 6 + lnrpc/peersrpc/peers_server.go | 55 +++++++- lntest/harness_node.go | 4 + lntest/itest/lnd_channel_graph_test.go | 25 ++++ lntest/itest/lnd_test_list_on_test.go | 4 + log.go | 2 + make/release_flags.mk | 4 +- make/testing_flags.mk | 2 +- rpcserver.go | 12 +- server.go | 48 +++++++ subrpcserver_config.go | 24 ++++ 17 files changed, 438 insertions(+), 8 deletions(-) create mode 100644 lnrpc/peersrpc/peers.pb.gw.go diff --git a/.golangci.yml b/.golangci.yml index 3949f123e..6c1fc1908 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -17,6 +17,7 @@ run: - chainrpc - dev - invoicesrpc + - peersrpc - signrpc - walletrpc - watchtowerrpc diff --git a/config.go b/config.go index b2f5dfbd0..91a42a299 100644 --- a/config.go +++ b/config.go @@ -35,6 +35,7 @@ import ( "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/lncfg" "github.com/lightningnetwork/lnd/lnrpc" + "github.com/lightningnetwork/lnd/lnrpc/peersrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc" "github.com/lightningnetwork/lnd/lnrpc/signrpc" "github.com/lightningnetwork/lnd/lnwallet" @@ -516,6 +517,7 @@ func DefaultConfig() Config { SubRPCServers: &subRPCServerConfigs{ SignRPC: &signrpc.Config{}, RouterRPC: routerrpc.DefaultConfig(), + PeersRPC: &peersrpc.Config{}, }, Autopilot: &lncfg.AutoPilot{ MaxChannels: 5, diff --git a/dev.Dockerfile b/dev.Dockerfile index 2282c3c7f..df77303e7 100644 --- a/dev.Dockerfile +++ b/dev.Dockerfile @@ -22,7 +22,7 @@ COPY . /go/src/github.com/lightningnetwork/lnd RUN cd /go/src/github.com/lightningnetwork/lnd \ && make \ -&& make install tags="signrpc walletrpc chainrpc invoicesrpc" +&& make install tags="signrpc walletrpc chainrpc invoicesrpc peersrpc" # Start a new, final image to reduce size. FROM alpine as final diff --git a/lnrpc/peersrpc/config_active.go b/lnrpc/peersrpc/config_active.go index f661875fc..860e7627e 100644 --- a/lnrpc/peersrpc/config_active.go +++ b/lnrpc/peersrpc/config_active.go @@ -3,4 +3,27 @@ package peersrpc -type Config struct{} +import ( + "net" + + "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/netann" +) + +// Config is the primary configuration struct for the peers RPC subserver. +// It contains all the items required for the server to carry out its duties. +// The fields with struct tags are meant to be parsed as normal configuration +// options, while if able to be populated, the latter fields MUST also be +// specified. +type Config struct { + // GetNodeAnnouncement is used to send our retrieve the current + // node announcement information. + GetNodeAnnouncement func() (lnwire.NodeAnnouncement, error) + + // ParseAddr parses an address from its string format to a net.Addr. + ParseAddr func(addr string) (net.Addr, error) + + // UpdateNodeAnnouncement updates our node announcement applying the + // given NodeAnnModifiers and broadcasts the new version to the network. + UpdateNodeAnnouncement func(...netann.NodeAnnModifier) error +} diff --git a/lnrpc/peersrpc/peers.pb.gw.go b/lnrpc/peersrpc/peers.pb.gw.go new file mode 100644 index 000000000..f45a8db69 --- /dev/null +++ b/lnrpc/peersrpc/peers.pb.gw.go @@ -0,0 +1,167 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: peersrpc/peers.proto + +/* +Package peersrpc is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package peersrpc + +import ( + "context" + "io" + "net/http" + + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "github.com/grpc-ecosystem/grpc-gateway/v2/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" +) + +// Suppress "imported and not used" errors +var _ codes.Code +var _ io.Reader +var _ status.Status +var _ = runtime.String +var _ = utilities.NewDoubleArray +var _ = metadata.Join + +func request_Peers_UpdateNodeAnnouncement_0(ctx context.Context, marshaler runtime.Marshaler, client PeersClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq NodeAnnouncementUpdateRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.UpdateNodeAnnouncement(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Peers_UpdateNodeAnnouncement_0(ctx context.Context, marshaler runtime.Marshaler, server PeersServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq NodeAnnouncementUpdateRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.UpdateNodeAnnouncement(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterPeersHandlerServer registers the http handlers for service Peers to "mux". +// UnaryRPC :call PeersServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterPeersHandlerFromEndpoint instead. +func RegisterPeersHandlerServer(ctx context.Context, mux *runtime.ServeMux, server PeersServer) error { + + mux.Handle("POST", pattern_Peers_UpdateNodeAnnouncement_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/peersrpc.Peers/UpdateNodeAnnouncement", runtime.WithHTTPPathPattern("/v2/peers/nodeannouncement")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Peers_UpdateNodeAnnouncement_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Peers_UpdateNodeAnnouncement_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterPeersHandlerFromEndpoint is same as RegisterPeersHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterPeersHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.Dial(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterPeersHandler(ctx, mux, conn) +} + +// RegisterPeersHandler registers the http handlers for service Peers to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterPeersHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterPeersHandlerClient(ctx, mux, NewPeersClient(conn)) +} + +// RegisterPeersHandlerClient registers the http handlers for service Peers +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "PeersClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "PeersClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "PeersClient" to call the correct interceptors. +func RegisterPeersHandlerClient(ctx context.Context, mux *runtime.ServeMux, client PeersClient) error { + + mux.Handle("POST", pattern_Peers_UpdateNodeAnnouncement_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/peersrpc.Peers/UpdateNodeAnnouncement", runtime.WithHTTPPathPattern("/v2/peers/nodeannouncement")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Peers_UpdateNodeAnnouncement_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Peers_UpdateNodeAnnouncement_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_Peers_UpdateNodeAnnouncement_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v2", "peers", "nodeannouncement"}, "")) +) + +var ( + forward_Peers_UpdateNodeAnnouncement_0 = runtime.ForwardResponseMessage +) diff --git a/lnrpc/peersrpc/peers.swagger.json b/lnrpc/peersrpc/peers.swagger.json index dd896cad3..c7b9970cf 100644 --- a/lnrpc/peersrpc/peers.swagger.json +++ b/lnrpc/peersrpc/peers.swagger.json @@ -15,7 +15,41 @@ "produces": [ "application/json" ], - "paths": {}, + "paths": { + "/v2/peers/nodeannouncement": { + "post": { + "summary": "lncli: peers updatenodeannouncement\nUpdateNodeAnnouncement allows the caller to update the node parameters\nand broadcasts a new version of the node announcement to its peers.", + "operationId": "Peers_UpdateNodeAnnouncement", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/peersrpcNodeAnnouncementUpdateResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/peersrpcNodeAnnouncementUpdateRequest" + } + } + ], + "tags": [ + "Peers" + ] + } + } + }, "definitions": { "lnrpcFeatureBit": { "type": "string", @@ -62,6 +96,33 @@ } } }, + "peersrpcNodeAnnouncementUpdateRequest": { + "type": "object", + "properties": { + "feature_updates": { + "type": "array", + "items": { + "$ref": "#/definitions/peersrpcUpdateFeatureAction" + }, + "description": "Set of changes for the features that the node supports." + }, + "color": { + "type": "string", + "description": "Color is the node's color in hex code format." + }, + "alias": { + "type": "string", + "description": "Alias or nick name of the node." + }, + "address_updates": { + "type": "array", + "items": { + "$ref": "#/definitions/peersrpcUpdateAddressAction" + }, + "description": "Set of changes for the node's known addresses." + } + } + }, "peersrpcNodeAnnouncementUpdateResponse": { "type": "object", "properties": { diff --git a/lnrpc/peersrpc/peers.yaml b/lnrpc/peersrpc/peers.yaml index c4ad3476d..b1b9544f1 100644 --- a/lnrpc/peersrpc/peers.yaml +++ b/lnrpc/peersrpc/peers.yaml @@ -1,2 +1,8 @@ type: google.api.Service config_version: 3 + +http: + rules: + - selector: peersrpc.Peers.UpdateNodeAnnouncement + post: "/v2/peers/nodeannouncement" + body: "*" diff --git a/lnrpc/peersrpc/peers_server.go b/lnrpc/peersrpc/peers_server.go index c8af26617..5cfd7e589 100644 --- a/lnrpc/peersrpc/peers_server.go +++ b/lnrpc/peersrpc/peers_server.go @@ -5,10 +5,12 @@ package peersrpc import ( "context" + "fmt" "sync/atomic" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "github.com/lightningnetwork/lnd/lnrpc" + "github.com/lightningnetwork/lnd/netann" "google.golang.org/grpc" "gopkg.in/macaroon-bakery.v2/bakery" ) @@ -23,7 +25,12 @@ const ( var ( // macPermissions maps RPC calls to the permissions they require. - macPermissions = map[string][]bakery.Op{} + macPermissions = map[string][]bakery.Op{ + "/peersrpc.Peers/UpdateNodeAnnouncement": {{ + Entity: "peers", + Action: "write", + }}, + } ) // ServerShell is a shell struct holding a reference to the actual sub-server. @@ -119,6 +126,17 @@ func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error { func (r *ServerShell) RegisterWithRestServer(ctx context.Context, mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error { + // We make sure that we register it with the main REST server to ensure + // all our methods are routed properly. + err := RegisterPeersHandlerFromEndpoint(ctx, mux, dest, opts) + if err != nil { + log.Errorf("Could not register Peers REST server "+ + "with root REST server: %v", err) + return err + } + + log.Debugf("Peers REST server successfully registered with " + + "root REST server") return nil } @@ -140,3 +158,38 @@ func (r *ServerShell) CreateSubServer(configRegistry lnrpc.SubServerConfigDispat r.PeersServer = subServer return subServer, macPermissions, nil } + +// UpdateNodeAnnouncement allows the caller to update the node parameters +// and broadcasts a new version of the node announcement to its peers. +func (s *Server) UpdateNodeAnnouncement(_ context.Context, + req *NodeAnnouncementUpdateRequest) ( + *NodeAnnouncementUpdateResponse, error) { + + resp := &NodeAnnouncementUpdateResponse{} + nodeModifiers := make([]netann.NodeAnnModifier, 0) + + _, err := s.cfg.GetNodeAnnouncement() + if err != nil { + return nil, fmt.Errorf("unable to get current node "+ + "announcement: %v", err) + } + + // TODO(positiveblue): apply feature bit modifications + + // TODO(positiveblue): apply color modifications + + // TODO(positiveblue): apply alias modifications + + // TODO(positiveblue): apply addresses modifications + + if len(nodeModifiers) == 0 { + return nil, fmt.Errorf("unable detect any new values to " + + "update the node announcement") + } + + if err := s.cfg.UpdateNodeAnnouncement(nodeModifiers...); err != nil { + return nil, err + } + + return resp, nil +} diff --git a/lntest/harness_node.go b/lntest/harness_node.go index f3e7b3496..477b97307 100644 --- a/lntest/harness_node.go +++ b/lntest/harness_node.go @@ -25,6 +25,7 @@ import ( "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/chainrpc" "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" + "github.com/lightningnetwork/lnd/lnrpc/peersrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc" "github.com/lightningnetwork/lnd/lnrpc/signrpc" "github.com/lightningnetwork/lnd/lnrpc/walletrpc" @@ -356,6 +357,7 @@ type HarnessNode struct { lnrpc.LightningClient lnrpc.WalletUnlockerClient invoicesrpc.InvoicesClient + peersrpc.PeersClient SignerClient signrpc.SignerClient RouterClient routerrpc.RouterClient WalletKitClient walletrpc.WalletKitClient @@ -387,6 +389,7 @@ type RPCClients struct { var _ lnrpc.LightningClient = (*HarnessNode)(nil) var _ lnrpc.WalletUnlockerClient = (*HarnessNode)(nil) var _ invoicesrpc.InvoicesClient = (*HarnessNode)(nil) +var _ peersrpc.PeersClient = (*HarnessNode)(nil) // nextNodeID generates a unique sequence to be used as the node's ID. func nextNodeID() int { @@ -952,6 +955,7 @@ func (hn *HarnessNode) initLightningClient() error { hn.Watchtower = watchtowerrpc.NewWatchtowerClient(conn) hn.WatchtowerClient = wtclientrpc.NewWatchtowerClientClient(conn) hn.SignerClient = signrpc.NewSignerClient(conn) + hn.PeersClient = peersrpc.NewPeersClient(conn) hn.StateClient = lnrpc.NewStateClient(conn) hn.ChainClient = chainrpc.NewChainNotifierClient(conn) diff --git a/lntest/itest/lnd_channel_graph_test.go b/lntest/itest/lnd_channel_graph_test.go index 197461bd3..b0643ad9c 100644 --- a/lntest/itest/lnd_channel_graph_test.go +++ b/lntest/itest/lnd_channel_graph_test.go @@ -12,6 +12,7 @@ import ( "github.com/lightningnetwork/lnd/chainreg" "github.com/lightningnetwork/lnd/funding" "github.com/lightningnetwork/lnd/lnrpc" + "github.com/lightningnetwork/lnd/lnrpc/peersrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc" "github.com/lightningnetwork/lnd/lntest" "github.com/lightningnetwork/lnd/lntest/wait" @@ -767,3 +768,27 @@ func subscribeGraphNotifications(ctxb context.Context, t *harnessTest, quit: quit, } } + +// 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() + + var lndArgs []string + dave := net.NewNode(t.t, "Dave", lndArgs) + defer shutdownAndAssert(net, t, dave) + + // 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 + // modifiers are applied. + invalidNodeAnnReq := &peersrpc.NodeAnnouncementUpdateRequest{ + Alias: "", + } + ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout) + defer cancel() + + _, err := dave.UpdateNodeAnnouncement(ctxt, invalidNodeAnnReq) + require.Error(t.t, err, "requests without modifiers should field") +} diff --git a/lntest/itest/lnd_test_list_on_test.go b/lntest/itest/lnd_test_list_on_test.go index 28a3a9b6a..fe6dc0a2a 100644 --- a/lntest/itest/lnd_test_list_on_test.go +++ b/lntest/itest/lnd_test_list_on_test.go @@ -84,6 +84,10 @@ var allTestCases = []*testCase{ name: "update channel status", test: testUpdateChanStatus, }, + { + name: "test update node announcement rpc", + test: testUpdateNodeAnnouncement, + }, { name: "list outgoing payments", test: testListPayments, diff --git a/log.go b/log.go index ffbd0de50..64156e2fc 100644 --- a/log.go +++ b/log.go @@ -25,6 +25,7 @@ import ( "github.com/lightningnetwork/lnd/lnrpc/chainrpc" "github.com/lightningnetwork/lnd/lnrpc/devrpc" "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" + "github.com/lightningnetwork/lnd/lnrpc/peersrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc" "github.com/lightningnetwork/lnd/lnrpc/signrpc" "github.com/lightningnetwork/lnd/lnrpc/verrpc" @@ -168,6 +169,7 @@ func SetupLoggers(root *build.RotatingLogWriter, interceptor signal.Interceptor) AddSubLogger(root, tor.Subsystem, interceptor, tor.UseLogger) AddSubLogger(root, btcwallet.Subsystem, interceptor, btcwallet.UseLogger) AddSubLogger(root, rpcwallet.Subsystem, interceptor, rpcwallet.UseLogger) + AddSubLogger(root, peersrpc.Subsystem, interceptor, peersrpc.UseLogger) } // AddSubLogger is a helper method to conveniently create and register the diff --git a/make/release_flags.mk b/make/release_flags.mk index 2c1db19a3..a5e7f6108 100644 --- a/make/release_flags.mk +++ b/make/release_flags.mk @@ -37,9 +37,9 @@ windows-386 \ windows-amd64 \ windows-arm -RELEASE_TAGS = autopilotrpc signrpc walletrpc chainrpc invoicesrpc watchtowerrpc monitoring kvdb_postgres kvdb_etcd +RELEASE_TAGS = autopilotrpc signrpc walletrpc chainrpc invoicesrpc watchtowerrpc monitoring peersrpc kvdb_postgres kvdb_etcd -WASM_RELEASE_TAGS = autopilotrpc signrpc walletrpc chainrpc invoicesrpc watchtowerrpc monitoring +WASM_RELEASE_TAGS = autopilotrpc signrpc walletrpc chainrpc invoicesrpc watchtowerrpc monitoring peersrpc # One can either specify a git tag as the version suffix or one is generated # from the current date. diff --git a/make/testing_flags.mk b/make/testing_flags.mk index cad28daec..002ea4b17 100644 --- a/make/testing_flags.mk +++ b/make/testing_flags.mk @@ -1,5 +1,5 @@ DEV_TAGS = dev -RPC_TAGS = autopilotrpc chainrpc invoicesrpc routerrpc signrpc verrpc walletrpc watchtowerrpc wtclientrpc +RPC_TAGS = autopilotrpc chainrpc invoicesrpc peersrpc routerrpc signrpc verrpc walletrpc watchtowerrpc wtclientrpc LOG_TAGS = TEST_FLAGS = ITEST_FLAGS = diff --git a/rpcserver.go b/rpcserver.go index 6a08b342d..6c768d0f9 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -8,6 +8,7 @@ import ( "fmt" "io" "math" + "net" "net/http" "runtime" "sort" @@ -729,6 +730,14 @@ func (r *rpcServer) addDeps(s *server, macService *macaroons.Service, return s.featureMgr.Get(feature.SetInvoiceAmp) } + getNodeAnnouncement := func() (lnwire.NodeAnnouncement, error) { + return s.genNodeAnnouncement(false) + } + + parseAddr := func(addr string) (net.Addr, error) { + return parseAddr(addr, r.cfg.net) + } + var ( subServers []lnrpc.SubServer subServerPerms []lnrpc.MacaroonPerms @@ -745,7 +754,8 @@ func (r *rpcServer) addDeps(s *server, macService *macaroons.Service, routerBackend, s.nodeSigner, s.graphDB, s.chanStateDB, s.sweeper, tower, s.towerClient, s.anchorTowerClient, r.cfg.net.ResolveTCPAddr, genInvoiceFeatures, - genAmpInvoiceFeatures, rpcsLog, + genAmpInvoiceFeatures, getNodeAnnouncement, + s.updateAndBrodcastSelfNode, parseAddr, rpcsLog, ) if err != nil { return err diff --git a/server.go b/server.go index 4accc6e97..ad08fe4b0 100644 --- a/server.go +++ b/server.go @@ -2734,6 +2734,54 @@ func (s *server) genNodeAnnouncement(refresh bool, return *s.currentNodeAnn, nil } +// updateAndBrodcastSelfNode generates a new node announcement +// applying the giving modifiers and updating the time stamp +// to ensure it propagates through the network. Then it brodcasts +// it to the network. +func (s *server) updateAndBrodcastSelfNode( + modifiers ...netann.NodeAnnModifier) error { + + newNodeAnn, err := s.genNodeAnnouncement(true, modifiers...) + if err != nil { + return fmt.Errorf("unable to generate new node "+ + "announcement: %v", err) + } + + // Update the on-disk version of our announcement. + // Load and modify self node istead of creating anew instance so we + // don't risk overwriting any existing values. + selfNode, err := s.graphDB.SourceNode() + if err != nil { + return fmt.Errorf("unable to get current source node: %v", err) + } + + selfNode.HaveNodeAnnouncement = true + selfNode.LastUpdate = time.Unix(int64(newNodeAnn.Timestamp), 0) + selfNode.Addresses = newNodeAnn.Addresses + selfNode.Alias = newNodeAnn.Alias.String() + selfNode.Features = lnwire.NewFeatureVector( + newNodeAnn.Features, lnwire.Features, + ) + selfNode.Color = newNodeAnn.RGBColor + selfNode.AuthSigBytes = newNodeAnn.Signature.ToSignatureBytes() + + copy(selfNode.PubKeyBytes[:], s.identityECDH.PubKey().SerializeCompressed()) + + if err := s.graphDB.SetSourceNode(selfNode); err != nil { + return fmt.Errorf("can't set self node: %v", err) + } + + // Finally, propagate it to the nodes in the network. + err = s.BroadcastMessage(nil, &newNodeAnn) + if err != nil { + rpcsLog.Debugf("Unable to broadcast new node "+ + "announcement to peers: %v", err) + return err + } + + return nil +} + type nodeAddresses struct { pubKey *btcec.PublicKey addresses []net.Addr diff --git a/subrpcserver_config.go b/subrpcserver_config.go index e11993f10..d9473edfd 100644 --- a/subrpcserver_config.go +++ b/subrpcserver_config.go @@ -2,6 +2,7 @@ package lnd import ( "fmt" + "net" "reflect" "github.com/btcsuite/btcd/chaincfg" @@ -16,6 +17,7 @@ import ( "github.com/lightningnetwork/lnd/lnrpc/chainrpc" "github.com/lightningnetwork/lnd/lnrpc/devrpc" "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" + "github.com/lightningnetwork/lnd/lnrpc/peersrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc" "github.com/lightningnetwork/lnd/lnrpc/signrpc" "github.com/lightningnetwork/lnd/lnrpc/walletrpc" @@ -60,6 +62,10 @@ type subRPCServerConfigs struct { // as a gRPC service. InvoicesRPC *invoicesrpc.Config `group:"invoicesrpc" namespace:"invoicesrpc"` + // PeersRPC is a sub-RPC server that exposes peer related methods + // as a gRPC service. + PeersRPC *peersrpc.Config `group:"peersrpc" namespace:"peersrpc"` + // RouterRPC is a sub-RPC server the exposes functionality that allows // clients to send payments on the network, and perform Lightning // payment related queries such as requests for estimates of off-chain @@ -107,6 +113,9 @@ func (s *subRPCServerConfigs) PopulateDependencies(cfg *Config, tcpResolver lncfg.TCPResolver, genInvoiceFeatures func() *lnwire.FeatureVector, genAmpInvoiceFeatures func() *lnwire.FeatureVector, + getNodeAnnouncement func() (lnwire.NodeAnnouncement, error), + updateNodeAnnouncement func(modifiers ...netann.NodeAnnModifier) error, + parseAddr func(addr string) (net.Addr, error), rpcLogger btclog.Logger) error { // First, we'll use reflect to obtain a version of the config struct @@ -287,6 +296,21 @@ func (s *subRPCServerConfigs) PopulateDependencies(cfg *Config, reflect.ValueOf(graphDB), ) + case *peersrpc.Config: + subCfgValue := extractReflectValue(subCfg) + + subCfgValue.FieldByName("GetNodeAnnouncement").Set( + reflect.ValueOf(getNodeAnnouncement), + ) + + subCfgValue.FieldByName("ParseAddr").Set( + reflect.ValueOf(parseAddr), + ) + + subCfgValue.FieldByName("UpdateNodeAnnouncement").Set( + reflect.ValueOf(updateNodeAnnouncement), + ) + default: return fmt.Errorf("unknown field: %v, %T", fieldName, cfg)