From d2f016c1059215cf7b674eb5d38a44ed845447e8 Mon Sep 17 00:00:00 2001 From: positiveblue Date: Thu, 25 Nov 2021 18:20:27 -0800 Subject: [PATCH 01/11] lnrpc: add peers `.proto` files --- lnrpc/gen_protos.sh | 2 +- lnrpc/peersrpc/peers.pb.go | 65 +++++++++++++++++++++++++++++++ lnrpc/peersrpc/peers.proto | 10 +++++ lnrpc/peersrpc/peers.swagger.json | 46 ++++++++++++++++++++++ lnrpc/peersrpc/peers.yaml | 2 + lnrpc/peersrpc/peers_grpc.pb.go | 61 +++++++++++++++++++++++++++++ 6 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 lnrpc/peersrpc/peers.pb.go create mode 100644 lnrpc/peersrpc/peers.proto create mode 100644 lnrpc/peersrpc/peers.swagger.json create mode 100644 lnrpc/peersrpc/peers.yaml create mode 100644 lnrpc/peersrpc/peers_grpc.pb.go diff --git a/lnrpc/gen_protos.sh b/lnrpc/gen_protos.sh index 531f6a43b8..fb01b71b86 100755 --- a/lnrpc/gen_protos.sh +++ b/lnrpc/gen_protos.sh @@ -48,7 +48,7 @@ function generate() { --custom_opt="$opts" \ lightning.proto stateservice.proto walletunlocker.proto - PACKAGES="autopilotrpc chainrpc invoicesrpc routerrpc signrpc verrpc walletrpc watchtowerrpc wtclientrpc devrpc" + PACKAGES="autopilotrpc chainrpc invoicesrpc peersrpc routerrpc signrpc verrpc walletrpc watchtowerrpc wtclientrpc devrpc" for package in $PACKAGES; do # Special import for the wallet kit. manual_import="" diff --git a/lnrpc/peersrpc/peers.pb.go b/lnrpc/peersrpc/peers.pb.go new file mode 100644 index 0000000000..4139c8aea9 --- /dev/null +++ b/lnrpc/peersrpc/peers.pb.go @@ -0,0 +1,65 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc v3.6.1 +// source: peersrpc/peers.proto + +package peersrpc + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +var File_peersrpc_peers_proto protoreflect.FileDescriptor + +var file_peersrpc_peers_proto_rawDesc = []byte{ + 0x0a, 0x14, 0x70, 0x65, 0x65, 0x72, 0x73, 0x72, 0x70, 0x63, 0x2f, 0x70, 0x65, 0x65, 0x72, 0x73, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x70, 0x65, 0x65, 0x72, 0x73, 0x72, 0x70, 0x63, + 0x32, 0x07, 0x0a, 0x05, 0x50, 0x65, 0x65, 0x72, 0x73, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, + 0x67, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x6c, 0x6e, 0x64, 0x2f, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2f, 0x70, 0x65, 0x65, 0x72, 0x73, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, +} + +var file_peersrpc_peers_proto_goTypes = []interface{}{} +var file_peersrpc_peers_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_peersrpc_peers_proto_init() } +func file_peersrpc_peers_proto_init() { + if File_peersrpc_peers_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_peersrpc_peers_proto_rawDesc, + NumEnums: 0, + NumMessages: 0, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_peersrpc_peers_proto_goTypes, + DependencyIndexes: file_peersrpc_peers_proto_depIdxs, + }.Build() + File_peersrpc_peers_proto = out.File + file_peersrpc_peers_proto_rawDesc = nil + file_peersrpc_peers_proto_goTypes = nil + file_peersrpc_peers_proto_depIdxs = nil +} diff --git a/lnrpc/peersrpc/peers.proto b/lnrpc/peersrpc/peers.proto new file mode 100644 index 0000000000..a6c74634ea --- /dev/null +++ b/lnrpc/peersrpc/peers.proto @@ -0,0 +1,10 @@ +syntax = "proto3"; + +package peersrpc; + +option go_package = "github.com/lightningnetwork/lnd/lnrpc/peersrpc"; + +// Peers is a service that can be used to get information and interact +// with the other nodes of the newtwork. +service Peers { +} diff --git a/lnrpc/peersrpc/peers.swagger.json b/lnrpc/peersrpc/peers.swagger.json new file mode 100644 index 0000000000..607f7b2c72 --- /dev/null +++ b/lnrpc/peersrpc/peers.swagger.json @@ -0,0 +1,46 @@ +{ + "swagger": "2.0", + "info": { + "title": "peersrpc/peers.proto", + "version": "version not set" + }, + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "paths": {}, + "definitions": { + "protobufAny": { + "type": "object", + "properties": { + "type_url": { + "type": "string" + }, + "value": { + "type": "string", + "format": "byte" + } + } + }, + "rpcStatus": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + }, + "details": { + "type": "array", + "items": { + "$ref": "#/definitions/protobufAny" + } + } + } + } + } +} diff --git a/lnrpc/peersrpc/peers.yaml b/lnrpc/peersrpc/peers.yaml new file mode 100644 index 0000000000..c4ad3476d6 --- /dev/null +++ b/lnrpc/peersrpc/peers.yaml @@ -0,0 +1,2 @@ +type: google.api.Service +config_version: 3 diff --git a/lnrpc/peersrpc/peers_grpc.pb.go b/lnrpc/peersrpc/peers_grpc.pb.go new file mode 100644 index 0000000000..243997a5da --- /dev/null +++ b/lnrpc/peersrpc/peers_grpc.pb.go @@ -0,0 +1,61 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. + +package peersrpc + +import ( + grpc "google.golang.org/grpc" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// PeersClient is the client API for Peers service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type PeersClient interface { +} + +type peersClient struct { + cc grpc.ClientConnInterface +} + +func NewPeersClient(cc grpc.ClientConnInterface) PeersClient { + return &peersClient{cc} +} + +// PeersServer is the server API for Peers service. +// All implementations must embed UnimplementedPeersServer +// for forward compatibility +type PeersServer interface { + mustEmbedUnimplementedPeersServer() +} + +// UnimplementedPeersServer must be embedded to have forward compatible implementations. +type UnimplementedPeersServer struct { +} + +func (UnimplementedPeersServer) mustEmbedUnimplementedPeersServer() {} + +// UnsafePeersServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to PeersServer will +// result in compilation errors. +type UnsafePeersServer interface { + mustEmbedUnimplementedPeersServer() +} + +func RegisterPeersServer(s grpc.ServiceRegistrar, srv PeersServer) { + s.RegisterService(&Peers_ServiceDesc, srv) +} + +// Peers_ServiceDesc is the grpc.ServiceDesc for Peers service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Peers_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "peersrpc.Peers", + HandlerType: (*PeersServer)(nil), + Methods: []grpc.MethodDesc{}, + Streams: []grpc.StreamDesc{}, + Metadata: "peersrpc/peers.proto", +} From 9db17d7a228128a70c280c66210c5721a29a5dac Mon Sep 17 00:00:00 2001 From: positiveblue Date: Fri, 26 Nov 2021 16:03:00 -0800 Subject: [PATCH 02/11] lnrpc: add peersrpc subsever --- lnrpc/peersrpc/config_active.go | 6 ++ lnrpc/peersrpc/config_default.go | 7 ++ lnrpc/peersrpc/driver.go | 55 ++++++++++++ lnrpc/peersrpc/log.go | 48 +++++++++++ lnrpc/peersrpc/peers_server.go | 142 +++++++++++++++++++++++++++++++ 5 files changed, 258 insertions(+) create mode 100644 lnrpc/peersrpc/config_active.go create mode 100644 lnrpc/peersrpc/config_default.go create mode 100644 lnrpc/peersrpc/driver.go create mode 100644 lnrpc/peersrpc/log.go create mode 100644 lnrpc/peersrpc/peers_server.go diff --git a/lnrpc/peersrpc/config_active.go b/lnrpc/peersrpc/config_active.go new file mode 100644 index 0000000000..f661875fcf --- /dev/null +++ b/lnrpc/peersrpc/config_active.go @@ -0,0 +1,6 @@ +//go:build peersrpc +// +build peersrpc + +package peersrpc + +type Config struct{} diff --git a/lnrpc/peersrpc/config_default.go b/lnrpc/peersrpc/config_default.go new file mode 100644 index 0000000000..8b359a85c9 --- /dev/null +++ b/lnrpc/peersrpc/config_default.go @@ -0,0 +1,7 @@ +//go:build !peersrpc +// +build !peersrpc + +package peersrpc + +// Config is empty for non-peersrpc builds. +type Config struct{} diff --git a/lnrpc/peersrpc/driver.go b/lnrpc/peersrpc/driver.go new file mode 100644 index 0000000000..8abf3ed924 --- /dev/null +++ b/lnrpc/peersrpc/driver.go @@ -0,0 +1,55 @@ +//go:build peersrpc +// +build peersrpc + +package peersrpc + +import ( + "fmt" + + "github.com/lightningnetwork/lnd/lnrpc" +) + +// createNewSubServer is a helper method that will create the new sub server +// given the main config dispatcher method. If we're unable to find the config +// that is meant for us in the config dispatcher, then we'll exit with an +// error. +func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) ( + *Server, lnrpc.MacaroonPerms, error) { + + // We'll attempt to look up the config that we expect, according to our + // subServerName name. If we can't find this, then we'll exit with an + // error, as we're unable to properly initialize ourselves without this + // config. + subServerConf, ok := configRegistry.FetchConfig(subServerName) + if !ok { + return nil, nil, fmt.Errorf("unable to find config for "+ + "subserver type %s", subServerName) + } + + // Now that we've found an object mapping to our service name, we'll + // ensure that it's the type we need. + config, ok := subServerConf.(*Config) + if !ok { + return nil, nil, fmt.Errorf("wrong type of config for "+ + "subserver %s, expected %T got %T", subServerName, + &Config{}, subServerConf) + } + + return New(config) +} + +func init() { + subServer := &lnrpc.SubServerDriver{ + SubServerName: subServerName, + NewGrpcHandler: func() lnrpc.GrpcHandler { + return &ServerShell{} + }, + } + + // If the build tag is active, then we'll register ourselves as a + // sub-RPC server within the global lnrpc package namespace. + if err := lnrpc.RegisterSubServer(subServer); err != nil { + panic(fmt.Sprintf("failed to register sub server driver "+ + "'%s': %v", subServerName, err)) + } +} diff --git a/lnrpc/peersrpc/log.go b/lnrpc/peersrpc/log.go new file mode 100644 index 0000000000..13bc6c3548 --- /dev/null +++ b/lnrpc/peersrpc/log.go @@ -0,0 +1,48 @@ +package peersrpc + +import ( + "github.com/btcsuite/btclog" + "github.com/lightningnetwork/lnd/build" +) + +// log is a logger that is initialized with no output filters. This means the +// package will not perform any logging by default until the caller requests +// it. +var log btclog.Logger + +// Subsystem defines the logging code for this subsystem. +const Subsystem = "PRPC" + +// The default amount of logging is none. +func init() { + UseLogger(build.NewSubLogger(Subsystem, nil)) +} + +// DisableLog disables all library log output. Logging output is disabled by +// by default until UseLogger is called. +func DisableLog() { + UseLogger(btclog.Disabled) +} + +// UseLogger uses a specified Logger to output package logging info. This +// should be used in preference to SetLogWriter if the caller is also using +// btclog. +func UseLogger(logger btclog.Logger) { + log = logger +} + +// logClosure is used to provide a closure over expensive logging operations so +// don't have to be performed when the logging level doesn't warrant it. +type logClosure func() string // nolint:unused + +// String invokes the underlying function and returns the result. +func (c logClosure) String() string { + return c() +} + +// newLogClosure returns a new closure over a function that returns a string +// which itself provides a Stringer interface so that it can be used with the +// logging system. +func newLogClosure(c func() string) logClosure { // nolint:unused + return logClosure(c) +} diff --git a/lnrpc/peersrpc/peers_server.go b/lnrpc/peersrpc/peers_server.go new file mode 100644 index 0000000000..c8af26617f --- /dev/null +++ b/lnrpc/peersrpc/peers_server.go @@ -0,0 +1,142 @@ +//go:build peersrpc +// +build peersrpc + +package peersrpc + +import ( + "context" + "sync/atomic" + + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "github.com/lightningnetwork/lnd/lnrpc" + "google.golang.org/grpc" + "gopkg.in/macaroon-bakery.v2/bakery" +) + +const ( + // subServerName is the name of the sub rpc server. We'll use this name + // to register ourselves, and we also require that the main + // SubServerConfigDispatcher instance recognize tt as the name of our + // RPC service. + subServerName = "PeersRPC" +) + +var ( + // macPermissions maps RPC calls to the permissions they require. + macPermissions = map[string][]bakery.Op{} +) + +// ServerShell is a shell struct holding a reference to the actual sub-server. +// It is used to register the gRPC sub-server with the root server before we +// have the necessary dependencies to populate the actual sub-server. +type ServerShell struct { + PeersServer +} + +// Server is a sub-server of the main RPC server: the peers RPC. This sub +// RPC server allows to intereact with our Peers in the Lightning Network. +type Server struct { + started int32 // To be used atomically. + shutdown int32 // To be used atomically. + + // Required by the grpc-gateway/v2 library for forward compatibility. + // Must be after the atomically used variables to not break struct + // alignment. + UnimplementedPeersServer + + cfg *Config +} + +// A compile time check to ensure that Server fully implements the PeersServer +// gRPC service. +var _ PeersServer = (*Server)(nil) + +// New returns a new instance of the peersrpc Peers sub-server. We also +// return the set of permissions for the macaroons that we may create within +// this method. If the macaroons we need aren't found in the filepath, then +// we'll create them on start up. If we're unable to locate, or create the +// macaroons we need, then we'll return with an error. +func New(cfg *Config) (*Server, lnrpc.MacaroonPerms, error) { + server := &Server{ + cfg: cfg, + } + + return server, macPermissions, nil +} + +// Start launches any helper goroutines required for the Server to function. +// +// NOTE: This is part of the lnrpc.SubServer interface. +func (s *Server) Start() error { + if atomic.AddInt32(&s.started, 1) != 1 { + return nil + } + + return nil +} + +// Stop signals any active goroutines for a graceful closure. +// +// NOTE: This is part of the lnrpc.SubServer interface. +func (s *Server) Stop() error { + if atomic.AddInt32(&s.shutdown, 1) != 1 { + return nil + } + + return nil +} + +// Name returns a unique string representation of the sub-server. This can be +// used to identify the sub-server and also de-duplicate them. +// +// NOTE: This is part of the lnrpc.SubServer interface. +func (s *Server) Name() string { + return subServerName +} + +// RegisterWithRootServer will be called by the root gRPC server to direct a +// sub RPC server to register itself with the main gRPC root server. Until this +// is called, each sub-server won't be able to have +// requests routed towards it. +// +// NOTE: This is part of the lnrpc.GrpcHandler interface. +func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error { + // We make sure that we register it with the main gRPC server to ensure + // all our methods are routed properly. + RegisterPeersServer(grpcServer, r) + + log.Debugf("Peers RPC server successfully register with root " + + "gRPC server") + + return nil +} + +// RegisterWithRestServer will be called by the root REST mux to direct a sub +// RPC server to register itself with the main REST mux server. Until this is +// called, each sub-server won't be able to have requests routed towards it. +// +// NOTE: This is part of the lnrpc.GrpcHandler interface. +func (r *ServerShell) RegisterWithRestServer(ctx context.Context, + mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error { + + return nil +} + +// CreateSubServer populates the subserver's dependencies using the passed +// SubServerConfigDispatcher. This method should fully initialize the +// sub-server instance, making it ready for action. It returns the macaroon +// permissions that the sub-server wishes to pass on to the root server for all +// methods routed towards it. +// +// NOTE: This is part of the lnrpc.GrpcHandler interface. +func (r *ServerShell) CreateSubServer(configRegistry lnrpc.SubServerConfigDispatcher) ( + lnrpc.SubServer, lnrpc.MacaroonPerms, error) { + + subServer, macPermissions, err := createNewSubServer(configRegistry) + if err != nil { + return nil, nil, err + } + + r.PeersServer = subServer + return subServer, macPermissions, nil +} From d4021c5aca12b976940f729c2aae478912a0a39c Mon Sep 17 00:00:00 2001 From: positiveblue Date: Fri, 3 Dec 2021 13:15:37 -0800 Subject: [PATCH 03/11] lnrpc: fix `log.go` files The `log.go` file of each subserver is a copy and paste of the others. All of them have the same typo, an extra space in a comment, and the the definition of a function not used anywhere. --- lnrpc/autopilotrpc/log.go | 20 ++------------------ lnrpc/chainrpc/log.go | 18 +----------------- lnrpc/invoicesrpc/log.go | 4 ++-- lnrpc/peersrpc/log.go | 20 ++------------------ lnrpc/routerrpc/log.go | 18 +----------------- lnrpc/signrpc/log.go | 18 +----------------- lnrpc/verrpc/log.go | 2 +- lnrpc/walletrpc/log.go | 20 ++------------------ lnrpc/watchtowerrpc/log.go | 16 ---------------- 9 files changed, 12 insertions(+), 124 deletions(-) diff --git a/lnrpc/autopilotrpc/log.go b/lnrpc/autopilotrpc/log.go index e844bcab9d..113486d0d9 100644 --- a/lnrpc/autopilotrpc/log.go +++ b/lnrpc/autopilotrpc/log.go @@ -5,7 +5,7 @@ import ( "github.com/lightningnetwork/lnd/build" ) -// log is a logger that is initialized with no output filters. This means the +// log is a logger that is initialized with no output filters. This means the // package will not perform any logging by default until the caller requests // it. var log btclog.Logger @@ -21,25 +21,9 @@ func DisableLog() { UseLogger(btclog.Disabled) } -// UseLogger uses a specified Logger to output package logging info. This +// UseLogger uses a specified Logger to output package logging info. This // should be used in preference to SetLogWriter if the caller is also using // btclog. func UseLogger(logger btclog.Logger) { log = logger } - -// logClosure is used to provide a closure over expensive logging operations so -// don't have to be performed when the logging level doesn't warrant it. -type logClosure func() string // nolint:unused - -// String invokes the underlying function and returns the result. -func (c logClosure) String() string { - return c() -} - -// newLogClosure returns a new closure over a function that returns a string -// which itself provides a Stringer interface so that it can be used with the -// logging system. -func newLogClosure(c func() string) logClosure { // nolint:unused - return logClosure(c) -} diff --git a/lnrpc/chainrpc/log.go b/lnrpc/chainrpc/log.go index 0d47ea9710..6b68e324aa 100644 --- a/lnrpc/chainrpc/log.go +++ b/lnrpc/chainrpc/log.go @@ -5,7 +5,7 @@ import ( "github.com/lightningnetwork/lnd/build" ) -// log is a logger that is initialized with no output filters. This +// log is a logger that is initialized with no output filters. This // means the package will not perform any logging by default until the caller // requests it. var log btclog.Logger @@ -27,19 +27,3 @@ func DisableLog() { func UseLogger(logger btclog.Logger) { log = logger } - -// logClosure is used to provide a closure over expensive logging operations so -// don't have to be performed when the logging level doesn't warrant it. -type logClosure func() string // nolint:unused - -// String invokes the underlying function and returns the result. -func (c logClosure) String() string { - return c() -} - -// newLogClosure returns a new closure over a function that returns a string -// which itself provides a Stringer interface so that it can be used with the -// logging system. -func newLogClosure(c func() string) logClosure { // nolint:unused - return logClosure(c) -} diff --git a/lnrpc/invoicesrpc/log.go b/lnrpc/invoicesrpc/log.go index d29c737809..8429ee51cf 100644 --- a/lnrpc/invoicesrpc/log.go +++ b/lnrpc/invoicesrpc/log.go @@ -5,7 +5,7 @@ import ( "github.com/lightningnetwork/lnd/build" ) -// log is a logger that is initialized with no output filters. This means the +// log is a logger that is initialized with no output filters. This means the // package will not perform any logging by default until the caller requests // it. var log btclog.Logger @@ -21,7 +21,7 @@ func DisableLog() { UseLogger(btclog.Disabled) } -// UseLogger uses a specified Logger to output package logging info. This +// UseLogger uses a specified Logger to output package logging info. This // should be used in preference to SetLogWriter if the caller is also using // btclog. func UseLogger(logger btclog.Logger) { diff --git a/lnrpc/peersrpc/log.go b/lnrpc/peersrpc/log.go index 13bc6c3548..3fd59789cb 100644 --- a/lnrpc/peersrpc/log.go +++ b/lnrpc/peersrpc/log.go @@ -5,7 +5,7 @@ import ( "github.com/lightningnetwork/lnd/build" ) -// log is a logger that is initialized with no output filters. This means the +// log is a logger that is initialized with no output filters. This means the // package will not perform any logging by default until the caller requests // it. var log btclog.Logger @@ -24,25 +24,9 @@ func DisableLog() { UseLogger(btclog.Disabled) } -// UseLogger uses a specified Logger to output package logging info. This +// UseLogger uses a specified Logger to output package logging info. This // should be used in preference to SetLogWriter if the caller is also using // btclog. func UseLogger(logger btclog.Logger) { log = logger } - -// logClosure is used to provide a closure over expensive logging operations so -// don't have to be performed when the logging level doesn't warrant it. -type logClosure func() string // nolint:unused - -// String invokes the underlying function and returns the result. -func (c logClosure) String() string { - return c() -} - -// newLogClosure returns a new closure over a function that returns a string -// which itself provides a Stringer interface so that it can be used with the -// logging system. -func newLogClosure(c func() string) logClosure { // nolint:unused - return logClosure(c) -} diff --git a/lnrpc/routerrpc/log.go b/lnrpc/routerrpc/log.go index 6cbd8bea33..5c4ad01d83 100644 --- a/lnrpc/routerrpc/log.go +++ b/lnrpc/routerrpc/log.go @@ -5,7 +5,7 @@ import ( "github.com/lightningnetwork/lnd/build" ) -// log is a logger that is initialized with no output filters. This +// log is a logger that is initialized with no output filters. This // means the package will not perform any logging by default until the caller // requests it. var log btclog.Logger @@ -30,19 +30,3 @@ func DisableLog() { func UseLogger(logger btclog.Logger) { log = logger } - -// logClosure is used to provide a closure over expensive logging operations so -// don't have to be performed when the logging level doesn't warrant it. -type logClosure func() string // nolint:unused - -// String invokes the underlying function and returns the result. -func (c logClosure) String() string { - return c() -} - -// newLogClosure returns a new closure over a function that returns a string -// which itself provides a Stringer interface so that it can be used with the -// logging system. -func newLogClosure(c func() string) logClosure { // nolint:unused - return logClosure(c) -} diff --git a/lnrpc/signrpc/log.go b/lnrpc/signrpc/log.go index b5ff742a08..2dc6533435 100644 --- a/lnrpc/signrpc/log.go +++ b/lnrpc/signrpc/log.go @@ -5,7 +5,7 @@ import ( "github.com/lightningnetwork/lnd/build" ) -// log is a logger that is initialized with no output filters. This +// log is a logger that is initialized with no output filters. This // means the package will not perform any logging by default until the caller // requests it. var log btclog.Logger @@ -27,19 +27,3 @@ func DisableLog() { func UseLogger(logger btclog.Logger) { log = logger } - -// logClosure is used to provide a closure over expensive logging operations so -// don't have to be performed when the logging level doesn't warrant it. -type logClosure func() string // nolint:unused - -// String invokes the underlying function and returns the result. -func (c logClosure) String() string { - return c() -} - -// newLogClosure returns a new closure over a function that returns a string -// which itself provides a Stringer interface so that it can be used with the -// logging system. -func newLogClosure(c func() string) logClosure { // nolint:unused - return logClosure(c) -} diff --git a/lnrpc/verrpc/log.go b/lnrpc/verrpc/log.go index fb57daa212..3553b56806 100644 --- a/lnrpc/verrpc/log.go +++ b/lnrpc/verrpc/log.go @@ -5,7 +5,7 @@ import ( "github.com/lightningnetwork/lnd/build" ) -// log is a logger that is initialized with no output filters. This +// log is a logger that is initialized with no output filters. This // means the package will not perform any logging by default until the caller // requests it. var log btclog.Logger diff --git a/lnrpc/walletrpc/log.go b/lnrpc/walletrpc/log.go index 5c14879f6f..1916c549f2 100644 --- a/lnrpc/walletrpc/log.go +++ b/lnrpc/walletrpc/log.go @@ -5,7 +5,7 @@ import ( "github.com/lightningnetwork/lnd/build" ) -// log is a logger that is initialized with no output filters. This means the +// log is a logger that is initialized with no output filters. This means the // package will not perform any logging by default until the caller requests // it. var log btclog.Logger @@ -21,25 +21,9 @@ func DisableLog() { UseLogger(btclog.Disabled) } -// UseLogger uses a specified Logger to output package logging info. This +// UseLogger uses a specified Logger to output package logging info. This // should be used in preference to SetLogWriter if the caller is also using // btclog. func UseLogger(logger btclog.Logger) { log = logger } - -// logClosure is used to provide a closure over expensive logging operations so -// don't have to be performed when the logging level doesn't warrant it. -type logClosure func() string // nolint:unused - -// String invokes the underlying function and returns the result. -func (c logClosure) String() string { - return c() -} - -// newLogClosure returns a new closure over a function that returns a string -// which itself provides a Stringer interface so that it can be used with the -// logging system. -func newLogClosure(c func() string) logClosure { // nolint:unused - return logClosure(c) -} diff --git a/lnrpc/watchtowerrpc/log.go b/lnrpc/watchtowerrpc/log.go index 0ff4e68012..a71b7827fb 100644 --- a/lnrpc/watchtowerrpc/log.go +++ b/lnrpc/watchtowerrpc/log.go @@ -29,19 +29,3 @@ func DisableLog() { func UseLogger(logger btclog.Logger) { log = logger } - -// logClosure is used to provide a closure over expensive logging operations so -// don't have to be performed when the logging level doesn't warrant it. -type logClosure func() string // nolint:unused - -// String invokes the underlying function and returns the result. -func (c logClosure) String() string { - return c() -} - -// newLogClosure returns a new closure over a function that returns a string -// which itself provides a Stringer interface so that it can be used with the -// logging system. -func newLogClosure(c func() string) logClosure { // nolint:unused - return logClosure(c) -} From 5ab0cbd43304057c434197b55747796c73968b70 Mon Sep 17 00:00:00 2001 From: positiveblue Date: Sun, 2 Jan 2022 14:32:14 -0800 Subject: [PATCH 04/11] lnrpc: add `updatenodeannouncement` to `Peers` service New endpoint for the `Peers` subrpc. --- lnrpc/peersrpc/peers.pb.go | 515 +++++++++++++++++++++++++++++- lnrpc/peersrpc/peers.pb.json.go | 50 +++ lnrpc/peersrpc/peers.proto | 84 +++++ lnrpc/peersrpc/peers.swagger.json | 96 ++++++ lnrpc/peersrpc/peers_grpc.pb.go | 52 ++- 5 files changed, 781 insertions(+), 16 deletions(-) create mode 100644 lnrpc/peersrpc/peers.pb.json.go diff --git a/lnrpc/peersrpc/peers.pb.go b/lnrpc/peersrpc/peers.pb.go index 4139c8aea9..a0efe7974b 100644 --- a/lnrpc/peersrpc/peers.pb.go +++ b/lnrpc/peersrpc/peers.pb.go @@ -7,9 +7,11 @@ package peersrpc import ( + lnrpc "github.com/lightningnetwork/lnd/lnrpc" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" + sync "sync" ) const ( @@ -19,25 +21,460 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +// UpdateAction is used to determine the kind of action we are referring to. +type UpdateAction int32 + +const ( + // ADD indicates this is an "insertion" kind of action. + UpdateAction_ADD UpdateAction = 0 + // REMOVE indicates this is a "deletion" kind of action. + UpdateAction_REMOVE UpdateAction = 1 +) + +// Enum value maps for UpdateAction. +var ( + UpdateAction_name = map[int32]string{ + 0: "ADD", + 1: "REMOVE", + } + UpdateAction_value = map[string]int32{ + "ADD": 0, + "REMOVE": 1, + } +) + +func (x UpdateAction) Enum() *UpdateAction { + p := new(UpdateAction) + *p = x + return p +} + +func (x UpdateAction) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (UpdateAction) Descriptor() protoreflect.EnumDescriptor { + return file_peersrpc_peers_proto_enumTypes[0].Descriptor() +} + +func (UpdateAction) Type() protoreflect.EnumType { + return &file_peersrpc_peers_proto_enumTypes[0] +} + +func (x UpdateAction) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use UpdateAction.Descriptor instead. +func (UpdateAction) EnumDescriptor() ([]byte, []int) { + return file_peersrpc_peers_proto_rawDescGZIP(), []int{0} +} + +type FeatureSet int32 + +const ( + // + //SET_INIT identifies features that should be sent in an Init message to + //a remote peer. + FeatureSet_SET_INIT FeatureSet = 0 + // + //SET_LEGACY_GLOBAL identifies features that should be set in the legacy + //GlobalFeatures field of an Init message, which maintains backwards + //compatibility with nodes that haven't implemented flat features. + FeatureSet_SET_LEGACY_GLOBAL FeatureSet = 1 + // + //SET_NODE_ANN identifies features that should be advertised on node + //announcements. + FeatureSet_SET_NODE_ANN FeatureSet = 2 + // + //SET_INVOICE identifies features that should be advertised on invoices + //generated by the daemon. + FeatureSet_SET_INVOICE FeatureSet = 3 + // + //SET_INVOICE_AMP identifies the features that should be advertised on + //AMP invoices generated by the daemon. + FeatureSet_SET_INVOICE_AMP FeatureSet = 4 +) + +// Enum value maps for FeatureSet. +var ( + FeatureSet_name = map[int32]string{ + 0: "SET_INIT", + 1: "SET_LEGACY_GLOBAL", + 2: "SET_NODE_ANN", + 3: "SET_INVOICE", + 4: "SET_INVOICE_AMP", + } + FeatureSet_value = map[string]int32{ + "SET_INIT": 0, + "SET_LEGACY_GLOBAL": 1, + "SET_NODE_ANN": 2, + "SET_INVOICE": 3, + "SET_INVOICE_AMP": 4, + } +) + +func (x FeatureSet) Enum() *FeatureSet { + p := new(FeatureSet) + *p = x + return p +} + +func (x FeatureSet) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (FeatureSet) Descriptor() protoreflect.EnumDescriptor { + return file_peersrpc_peers_proto_enumTypes[1].Descriptor() +} + +func (FeatureSet) Type() protoreflect.EnumType { + return &file_peersrpc_peers_proto_enumTypes[1] +} + +func (x FeatureSet) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use FeatureSet.Descriptor instead. +func (FeatureSet) EnumDescriptor() ([]byte, []int) { + return file_peersrpc_peers_proto_rawDescGZIP(), []int{1} +} + +type UpdateAddressAction struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Determines the kind of action. + Action UpdateAction `protobuf:"varint,1,opt,name=action,proto3,enum=peersrpc.UpdateAction" json:"action,omitempty"` + // The address used to apply the update action. + Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"` +} + +func (x *UpdateAddressAction) Reset() { + *x = UpdateAddressAction{} + if protoimpl.UnsafeEnabled { + mi := &file_peersrpc_peers_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateAddressAction) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateAddressAction) ProtoMessage() {} + +func (x *UpdateAddressAction) ProtoReflect() protoreflect.Message { + mi := &file_peersrpc_peers_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateAddressAction.ProtoReflect.Descriptor instead. +func (*UpdateAddressAction) Descriptor() ([]byte, []int) { + return file_peersrpc_peers_proto_rawDescGZIP(), []int{0} +} + +func (x *UpdateAddressAction) GetAction() UpdateAction { + if x != nil { + return x.Action + } + return UpdateAction_ADD +} + +func (x *UpdateAddressAction) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + +type UpdateFeatureAction struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Determines the kind of action. + Action UpdateAction `protobuf:"varint,1,opt,name=action,proto3,enum=peersrpc.UpdateAction" json:"action,omitempty"` + // The feature bit used to apply the update action. + FeatureBit lnrpc.FeatureBit `protobuf:"varint,2,opt,name=feature_bit,json=featureBit,proto3,enum=lnrpc.FeatureBit" json:"feature_bit,omitempty"` +} + +func (x *UpdateFeatureAction) Reset() { + *x = UpdateFeatureAction{} + if protoimpl.UnsafeEnabled { + mi := &file_peersrpc_peers_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateFeatureAction) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateFeatureAction) ProtoMessage() {} + +func (x *UpdateFeatureAction) ProtoReflect() protoreflect.Message { + mi := &file_peersrpc_peers_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateFeatureAction.ProtoReflect.Descriptor instead. +func (*UpdateFeatureAction) Descriptor() ([]byte, []int) { + return file_peersrpc_peers_proto_rawDescGZIP(), []int{1} +} + +func (x *UpdateFeatureAction) GetAction() UpdateAction { + if x != nil { + return x.Action + } + return UpdateAction_ADD +} + +func (x *UpdateFeatureAction) GetFeatureBit() lnrpc.FeatureBit { + if x != nil { + return x.FeatureBit + } + return lnrpc.FeatureBit_DATALOSS_PROTECT_REQ +} + +type NodeAnnouncementUpdateRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Set of changes for the features that the node supports. + FeatureUpdates []*UpdateFeatureAction `protobuf:"bytes,1,rep,name=feature_updates,json=featureUpdates,proto3" json:"feature_updates,omitempty"` + // Color is the node's color in hex code format. + Color string `protobuf:"bytes,2,opt,name=color,proto3" json:"color,omitempty"` + // Alias or nick name of the node. + Alias string `protobuf:"bytes,3,opt,name=alias,proto3" json:"alias,omitempty"` + // Set of changes for the node's known addresses. + AddressUpdates []*UpdateAddressAction `protobuf:"bytes,4,rep,name=address_updates,json=addressUpdates,proto3" json:"address_updates,omitempty"` +} + +func (x *NodeAnnouncementUpdateRequest) Reset() { + *x = NodeAnnouncementUpdateRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_peersrpc_peers_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NodeAnnouncementUpdateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NodeAnnouncementUpdateRequest) ProtoMessage() {} + +func (x *NodeAnnouncementUpdateRequest) ProtoReflect() protoreflect.Message { + mi := &file_peersrpc_peers_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NodeAnnouncementUpdateRequest.ProtoReflect.Descriptor instead. +func (*NodeAnnouncementUpdateRequest) Descriptor() ([]byte, []int) { + return file_peersrpc_peers_proto_rawDescGZIP(), []int{2} +} + +func (x *NodeAnnouncementUpdateRequest) GetFeatureUpdates() []*UpdateFeatureAction { + if x != nil { + return x.FeatureUpdates + } + return nil +} + +func (x *NodeAnnouncementUpdateRequest) GetColor() string { + if x != nil { + return x.Color + } + return "" +} + +func (x *NodeAnnouncementUpdateRequest) GetAlias() string { + if x != nil { + return x.Alias + } + return "" +} + +func (x *NodeAnnouncementUpdateRequest) GetAddressUpdates() []*UpdateAddressAction { + if x != nil { + return x.AddressUpdates + } + return nil +} + +type NodeAnnouncementUpdateResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Ops []*lnrpc.Op `protobuf:"bytes,1,rep,name=ops,proto3" json:"ops,omitempty"` +} + +func (x *NodeAnnouncementUpdateResponse) Reset() { + *x = NodeAnnouncementUpdateResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_peersrpc_peers_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NodeAnnouncementUpdateResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NodeAnnouncementUpdateResponse) ProtoMessage() {} + +func (x *NodeAnnouncementUpdateResponse) ProtoReflect() protoreflect.Message { + mi := &file_peersrpc_peers_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NodeAnnouncementUpdateResponse.ProtoReflect.Descriptor instead. +func (*NodeAnnouncementUpdateResponse) Descriptor() ([]byte, []int) { + return file_peersrpc_peers_proto_rawDescGZIP(), []int{3} +} + +func (x *NodeAnnouncementUpdateResponse) GetOps() []*lnrpc.Op { + if x != nil { + return x.Ops + } + return nil +} + var File_peersrpc_peers_proto protoreflect.FileDescriptor var file_peersrpc_peers_proto_rawDesc = []byte{ 0x0a, 0x14, 0x70, 0x65, 0x65, 0x72, 0x73, 0x72, 0x70, 0x63, 0x2f, 0x70, 0x65, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x70, 0x65, 0x65, 0x72, 0x73, 0x72, 0x70, 0x63, - 0x32, 0x07, 0x0a, 0x05, 0x50, 0x65, 0x65, 0x72, 0x73, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, - 0x67, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x6c, 0x6e, 0x64, 0x2f, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2f, 0x70, 0x65, 0x65, 0x72, 0x73, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x1a, 0x0f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x22, 0x5f, 0x0a, 0x13, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x73, + 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x22, 0x79, 0x0a, 0x13, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x46, 0x65, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x06, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x70, 0x65, 0x65, 0x72, + 0x73, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x32, 0x0a, 0x0b, 0x66, 0x65, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x5f, 0x62, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x42, 0x69, + 0x74, 0x52, 0x0a, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x42, 0x69, 0x74, 0x22, 0xdb, 0x01, + 0x0a, 0x1d, 0x4e, 0x6f, 0x64, 0x65, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x46, 0x0a, 0x0f, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x73, + 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, + 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x6c, 0x6f, 0x72, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x12, 0x14, 0x0a, + 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x6c, + 0x69, 0x61, 0x73, 0x12, 0x46, 0x0a, 0x0f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, + 0x65, 0x65, 0x72, 0x73, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x22, 0x3d, 0x0a, 0x1e, 0x4e, + 0x6f, 0x64, 0x65, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, + 0x03, 0x6f, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x4f, 0x70, 0x52, 0x03, 0x6f, 0x70, 0x73, 0x2a, 0x23, 0x0a, 0x0c, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x44, + 0x44, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x52, 0x45, 0x4d, 0x4f, 0x56, 0x45, 0x10, 0x01, 0x2a, + 0x69, 0x0a, 0x0a, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x12, 0x0c, 0x0a, + 0x08, 0x53, 0x45, 0x54, 0x5f, 0x49, 0x4e, 0x49, 0x54, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x53, + 0x45, 0x54, 0x5f, 0x4c, 0x45, 0x47, 0x41, 0x43, 0x59, 0x5f, 0x47, 0x4c, 0x4f, 0x42, 0x41, 0x4c, + 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x53, 0x45, 0x54, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x41, + 0x4e, 0x4e, 0x10, 0x02, 0x12, 0x0f, 0x0a, 0x0b, 0x53, 0x45, 0x54, 0x5f, 0x49, 0x4e, 0x56, 0x4f, + 0x49, 0x43, 0x45, 0x10, 0x03, 0x12, 0x13, 0x0a, 0x0f, 0x53, 0x45, 0x54, 0x5f, 0x49, 0x4e, 0x56, + 0x4f, 0x49, 0x43, 0x45, 0x5f, 0x41, 0x4d, 0x50, 0x10, 0x04, 0x32, 0x74, 0x0a, 0x05, 0x50, 0x65, + 0x65, 0x72, 0x73, 0x12, 0x6b, 0x0a, 0x16, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4e, 0x6f, 0x64, + 0x65, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x27, 0x2e, + 0x70, 0x65, 0x65, 0x72, 0x73, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x41, 0x6e, 0x6e, + 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x73, 0x72, 0x70, + 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, + 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, + 0x6c, 0x6e, 0x64, 0x2f, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2f, 0x70, 0x65, 0x65, 0x72, 0x73, 0x72, + 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_peersrpc_peers_proto_rawDescOnce sync.Once + file_peersrpc_peers_proto_rawDescData = file_peersrpc_peers_proto_rawDesc +) + +func file_peersrpc_peers_proto_rawDescGZIP() []byte { + file_peersrpc_peers_proto_rawDescOnce.Do(func() { + file_peersrpc_peers_proto_rawDescData = protoimpl.X.CompressGZIP(file_peersrpc_peers_proto_rawDescData) + }) + return file_peersrpc_peers_proto_rawDescData } -var file_peersrpc_peers_proto_goTypes = []interface{}{} +var file_peersrpc_peers_proto_enumTypes = make([]protoimpl.EnumInfo, 2) +var file_peersrpc_peers_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_peersrpc_peers_proto_goTypes = []interface{}{ + (UpdateAction)(0), // 0: peersrpc.UpdateAction + (FeatureSet)(0), // 1: peersrpc.FeatureSet + (*UpdateAddressAction)(nil), // 2: peersrpc.UpdateAddressAction + (*UpdateFeatureAction)(nil), // 3: peersrpc.UpdateFeatureAction + (*NodeAnnouncementUpdateRequest)(nil), // 4: peersrpc.NodeAnnouncementUpdateRequest + (*NodeAnnouncementUpdateResponse)(nil), // 5: peersrpc.NodeAnnouncementUpdateResponse + (lnrpc.FeatureBit)(0), // 6: lnrpc.FeatureBit + (*lnrpc.Op)(nil), // 7: lnrpc.Op +} var file_peersrpc_peers_proto_depIdxs = []int32{ - 0, // [0:0] is the sub-list for method output_type - 0, // [0:0] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name + 0, // 0: peersrpc.UpdateAddressAction.action:type_name -> peersrpc.UpdateAction + 0, // 1: peersrpc.UpdateFeatureAction.action:type_name -> peersrpc.UpdateAction + 6, // 2: peersrpc.UpdateFeatureAction.feature_bit:type_name -> lnrpc.FeatureBit + 3, // 3: peersrpc.NodeAnnouncementUpdateRequest.feature_updates:type_name -> peersrpc.UpdateFeatureAction + 2, // 4: peersrpc.NodeAnnouncementUpdateRequest.address_updates:type_name -> peersrpc.UpdateAddressAction + 7, // 5: peersrpc.NodeAnnouncementUpdateResponse.ops:type_name -> lnrpc.Op + 4, // 6: peersrpc.Peers.UpdateNodeAnnouncement:input_type -> peersrpc.NodeAnnouncementUpdateRequest + 5, // 7: peersrpc.Peers.UpdateNodeAnnouncement:output_type -> peersrpc.NodeAnnouncementUpdateResponse + 7, // [7:8] is the sub-list for method output_type + 6, // [6:7] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name } func init() { file_peersrpc_peers_proto_init() } @@ -45,18 +482,70 @@ func file_peersrpc_peers_proto_init() { if File_peersrpc_peers_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_peersrpc_peers_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateAddressAction); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_peersrpc_peers_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateFeatureAction); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_peersrpc_peers_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NodeAnnouncementUpdateRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_peersrpc_peers_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NodeAnnouncementUpdateResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_peersrpc_peers_proto_rawDesc, - NumEnums: 0, - NumMessages: 0, + NumEnums: 2, + NumMessages: 4, NumExtensions: 0, NumServices: 1, }, GoTypes: file_peersrpc_peers_proto_goTypes, DependencyIndexes: file_peersrpc_peers_proto_depIdxs, + EnumInfos: file_peersrpc_peers_proto_enumTypes, + MessageInfos: file_peersrpc_peers_proto_msgTypes, }.Build() File_peersrpc_peers_proto = out.File file_peersrpc_peers_proto_rawDesc = nil diff --git a/lnrpc/peersrpc/peers.pb.json.go b/lnrpc/peersrpc/peers.pb.json.go new file mode 100644 index 0000000000..8efa8585ae --- /dev/null +++ b/lnrpc/peersrpc/peers.pb.json.go @@ -0,0 +1,50 @@ +// Code generated by falafel 0.9.1. DO NOT EDIT. +// source: peers.proto + +// +build js + +package peersrpc + +import ( + "context" + + gateway "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "google.golang.org/grpc" + "google.golang.org/protobuf/encoding/protojson" +) + +func RegisterPeersJSONCallbacks(registry map[string]func(ctx context.Context, + conn *grpc.ClientConn, reqJSON string, callback func(string, error))) { + + marshaler := &gateway.JSONPb{ + MarshalOptions: protojson.MarshalOptions{ + UseProtoNames: true, + EmitUnpopulated: true, + }, + } + + registry["peersrpc.Peers.UpdateNodeAnnouncement"] = func(ctx context.Context, + conn *grpc.ClientConn, reqJSON string, callback func(string, error)) { + + req := &NodeAnnouncementUpdateRequest{} + err := marshaler.Unmarshal([]byte(reqJSON), req) + if err != nil { + callback("", err) + return + } + + client := NewPeersClient(conn) + resp, err := client.UpdateNodeAnnouncement(ctx, req) + if err != nil { + callback("", err) + return + } + + respBytes, err := marshaler.Marshal(resp) + if err != nil { + callback("", err) + return + } + callback(string(respBytes), nil) + } +} diff --git a/lnrpc/peersrpc/peers.proto b/lnrpc/peersrpc/peers.proto index a6c74634ea..7af9f0a7f3 100644 --- a/lnrpc/peersrpc/peers.proto +++ b/lnrpc/peersrpc/peers.proto @@ -1,5 +1,7 @@ syntax = "proto3"; +import "lightning.proto"; + package peersrpc; option go_package = "github.com/lightningnetwork/lnd/lnrpc/peersrpc"; @@ -7,4 +9,86 @@ option go_package = "github.com/lightningnetwork/lnd/lnrpc/peersrpc"; // Peers is a service that can be used to get information and interact // with the other nodes of the newtwork. service Peers { + /* lncli: peers updatenodeannouncement + UpdateNodeAnnouncement allows the caller to update the node parameters + and broadcasts a new version of the node announcement to its peers. + */ + rpc UpdateNodeAnnouncement (NodeAnnouncementUpdateRequest) + returns (NodeAnnouncementUpdateResponse); +} + +// UpdateAction is used to determine the kind of action we are referring to. +enum UpdateAction { + // ADD indicates this is an "insertion" kind of action. + ADD = 0; + + // REMOVE indicates this is a "deletion" kind of action. + REMOVE = 1; +} + +enum FeatureSet { + /* + SET_INIT identifies features that should be sent in an Init message to + a remote peer. + */ + SET_INIT = 0; + + /* + SET_LEGACY_GLOBAL identifies features that should be set in the legacy + GlobalFeatures field of an Init message, which maintains backwards + compatibility with nodes that haven't implemented flat features. + */ + SET_LEGACY_GLOBAL = 1; + + /* + SET_NODE_ANN identifies features that should be advertised on node + announcements. + */ + SET_NODE_ANN = 2; + + /* + SET_INVOICE identifies features that should be advertised on invoices + generated by the daemon. + */ + SET_INVOICE = 3; + + /* + SET_INVOICE_AMP identifies the features that should be advertised on + AMP invoices generated by the daemon. + */ + SET_INVOICE_AMP = 4; +} + +message UpdateAddressAction { + // Determines the kind of action. + UpdateAction action = 1; + + // The address used to apply the update action. + string address = 2; +} + +message UpdateFeatureAction { + // Determines the kind of action. + UpdateAction action = 1; + + // The feature bit used to apply the update action. + lnrpc.FeatureBit feature_bit = 2; +} + +message NodeAnnouncementUpdateRequest { + // Set of changes for the features that the node supports. + repeated UpdateFeatureAction feature_updates = 1; + + // Color is the node's color in hex code format. + string color = 2; + + // Alias or nick name of the node. + string alias = 3; + + // Set of changes for the node's known addresses. + repeated UpdateAddressAction address_updates = 4; +} + +message NodeAnnouncementUpdateResponse { + repeated lnrpc.Op ops = 1; } diff --git a/lnrpc/peersrpc/peers.swagger.json b/lnrpc/peersrpc/peers.swagger.json index 607f7b2c72..dd896cad37 100644 --- a/lnrpc/peersrpc/peers.swagger.json +++ b/lnrpc/peersrpc/peers.swagger.json @@ -4,6 +4,11 @@ "title": "peersrpc/peers.proto", "version": "version not set" }, + "tags": [ + { + "name": "Peers" + } + ], "consumes": [ "application/json" ], @@ -12,6 +17,97 @@ ], "paths": {}, "definitions": { + "lnrpcFeatureBit": { + "type": "string", + "enum": [ + "DATALOSS_PROTECT_REQ", + "DATALOSS_PROTECT_OPT", + "INITIAL_ROUING_SYNC", + "UPFRONT_SHUTDOWN_SCRIPT_REQ", + "UPFRONT_SHUTDOWN_SCRIPT_OPT", + "GOSSIP_QUERIES_REQ", + "GOSSIP_QUERIES_OPT", + "TLV_ONION_REQ", + "TLV_ONION_OPT", + "EXT_GOSSIP_QUERIES_REQ", + "EXT_GOSSIP_QUERIES_OPT", + "STATIC_REMOTE_KEY_REQ", + "STATIC_REMOTE_KEY_OPT", + "PAYMENT_ADDR_REQ", + "PAYMENT_ADDR_OPT", + "MPP_REQ", + "MPP_OPT", + "WUMBO_CHANNELS_REQ", + "WUMBO_CHANNELS_OPT", + "ANCHORS_REQ", + "ANCHORS_OPT", + "ANCHORS_ZERO_FEE_HTLC_REQ", + "ANCHORS_ZERO_FEE_HTLC_OPT", + "AMP_REQ", + "AMP_OPT" + ], + "default": "DATALOSS_PROTECT_REQ" + }, + "lnrpcOp": { + "type": "object", + "properties": { + "entity": { + "type": "string" + }, + "actions": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "peersrpcNodeAnnouncementUpdateResponse": { + "type": "object", + "properties": { + "ops": { + "type": "array", + "items": { + "$ref": "#/definitions/lnrpcOp" + } + } + } + }, + "peersrpcUpdateAction": { + "type": "string", + "enum": [ + "ADD", + "REMOVE" + ], + "default": "ADD", + "description": "UpdateAction is used to determine the kind of action we are referring to.\n\n - ADD: ADD indicates this is an \"insertion\" kind of action.\n - REMOVE: REMOVE indicates this is a \"deletion\" kind of action." + }, + "peersrpcUpdateAddressAction": { + "type": "object", + "properties": { + "action": { + "$ref": "#/definitions/peersrpcUpdateAction", + "description": "Determines the kind of action." + }, + "address": { + "type": "string", + "description": "The address used to apply the update action." + } + } + }, + "peersrpcUpdateFeatureAction": { + "type": "object", + "properties": { + "action": { + "$ref": "#/definitions/peersrpcUpdateAction", + "description": "Determines the kind of action." + }, + "feature_bit": { + "$ref": "#/definitions/lnrpcFeatureBit", + "description": "The feature bit used to apply the update action." + } + } + }, "protobufAny": { "type": "object", "properties": { diff --git a/lnrpc/peersrpc/peers_grpc.pb.go b/lnrpc/peersrpc/peers_grpc.pb.go index 243997a5da..437fe45b28 100644 --- a/lnrpc/peersrpc/peers_grpc.pb.go +++ b/lnrpc/peersrpc/peers_grpc.pb.go @@ -3,7 +3,10 @@ package peersrpc import ( + context "context" grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" ) // This is a compile-time assertion to ensure that this generated file @@ -15,6 +18,10 @@ const _ = grpc.SupportPackageIsVersion7 // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type PeersClient interface { + // lncli: peers updatenodeannouncement + //UpdateNodeAnnouncement allows the caller to update the node parameters + //and broadcasts a new version of the node announcement to its peers. + UpdateNodeAnnouncement(ctx context.Context, in *NodeAnnouncementUpdateRequest, opts ...grpc.CallOption) (*NodeAnnouncementUpdateResponse, error) } type peersClient struct { @@ -25,10 +32,23 @@ func NewPeersClient(cc grpc.ClientConnInterface) PeersClient { return &peersClient{cc} } +func (c *peersClient) UpdateNodeAnnouncement(ctx context.Context, in *NodeAnnouncementUpdateRequest, opts ...grpc.CallOption) (*NodeAnnouncementUpdateResponse, error) { + out := new(NodeAnnouncementUpdateResponse) + err := c.cc.Invoke(ctx, "/peersrpc.Peers/UpdateNodeAnnouncement", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // PeersServer is the server API for Peers service. // All implementations must embed UnimplementedPeersServer // for forward compatibility type PeersServer interface { + // lncli: peers updatenodeannouncement + //UpdateNodeAnnouncement allows the caller to update the node parameters + //and broadcasts a new version of the node announcement to its peers. + UpdateNodeAnnouncement(context.Context, *NodeAnnouncementUpdateRequest) (*NodeAnnouncementUpdateResponse, error) mustEmbedUnimplementedPeersServer() } @@ -36,6 +56,9 @@ type PeersServer interface { type UnimplementedPeersServer struct { } +func (UnimplementedPeersServer) UpdateNodeAnnouncement(context.Context, *NodeAnnouncementUpdateRequest) (*NodeAnnouncementUpdateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateNodeAnnouncement not implemented") +} func (UnimplementedPeersServer) mustEmbedUnimplementedPeersServer() {} // UnsafePeersServer may be embedded to opt out of forward compatibility for this service. @@ -49,13 +72,36 @@ func RegisterPeersServer(s grpc.ServiceRegistrar, srv PeersServer) { s.RegisterService(&Peers_ServiceDesc, srv) } +func _Peers_UpdateNodeAnnouncement_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(NodeAnnouncementUpdateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(PeersServer).UpdateNodeAnnouncement(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/peersrpc.Peers/UpdateNodeAnnouncement", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(PeersServer).UpdateNodeAnnouncement(ctx, req.(*NodeAnnouncementUpdateRequest)) + } + return interceptor(ctx, in, info, handler) +} + // Peers_ServiceDesc is the grpc.ServiceDesc for Peers service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var Peers_ServiceDesc = grpc.ServiceDesc{ ServiceName: "peersrpc.Peers", HandlerType: (*PeersServer)(nil), - Methods: []grpc.MethodDesc{}, - Streams: []grpc.StreamDesc{}, - Metadata: "peersrpc/peers.proto", + Methods: []grpc.MethodDesc{ + { + MethodName: "UpdateNodeAnnouncement", + Handler: _Peers_UpdateNodeAnnouncement_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "peersrpc/peers.proto", } From e4e093581697a0c1e0f94c9f54a7f008ad0df5c8 Mon Sep 17 00:00:00 2001 From: positiveblue Date: Sun, 5 Dec 2021 20:24:06 -0800 Subject: [PATCH 05/11] 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 3949f123e4..6c1fc19083 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 b2f5dfbd0b..91a42a299f 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 2282c3c7fd..df77303e72 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 f661875fcf..860e7627e1 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 0000000000..f45a8db693 --- /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 dd896cad37..c7b9970cf6 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 c4ad3476d6..b1b9544f18 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 c8af26617f..5cfd7e589c 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 f3e7b34962..477b973078 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 197461bd3d..b0643ad9ca 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 28a3a9b6a7..fe6dc0a2a7 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 ffbd0de50c..64156e2fc2 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 2c1db19a32..a5e7f61085 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 cad28daec2..002ea4b174 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 6a08b342d0..6c768d0f94 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 4accc6e975..ad08fe4b0b 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 e11993f10a..d9473edfd4 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) From ce4813940db90d585da0053bde429b1dd969ea6f Mon Sep 17 00:00:00 2001 From: positiveblue Date: Sat, 8 Jan 2022 12:16:42 -0800 Subject: [PATCH 06/11] lnrpc/peers: handle alias changes in updateNodeAnnouncement --- lnrpc/peersrpc/peers_server.go | 22 ++++- lntest/itest/assertions.go | 31 +++++++ lntest/itest/lnd_channel_graph_test.go | 110 ++++++++++++++++++++++++- netann/node_announcement.go | 8 ++ 4 files changed, 165 insertions(+), 6 deletions(-) diff --git a/lnrpc/peersrpc/peers_server.go b/lnrpc/peersrpc/peers_server.go index 5cfd7e589c..9ca6f437a9 100644 --- a/lnrpc/peersrpc/peers_server.go +++ b/lnrpc/peersrpc/peers_server.go @@ -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 diff --git a/lntest/itest/assertions.go b/lntest/itest/assertions.go index 248d3bcad4..2110d6e9ee 100644 --- a/lntest/itest/assertions.go +++ b/lntest/itest/assertions.go @@ -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) + } + } +} diff --git a/lntest/itest/lnd_channel_graph_test.go b/lntest/itest/lnd_channel_graph_test.go index b0643ad9ca..9910c62d13 100644 --- a/lntest/itest/lnd_channel_graph_test.go +++ b/lntest/itest/lnd_channel_graph_test.go @@ -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) } diff --git a/netann/node_announcement.go b/netann/node_announcement.go index d73b6692f2..df13ca1b62 100644 --- a/netann/node_announcement.go +++ b/netann/node_announcement.go @@ -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) { From aacb2565f593ca36c9f47cc78db7a589119d6ee7 Mon Sep 17 00:00:00 2001 From: positiveblue Date: Thu, 20 Jan 2022 15:01:44 -0800 Subject: [PATCH 07/11] lnrpc/peers: handle color changes in updateNodeAnnouncement --- lnrpc/peersrpc/peers_server.go | 21 ++++++++++++++++++++- lntest/itest/assertions.go | 3 +++ lntest/itest/lnd_channel_graph_test.go | 5 +++++ netann/node_announcement.go | 9 +++++++++ 4 files changed, 37 insertions(+), 1 deletion(-) diff --git a/lnrpc/peersrpc/peers_server.go b/lnrpc/peersrpc/peers_server.go index 9ca6f437a9..aae96bc1b3 100644 --- a/lnrpc/peersrpc/peers_server.go +++ b/lnrpc/peersrpc/peers_server.go @@ -9,6 +9,7 @@ import ( "sync/atomic" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "github.com/lightningnetwork/lnd/lncfg" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/netann" @@ -177,7 +178,25 @@ func (s *Server) UpdateNodeAnnouncement(_ context.Context, // TODO(positiveblue): apply feature bit modifications - // TODO(positiveblue): apply color modifications + if req.Color != "" { + color, err := lncfg.ParseHexColor(req.Color) + if err != nil { + return nil, fmt.Errorf("unable to parse color: %v", err) + } + + if color != currentNodeAnn.RGBColor { + resp.Ops = append(resp.Ops, &lnrpc.Op{ + Entity: "color", + Actions: []string{ + fmt.Sprintf("changed to %v", color), + }, + }) + nodeModifiers = append( + nodeModifiers, + netann.NodeAnnSetColor(color), + ) + } + } if req.Alias != "" { alias, err := lnwire.NewNodeAlias(req.Alias) diff --git a/lntest/itest/assertions.go b/lntest/itest/assertions.go index 2110d6e9ee..01fe6b2116 100644 --- a/lntest/itest/assertions.go +++ b/lntest/itest/assertions.go @@ -1862,6 +1862,9 @@ func assertAnchorOutputLost(t *harnessTest, node *lntest.HarnessNode, func assertNodeAnnouncement(t *harnessTest, n1, n2 *lnrpc.NodeUpdate) { // Alias should match. require.Equal(t.t, n1.Alias, n2.Alias, "alias don't match") + + // Color should match. + require.Equal(t.t, n1.Color, n2.Color, "color don't match") } // assertUpdateNodeAnnouncementResponse is a helper function to assert diff --git a/lntest/itest/lnd_channel_graph_test.go b/lntest/itest/lnd_channel_graph_test.go index 9910c62d13..7c19883e06 100644 --- a/lntest/itest/lnd_channel_graph_test.go +++ b/lntest/itest/lnd_channel_graph_test.go @@ -821,6 +821,7 @@ func testUpdateNodeAnnouncement(net *lntest.NetworkHarness, t *harnessTest) { defaultDaveNodeAnn := &lnrpc.NodeUpdate{ Alias: resp.Alias, + Color: resp.Color, } // Dave must have an open channel before he can send a node @@ -866,15 +867,18 @@ func testUpdateNodeAnnouncement(net *lntest.NetworkHarness, t *harnessTest) { // Update Node. newAlias := "new-alias" + newColor := "#2288ee" nodeAnnReq := &peersrpc.NodeAnnouncementUpdateRequest{ Alias: newAlias, + Color: newColor, } response, err := dave.UpdateNodeAnnouncement(ctxt, nodeAnnReq) require.NoError(t.t, err, "unable to update dave's node announcement") expectedOps := map[string]int{ + "color": 1, "alias": 1, } assertUpdateNodeAnnouncementResponse(t, response, expectedOps) @@ -883,6 +887,7 @@ func testUpdateNodeAnnouncement(net *lntest.NetworkHarness, t *harnessTest) { // the requested color, requested alias and the new added addresses. newDaveNodeAnn := &lnrpc.NodeUpdate{ Alias: newAlias, + Color: newColor, } // We'll then wait for Alice to receive dave's node announcement diff --git a/netann/node_announcement.go b/netann/node_announcement.go index df13ca1b62..48dc8411e0 100644 --- a/netann/node_announcement.go +++ b/netann/node_announcement.go @@ -1,6 +1,7 @@ package netann import ( + "image/color" "net" "time" @@ -29,6 +30,14 @@ func NodeAnnSetAddrs(addrs []net.Addr) func(*lnwire.NodeAnnouncement) { } } +// NodeAnnSetColor is a functional option that sets the color of the +// given node announcment. +func NodeAnnSetColor(newColor color.RGBA) func(*lnwire.NodeAnnouncement) { + return func(nodeAnn *lnwire.NodeAnnouncement) { + nodeAnn.RGBColor = newColor + } +} + // NodeAnnSetTimestamp is a functional option that sets the timestamp of the // announcement to the current time, or increments it if the timestamp is // already in the future. From 76196db70ef32e3fdf2e1df42eef2e8fb06abdd6 Mon Sep 17 00:00:00 2001 From: positiveblue Date: Thu, 20 Jan 2022 15:04:04 -0800 Subject: [PATCH 08/11] lnrpc/peers: handle net address changes in updateNodeAnnouncement --- lnrpc/peersrpc/peers_server.go | 96 +++++++++++++++++++++++++- lntest/itest/assertions.go | 18 +++++ lntest/itest/lnd_channel_graph_test.go | 71 ++++++++++++++++--- netann/node_announcement.go | 4 +- 4 files changed, 178 insertions(+), 11 deletions(-) diff --git a/lnrpc/peersrpc/peers_server.go b/lnrpc/peersrpc/peers_server.go index aae96bc1b3..16cc20af6a 100644 --- a/lnrpc/peersrpc/peers_server.go +++ b/lnrpc/peersrpc/peers_server.go @@ -6,6 +6,7 @@ package peersrpc import ( "context" "fmt" + "net" "sync/atomic" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" @@ -161,6 +162,85 @@ func (r *ServerShell) CreateSubServer(configRegistry lnrpc.SubServerConfigDispat return subServer, macPermissions, nil } +// updateAddresses computes the new address set after executing the update +// actions. +func (s *Server) updateAddresses(currentAddresses []net.Addr, + updates []*UpdateAddressAction) ([]net.Addr, *lnrpc.Op, error) { + + // net.Addr is not comparable so we cannot use the default map + // (map[net.Addr]struct{}) so we have to use arrays and a helping + // function. + findAddr := func(addr net.Addr, slice []net.Addr) bool { + for _, sAddr := range slice { + if sAddr.Network() != addr.Network() { + continue + } + + if sAddr.String() == addr.String() { + return true + } + } + return false + } + + // Preallocate enough memory for both arrays. + removeAddr := make([]net.Addr, 0, len(updates)) + addAddr := make([]net.Addr, 0, len(updates)) + for _, update := range updates { + addr, err := s.cfg.ParseAddr(update.Address) + if err != nil { + return nil, nil, fmt.Errorf("unable to resolve "+ + "address %v: %v", update.Address, err) + } + + switch update.Action { + case UpdateAction_ADD: + addAddr = append(addAddr, addr) + case UpdateAction_REMOVE: + removeAddr = append(removeAddr, addr) + default: + return nil, nil, fmt.Errorf("invalid address update "+ + "action: %v", update.Action) + } + } + + // Look for any inconsistency trying to add AND remove the same address. + for _, addr := range removeAddr { + if findAddr(addr, addAddr) { + return nil, nil, fmt.Errorf("invalid updates for "+ + "removing AND adding %v", addr) + } + } + + ops := &lnrpc.Op{Entity: "addresses"} + newAddrs := make([]net.Addr, 0, len(updates)+len(currentAddresses)) + + // Copy current addresses excluding the ones that need to be removed. + for _, addr := range currentAddresses { + if findAddr(addr, removeAddr) { + ops.Actions = append( + ops.Actions, + fmt.Sprintf("%s removed", addr.String()), + ) + continue + } + newAddrs = append(newAddrs, addr) + } + + // Add new adresses if needed. + for _, addr := range addAddr { + if !findAddr(addr, newAddrs) { + ops.Actions = append( + ops.Actions, + fmt.Sprintf("%s added", addr.String()), + ) + newAddrs = append(newAddrs, addr) + } + } + + return newAddrs, ops, 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, @@ -217,7 +297,21 @@ func (s *Server) UpdateNodeAnnouncement(_ context.Context, } } - // TODO(positiveblue): apply addresses modifications + if len(req.AddressUpdates) > 0 { + newAddrs, ops, err := s.updateAddresses( + currentNodeAnn.Addresses, + req.AddressUpdates, + ) + if err != nil { + return nil, fmt.Errorf("error trying to update node "+ + "addresses: %w", err) + } + resp.Ops = append(resp.Ops, ops) + nodeModifiers = append( + nodeModifiers, + netann.NodeAnnSetAddrs(newAddrs), + ) + } if len(nodeModifiers) == 0 { return nil, fmt.Errorf("unable detect any new values to " + diff --git a/lntest/itest/assertions.go b/lntest/itest/assertions.go index 01fe6b2116..dac88010d5 100644 --- a/lntest/itest/assertions.go +++ b/lntest/itest/assertions.go @@ -1865,6 +1865,24 @@ func assertNodeAnnouncement(t *harnessTest, n1, n2 *lnrpc.NodeUpdate) { // Color should match. require.Equal(t.t, n1.Color, n2.Color, "color don't match") + + // NodeAddresses should match. + require.Equal( + t.t, len(n1.NodeAddresses), len(n2.NodeAddresses), + "node addresses don't match", + ) + + addrs := make(map[string]struct{}, len(n1.NodeAddresses)) + for _, nodeAddr := range n1.NodeAddresses { + addrs[nodeAddr.Addr] = struct{}{} + } + + for _, nodeAddr := range n2.NodeAddresses { + if _, ok := addrs[nodeAddr.Addr]; !ok { + t.Fatalf("address %v not found in node announcement", + nodeAddr.Addr) + } + } } // assertUpdateNodeAnnouncementResponse is a helper function to assert diff --git a/lntest/itest/lnd_channel_graph_test.go b/lntest/itest/lnd_channel_graph_test.go index 7c19883e06..212ae00f14 100644 --- a/lntest/itest/lnd_channel_graph_test.go +++ b/lntest/itest/lnd_channel_graph_test.go @@ -810,6 +810,17 @@ func testUpdateNodeAnnouncement(net *lntest.NetworkHarness, t *harnessTest) { defer close(aliceSub.quit) var lndArgs []string + + // Add some exta addresses to the default ones. + extraAddrs := []string{ + "192.168.1.1:8333", + "[2001:db8:85a3:8d3:1319:8a2e:370:7348]:8337", + "bkb6azqggsaiskzi.onion:9735", + "fomvuglh6h6vcag73xo5t5gv56ombih3zr2xvplkpbfd7wrog4swjwid.onion:1234", + } + for _, addr := range extraAddrs { + lndArgs = append(lndArgs, "--externalip="+addr) + } dave := net.NewNode(t.t, "Dave", lndArgs) defer shutdownAndAssert(net, t, dave) @@ -819,9 +830,21 @@ func testUpdateNodeAnnouncement(net *lntest.NetworkHarness, t *harnessTest) { resp, err := dave.GetInfo(ctxt, nodeInfoReq) require.NoError(t.t, err, "unable to get dave's information") + defaultAddrs := make([]*lnrpc.NodeAddress, 0, len(resp.Uris)) + for _, uri := range resp.GetUris() { + values := strings.Split(uri, "@") + defaultAddrs = append( + defaultAddrs, &lnrpc.NodeAddress{ + Addr: values[1], + Network: "tcp", + }, + ) + } + defaultDaveNodeAnn := &lnrpc.NodeUpdate{ - Alias: resp.Alias, - Color: resp.Color, + Alias: resp.Alias, + Color: resp.Color, + NodeAddresses: defaultAddrs, } // Dave must have an open channel before he can send a node @@ -869,25 +892,57 @@ func testUpdateNodeAnnouncement(net *lntest.NetworkHarness, t *harnessTest) { newAlias := "new-alias" newColor := "#2288ee" + newAddresses := []string{ + "192.168.1.10:8333", + "192.168.1.11:8333", + } + + updateAddressActions := []*peersrpc.UpdateAddressAction{ + { + Action: peersrpc.UpdateAction_ADD, + Address: newAddresses[0], + }, + { + Action: peersrpc.UpdateAction_ADD, + Address: newAddresses[1], + }, + { + Action: peersrpc.UpdateAction_REMOVE, + Address: defaultAddrs[0].Addr, + }, + } + nodeAnnReq := &peersrpc.NodeAnnouncementUpdateRequest{ - Alias: newAlias, - Color: newColor, + Alias: newAlias, + Color: newColor, + AddressUpdates: updateAddressActions, } response, err := dave.UpdateNodeAnnouncement(ctxt, nodeAnnReq) require.NoError(t.t, err, "unable to update dave's node announcement") expectedOps := map[string]int{ - "color": 1, - "alias": 1, + "color": 1, + "alias": 1, + "addresses": 3, } assertUpdateNodeAnnouncementResponse(t, response, expectedOps) + newNodeAddresses := []*lnrpc.NodeAddress{} + // We removed the first address. + newNodeAddresses = append(newNodeAddresses, defaultAddrs[1:]...) + newNodeAddresses = append( + newNodeAddresses, + &lnrpc.NodeAddress{Addr: newAddresses[0], Network: "tcp"}, + &lnrpc.NodeAddress{Addr: newAddresses[1], Network: "tcp"}, + ) + // 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, - Color: newColor, + Alias: newAlias, + Color: newColor, + NodeAddresses: newNodeAddresses, } // We'll then wait for Alice to receive dave's node announcement diff --git a/netann/node_announcement.go b/netann/node_announcement.go index 48dc8411e0..e0fbb9df3c 100644 --- a/netann/node_announcement.go +++ b/netann/node_announcement.go @@ -15,7 +15,7 @@ import ( type NodeAnnModifier func(*lnwire.NodeAnnouncement) // NodeAnnSetAlias is a functional option that sets the alias of the -// given node announcment. +// given node announcement. func NodeAnnSetAlias(alias lnwire.NodeAlias) func(*lnwire.NodeAnnouncement) { return func(nodeAnn *lnwire.NodeAnnouncement) { nodeAnn.Alias = alias @@ -31,7 +31,7 @@ func NodeAnnSetAddrs(addrs []net.Addr) func(*lnwire.NodeAnnouncement) { } // NodeAnnSetColor is a functional option that sets the color of the -// given node announcment. +// given node announcement. func NodeAnnSetColor(newColor color.RGBA) func(*lnwire.NodeAnnouncement) { return func(nodeAnn *lnwire.NodeAnnouncement) { nodeAnn.RGBColor = newColor From ae2aa5671f61b2642862b18353a84f41c624ea0a Mon Sep 17 00:00:00 2001 From: positiveblue Date: Sun, 2 Jan 2022 15:56:04 -0800 Subject: [PATCH 09/11] lnrpc/peers: handle feature bit changes in updateNodeAnnouncement --- feature/manager.go | 5 ++ lnrpc/peersrpc/peers_server.go | 79 +++++++++++++++++++++++++- lntest/itest/lnd_channel_graph_test.go | 75 ++++++++++++++++++++++++ netann/node_announcement.go | 8 +++ server.go | 5 ++ 5 files changed, 171 insertions(+), 1 deletion(-) diff --git a/feature/manager.go b/feature/manager.go index 87c85f04a3..f69d2c49f8 100644 --- a/feature/manager.go +++ b/feature/manager.go @@ -141,6 +141,11 @@ func (m *Manager) GetRaw(set Set) *lnwire.RawFeatureVector { return lnwire.NewRawFeatureVector() } +// SetRaw sets a new raw feature vector for the given set. +func (m *Manager) SetRaw(set Set, raw *lnwire.RawFeatureVector) { + m.fsets[set] = raw +} + // Get returns a feature vector for the passed set. If no set is known, an empty // feature vector is returned. func (m *Manager) Get(set Set) *lnwire.FeatureVector { diff --git a/lnrpc/peersrpc/peers_server.go b/lnrpc/peersrpc/peers_server.go index 16cc20af6a..84516f7dca 100644 --- a/lnrpc/peersrpc/peers_server.go +++ b/lnrpc/peersrpc/peers_server.go @@ -10,6 +10,7 @@ import ( "sync/atomic" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "github.com/lightningnetwork/lnd/feature" "github.com/lightningnetwork/lnd/lncfg" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnwire" @@ -241,6 +242,68 @@ func (s *Server) updateAddresses(currentAddresses []net.Addr, return newAddrs, ops, nil } +// updateFeatures computes the new raw SetNodeAnn after executing the update +// actions. +func (s *Server) updateFeatures(currentfeatures *lnwire.RawFeatureVector, + updates []*UpdateFeatureAction) (*lnwire.RawFeatureVector, + *lnrpc.Op, error) { + + ops := &lnrpc.Op{Entity: "features"} + raw := currentfeatures.Clone() + + for _, update := range updates { + bit := lnwire.FeatureBit(update.FeatureBit) + + switch update.Action { + case UpdateAction_ADD: + if raw.IsSet(bit) { + return nil, nil, fmt.Errorf( + "invalid add action for bit %v, "+ + "bit is already set", + update.FeatureBit, + ) + } + raw.Set(bit) + ops.Actions = append( + ops.Actions, + fmt.Sprintf("%s set", lnwire.Features[bit]), + ) + + case UpdateAction_REMOVE: + if !raw.IsSet(bit) { + return nil, nil, fmt.Errorf( + "invalid remove action for bit %v, "+ + "bit is already unset", + update.FeatureBit, + ) + } + raw.Unset(bit) + ops.Actions = append( + ops.Actions, + fmt.Sprintf("%s unset", lnwire.Features[bit]), + ) + + default: + return nil, nil, fmt.Errorf( + "invalid update action (%v) for bit %v", + update.Action, + update.FeatureBit, + ) + } + } + + // Validate our new SetNodeAnn. + fv := lnwire.NewFeatureVector(raw, lnwire.Features) + if err := feature.ValidateDeps(fv); err != nil { + return nil, nil, fmt.Errorf( + "invalid feature set (SetNodeAnn): %v", + err, + ) + } + + return raw, ops, 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, @@ -256,7 +319,21 @@ func (s *Server) UpdateNodeAnnouncement(_ context.Context, "announcement: %v", err) } - // TODO(positiveblue): apply feature bit modifications + if len(req.FeatureUpdates) > 0 { + features, ops, err := s.updateFeatures( + currentNodeAnn.Features, + req.FeatureUpdates, + ) + if err != nil { + return nil, fmt.Errorf("error trying to update node "+ + "features: %w", err) + } + resp.Ops = append(resp.Ops, ops) + nodeModifiers = append( + nodeModifiers, + netann.NodeAnnSetFeatures(features), + ) + } if req.Color != "" { color, err := lncfg.ParseHexColor(req.Color) diff --git a/lntest/itest/lnd_channel_graph_test.go b/lntest/itest/lnd_channel_graph_test.go index 212ae00f14..c001c5f0ca 100644 --- a/lntest/itest/lnd_channel_graph_test.go +++ b/lntest/itest/lnd_channel_graph_test.go @@ -841,6 +841,15 @@ func testUpdateNodeAnnouncement(net *lntest.NetworkHarness, t *harnessTest) { ) } + // This feature bit is used to test that our endpoint sets/unsets + // feature bits properly. If the current FeatureBit is set by default + // update this one for another one unset by default at random. + featureBit := lnrpc.FeatureBit_WUMBO_CHANNELS_REQ + featureIdx := uint32(featureBit) + if _, ok := resp.Features[featureIdx]; ok { + t.Fatalf("unexpected feature bit enabled by default") + } + defaultDaveNodeAnn := &lnrpc.NodeUpdate{ Alias: resp.Alias, Color: resp.Color, @@ -912,16 +921,25 @@ func testUpdateNodeAnnouncement(net *lntest.NetworkHarness, t *harnessTest) { }, } + updateFeatureActions := []*peersrpc.UpdateFeatureAction{ + { + Action: peersrpc.UpdateAction_ADD, + FeatureBit: featureBit, + }, + } + nodeAnnReq := &peersrpc.NodeAnnouncementUpdateRequest{ Alias: newAlias, Color: newColor, AddressUpdates: updateAddressActions, + FeatureUpdates: updateFeatureActions, } response, err := dave.UpdateNodeAnnouncement(ctxt, nodeAnnReq) require.NoError(t.t, err, "unable to update dave's node announcement") expectedOps := map[string]int{ + "features": 1, "color": 1, "alias": 1, "addresses": 3, @@ -951,6 +969,63 @@ func testUpdateNodeAnnouncement(net *lntest.NetworkHarness, t *harnessTest) { aliceSub, dave.PubKeyStr, newDaveNodeAnn, t, ) + // Check that the feature bit was set correctly. + resp, err = dave.GetInfo(ctxt, nodeInfoReq) + require.NoError(t.t, err, "unable to get dave's information") + + if _, ok := resp.Features[featureIdx]; !ok { + t.Fatalf("failed to set feature bit") + } + + // Check that we cannot set a feature bit that is already set. + nodeAnnReq = &peersrpc.NodeAnnouncementUpdateRequest{ + FeatureUpdates: updateFeatureActions, + } + + _, err = dave.UpdateNodeAnnouncement(ctxt, nodeAnnReq) + require.Error( + t.t, err, "missing expected error: cannot set a feature bit "+ + "that is already set", + ) + + // Check that we can unset feature bits. + updateFeatureActions = []*peersrpc.UpdateFeatureAction{ + { + Action: peersrpc.UpdateAction_REMOVE, + FeatureBit: featureBit, + }, + } + + nodeAnnReq = &peersrpc.NodeAnnouncementUpdateRequest{ + FeatureUpdates: updateFeatureActions, + } + + response, err = dave.UpdateNodeAnnouncement(ctxt, nodeAnnReq) + require.NoError(t.t, err, "unable to update dave's node announcement") + + expectedOps = map[string]int{ + "features": 1, + } + assertUpdateNodeAnnouncementResponse(t, response, expectedOps) + + resp, err = dave.GetInfo(ctxt, nodeInfoReq) + require.NoError(t.t, err, "unable to get dave's information") + + if _, ok := resp.Features[featureIdx]; ok { + t.Fatalf("failed to unset feature bit") + } + + // Check that we cannot unset a feature bit that is already unset. + nodeAnnReq = &peersrpc.NodeAnnouncementUpdateRequest{ + FeatureUpdates: updateFeatureActions, + } + + _, err = dave.UpdateNodeAnnouncement(ctxt, nodeAnnReq) + require.Error( + t.t, err, "missing expected error: cannot unset a feature bit "+ + "that is already unset", + ) + // Close the channel between Bob and Dave. closeChannelAndAssert(t, net, net.Bob, chanPoint, false) } diff --git a/netann/node_announcement.go b/netann/node_announcement.go index e0fbb9df3c..42296bf222 100644 --- a/netann/node_announcement.go +++ b/netann/node_announcement.go @@ -38,6 +38,14 @@ func NodeAnnSetColor(newColor color.RGBA) func(*lnwire.NodeAnnouncement) { } } +// NodeAnnSetFeatures is a functional option that allows updating the features of +// the given node announcement. +func NodeAnnSetFeatures(features *lnwire.RawFeatureVector) func(*lnwire.NodeAnnouncement) { + return func(nodeAnn *lnwire.NodeAnnouncement) { + nodeAnn.Features = features + } +} + // NodeAnnSetTimestamp is a functional option that sets the timestamp of the // announcement to the current time, or increments it if the timestamp is // already in the future. diff --git a/server.go b/server.go index ad08fe4b0b..c4110bcc1e 100644 --- a/server.go +++ b/server.go @@ -2771,6 +2771,11 @@ func (s *server) updateAndBrodcastSelfNode( return fmt.Errorf("can't set self node: %v", err) } + // Update the feature bits for the SetNodeAnn in case they changed. + s.featureMgr.SetRaw( + feature.SetNodeAnn, selfNode.Features.RawFeatureVector, + ) + // Finally, propagate it to the nodes in the network. err = s.BroadcastMessage(nil, &newNodeAnn) if err != nil { From f3fdf8d28edb77a34cc6fdbc3cbd669566c52041 Mon Sep 17 00:00:00 2001 From: positiveblue Date: Fri, 1 Apr 2022 09:45:46 -0700 Subject: [PATCH 10/11] lncli: add `peers updatenodeannouncement` --- cmd/lncli/main.go | 1 + cmd/lncli/peersrpc_active.go | 162 ++++++++++++++++++++++++++++++++++ cmd/lncli/peersrpc_default.go | 11 +++ 3 files changed, 174 insertions(+) create mode 100644 cmd/lncli/peersrpc_active.go create mode 100644 cmd/lncli/peersrpc_default.go diff --git a/cmd/lncli/main.go b/cmd/lncli/main.go index 836a6794fe..5db1df4bc8 100644 --- a/cmd/lncli/main.go +++ b/cmd/lncli/main.go @@ -400,6 +400,7 @@ func main() { app.Commands = append(app.Commands, watchtowerCommands()...) app.Commands = append(app.Commands, wtclientCommands()...) app.Commands = append(app.Commands, devCommands()...) + app.Commands = append(app.Commands, peersCommands()...) if err := app.Run(os.Args); err != nil { fatal(err) diff --git a/cmd/lncli/peersrpc_active.go b/cmd/lncli/peersrpc_active.go new file mode 100644 index 0000000000..09ef17d583 --- /dev/null +++ b/cmd/lncli/peersrpc_active.go @@ -0,0 +1,162 @@ +//go:build peersrpc +// +build peersrpc + +package main + +import ( + "fmt" + + "github.com/lightningnetwork/lnd/lnrpc" + "github.com/lightningnetwork/lnd/lnrpc/peersrpc" + "github.com/urfave/cli" +) + +// peersCommands will return the set of commands to enable for peersrpc +// builds. +func peersCommands() []cli.Command { + return []cli.Command{ + { + Name: "peers", + Category: "Peers", + Usage: "Interacts with the other nodes of the " + + "newtwork", + Subcommands: []cli.Command{ + updateNodeAnnouncementCommand, + }, + }, + } +} + +func getPeersClient(ctx *cli.Context) (peersrpc.PeersClient, func()) { + conn := getClientConn(ctx, false) + cleanUp := func() { + conn.Close() + } + return peersrpc.NewPeersClient(conn), cleanUp +} + +var updateNodeAnnouncementCommand = cli.Command{ + Name: "updatenodeannouncement", + Category: "Peers", + Usage: "update and brodcast a new node announcement", + Description: ` + Update the node's information and broadcast a new node announcement. + + Add or remove addresses where your node can be reached at, change the + alias/color of the node or enable/disable supported feature bits without + restarting the node. A node announcement with the new information will + be created and brodcasted to the network.`, + ArgsUsage: "[--address_add=] [--address_remove=] [--alias=] " + + "[--color=] [--feature_bit_add=] [--feature_bit_remove=]", + Flags: []cli.Flag{ + cli.StringSliceFlag{ + Name: "address_add", + Usage: "a new address that should be added to the " + + "set of URIs of this node. Can be set " + + "multiple times in the same command", + }, + cli.StringSliceFlag{ + Name: "address_remove", + Usage: "an address that needs to be removed from the " + + "set of URIs of this node. Can be set " + + "multiple times in the same command", + }, + cli.StringFlag{ + Name: "alias", + Usage: "the new alias for this node, e.g. \"bob\"", + }, + cli.StringFlag{ + Name: "color", + Usage: "the new color for this node, e.g. #c42a81", + }, + cli.Int64SliceFlag{ + Name: "feature_bit_add", + Usage: "a feature bit index that needs to be enabled. " + + "Can be set multiple times in the same command", + }, + cli.Int64SliceFlag{ + Name: "feature_bit_remove", + Usage: "a feature bit that needs to be disabled" + + "Can be set multiple times in the same command", + }, + }, + Action: actionDecorator(updateNodeAnnouncement), +} + +func updateNodeAnnouncement(ctx *cli.Context) error { + ctxc := getContext() + client, cleanUp := getPeersClient(ctx) + defer cleanUp() + + change := false + + req := &peersrpc.NodeAnnouncementUpdateRequest{} + + if ctx.IsSet("address_add") { + change = true + for _, addr := range ctx.StringSlice("address_add") { + action := &peersrpc.UpdateAddressAction{ + Action: peersrpc.UpdateAction_ADD, + Address: addr, + } + req.AddressUpdates = append(req.AddressUpdates, action) + } + } + + if ctx.IsSet("address_remove") { + change = true + for _, addr := range ctx.StringSlice("address_remove") { + action := &peersrpc.UpdateAddressAction{ + Action: peersrpc.UpdateAction_REMOVE, + Address: addr, + } + req.AddressUpdates = append(req.AddressUpdates, action) + } + } + + if ctx.IsSet("alias") { + change = true + req.Alias = ctx.String("alias") + } + + if ctx.IsSet("color") { + change = true + req.Color = ctx.String("color") + } + + if ctx.IsSet("feature_bit_add") { + change = true + for _, feature := range ctx.IntSlice("feature_bit_add") { + action := &peersrpc.UpdateFeatureAction{ + Action: peersrpc.UpdateAction_ADD, + FeatureBit: lnrpc.FeatureBit(feature), + } + req.FeatureUpdates = append(req.FeatureUpdates, action) + } + } + + if ctx.IsSet("feature_bit_remove") { + change = true + for _, feature := range ctx.IntSlice("feature_bit_remove") { + action := &peersrpc.UpdateFeatureAction{ + Action: peersrpc.UpdateAction_REMOVE, + FeatureBit: lnrpc.FeatureBit(feature), + } + req.FeatureUpdates = append(req.FeatureUpdates, action) + } + } + + if !change { + return fmt.Errorf("no changes for the node information " + + "detected") + } + + resp, err := client.UpdateNodeAnnouncement(ctxc, req) + if err != nil { + return err + } + + printRespJSON(resp) + + return nil +} diff --git a/cmd/lncli/peersrpc_default.go b/cmd/lncli/peersrpc_default.go new file mode 100644 index 0000000000..24cb2b8134 --- /dev/null +++ b/cmd/lncli/peersrpc_default.go @@ -0,0 +1,11 @@ +//go:build !peersrpc +// +build !peersrpc + +package main + +import "github.com/urfave/cli" + +// peersCommands will return nil for non-peersrpc builds. +func peersCommands() []cli.Command { + return nil +} From fdfdd10e863646a4128ae79c1006397c601b5484 Mon Sep 17 00:00:00 2001 From: positiveblue Date: Fri, 1 Apr 2022 09:40:03 -0700 Subject: [PATCH 11/11] docs: add release notes --- docs/release-notes/release-notes-0.15.0.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/release-notes/release-notes-0.15.0.md b/docs/release-notes/release-notes-0.15.0.md index c3557e648c..996fa4eb5d 100644 --- a/docs/release-notes/release-notes-0.15.0.md +++ b/docs/release-notes/release-notes-0.15.0.md @@ -27,6 +27,9 @@ then watch it on chain. Taproot script spends are also supported through the * [Update description for `state` command](https://github.com/lightningnetwork/lnd/pull/6237). +* Add [update node announcement](https://github.com/lightningnetwork/lnd/pull/5587) + for updating and propagating node information. + ## Bug Fixes * [Pipelining an UpdateFulfillHTLC message now only happens when the related UpdateAddHTLC is locked-in.](https://github.com/lightningnetwork/lnd/pull/6246) @@ -173,6 +176,8 @@ then watch it on chain. Taproot script spends are also supported through the * [Improve validation of a PSBT packet when handling a request to finalize it.](https://github.com/lightningnetwork/lnd/pull/6217) +* [Add new Peers subserver](https://github.com/lightningnetwork/lnd/pull/5587) with a new endpoint for updating the `NodeAnnouncement` data without having to restart the node. + ## Documentation * Improved instructions on [how to build lnd for mobile](https://github.com/lightningnetwork/lnd/pull/6085).