From 3545685177cd8d194b24bdd756795ab784a24b6a Mon Sep 17 00:00:00 2001 From: Joost Jager Date: Thu, 20 Dec 2018 11:42:28 +0100 Subject: [PATCH] invoicesrpc: create sub server Sub server implementation is still empty. This is a preparatory step for adding invoice functionality. --- lnd.go | 2 +- lnrpc/invoicesrpc/config_active.go | 16 +++++ lnrpc/invoicesrpc/config_default.go | 6 ++ lnrpc/invoicesrpc/driver.go | 55 +++++++++++++++++ lnrpc/invoicesrpc/invoices.pb.go | 77 ++++++++++++++++++++++++ lnrpc/invoicesrpc/invoices.proto | 11 ++++ lnrpc/invoicesrpc/invoices_server.go | 89 ++++++++++++++++++++++++++++ lnrpc/invoicesrpc/log.go | 45 ++++++++++++++ log.go | 4 ++ rpcserver.go | 3 +- subrpcserver_config.go | 16 ++++- 11 files changed, 321 insertions(+), 3 deletions(-) create mode 100644 lnrpc/invoicesrpc/config_active.go create mode 100644 lnrpc/invoicesrpc/config_default.go create mode 100644 lnrpc/invoicesrpc/driver.go create mode 100644 lnrpc/invoicesrpc/invoices.pb.go create mode 100644 lnrpc/invoicesrpc/invoices.proto create mode 100644 lnrpc/invoicesrpc/invoices_server.go create mode 100644 lnrpc/invoicesrpc/log.go diff --git a/lnd.go b/lnd.go index 46adb4a4..96e5b9d6 100644 --- a/lnd.go +++ b/lnd.go @@ -337,7 +337,7 @@ func lndMain() error { // exported by the rpcServer. rpcServer, err := newRPCServer( server, macaroonService, cfg.SubRPCServers, serverOpts, - proxyOpts, atplManager, tlsConf, + proxyOpts, atplManager, server.invoices, tlsConf, ) if err != nil { srvrLog.Errorf("unable to start RPC server: %v", err) diff --git a/lnrpc/invoicesrpc/config_active.go b/lnrpc/invoicesrpc/config_active.go new file mode 100644 index 00000000..4cdb8f4b --- /dev/null +++ b/lnrpc/invoicesrpc/config_active.go @@ -0,0 +1,16 @@ +// +build invoicesrpc + +package invoicesrpc + +import ( + "github.com/lightningnetwork/lnd/invoices" +) + +// Config is the primary configuration struct for the invoices RPC server. It +// contains all the items required for the rpc 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 { + InvoiceRegistry *invoices.InvoiceRegistry +} diff --git a/lnrpc/invoicesrpc/config_default.go b/lnrpc/invoicesrpc/config_default.go new file mode 100644 index 00000000..bb40c480 --- /dev/null +++ b/lnrpc/invoicesrpc/config_default.go @@ -0,0 +1,6 @@ +// +build !invoicesrpc + +package invoicesrpc + +// Config is empty for non-invoicesrpc builds. +type Config struct{} diff --git a/lnrpc/invoicesrpc/driver.go b/lnrpc/invoicesrpc/driver.go new file mode 100644 index 00000000..54ad1fc1 --- /dev/null +++ b/lnrpc/invoicesrpc/driver.go @@ -0,0 +1,55 @@ +// +build invoicesrpc + +package invoicesrpc + +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) ( + lnrpc.SubServer, 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, + New: func(c lnrpc.SubServerConfigDispatcher) (lnrpc.SubServer, + lnrpc.MacaroonPerms, error) { + return createNewSubServer(c) + }, + } + + // 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/invoicesrpc/invoices.pb.go b/lnrpc/invoicesrpc/invoices.pb.go new file mode 100644 index 00000000..44b4701e --- /dev/null +++ b/lnrpc/invoicesrpc/invoices.pb.go @@ -0,0 +1,77 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: invoicesrpc/invoices.proto + +package invoicesrpc // import "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +import ( + context "golang.org/x/net/context" + grpc "google.golang.org/grpc" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// InvoicesClient is the client API for Invoices service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type InvoicesClient interface { +} + +type invoicesClient struct { + cc *grpc.ClientConn +} + +func NewInvoicesClient(cc *grpc.ClientConn) InvoicesClient { + return &invoicesClient{cc} +} + +// InvoicesServer is the server API for Invoices service. +type InvoicesServer interface { +} + +func RegisterInvoicesServer(s *grpc.Server, srv InvoicesServer) { + s.RegisterService(&_Invoices_serviceDesc, srv) +} + +var _Invoices_serviceDesc = grpc.ServiceDesc{ + ServiceName: "invoicesrpc.Invoices", + HandlerType: (*InvoicesServer)(nil), + Methods: []grpc.MethodDesc{}, + Streams: []grpc.StreamDesc{}, + Metadata: "invoicesrpc/invoices.proto", +} + +func init() { + proto.RegisterFile("invoicesrpc/invoices.proto", fileDescriptor_invoices_560fa62749d29606) +} + +var fileDescriptor_invoices_560fa62749d29606 = []byte{ + // 105 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0xca, 0xcc, 0x2b, 0xcb, + 0xcf, 0x4c, 0x4e, 0x2d, 0x2e, 0x2a, 0x48, 0xd6, 0x87, 0xb1, 0xf5, 0x0a, 0x8a, 0xf2, 0x4b, 0xf2, + 0x85, 0xb8, 0x91, 0xe4, 0x8c, 0xb8, 0xb8, 0x38, 0x3c, 0xa1, 0x5c, 0x27, 0xe3, 0x28, 0xc3, 0xf4, + 0xcc, 0x92, 0x8c, 0xd2, 0x24, 0xbd, 0xe4, 0xfc, 0x5c, 0xfd, 0x9c, 0xcc, 0xf4, 0x8c, 0x92, 0xbc, + 0xcc, 0xbc, 0xf4, 0xbc, 0xd4, 0x92, 0xf2, 0xfc, 0xa2, 0x6c, 0xfd, 0x9c, 0xbc, 0x14, 0xfd, 0x9c, + 0x3c, 0x64, 0x03, 0x8b, 0x0a, 0x92, 0x93, 0xd8, 0xc0, 0x86, 0x1a, 0x03, 0x02, 0x00, 0x00, 0xff, + 0xff, 0xaa, 0xe0, 0xa9, 0x52, 0x72, 0x00, 0x00, 0x00, +} diff --git a/lnrpc/invoicesrpc/invoices.proto b/lnrpc/invoicesrpc/invoices.proto new file mode 100644 index 00000000..f21ba1cc --- /dev/null +++ b/lnrpc/invoicesrpc/invoices.proto @@ -0,0 +1,11 @@ +syntax = "proto3"; + +package invoicesrpc; + +option go_package = "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"; + +// Invoices is a service that can be used to create, accept, settle and cancel +// invoices. +service Invoices { +} + diff --git a/lnrpc/invoicesrpc/invoices_server.go b/lnrpc/invoicesrpc/invoices_server.go new file mode 100644 index 00000000..0cdb477a --- /dev/null +++ b/lnrpc/invoicesrpc/invoices_server.go @@ -0,0 +1,89 @@ +// +build invoicesrpc + +package invoicesrpc + +import ( + "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 it as the name of our + // RPC service. + subServerName = "InvoicesRPC" +) + +var ( + // macPermissions maps RPC calls to the permissions they require. + macPermissions = map[string][]bakery.Op{} +) + +// Server is a sub-server of the main RPC server: the invoices RPC. This sub +// RPC server allows external callers to access the status of the invoices +// currently active within lnd, as well as configuring it at runtime. +type Server struct { + started int32 // To be used atomically. + shutdown int32 // To be used atomically. + + cfg *Config +} + +// A compile time check to ensure that Server fully implements the +// InvoicesServer gRPC service. +var _ InvoicesServer = (*Server)(nil) + +// New returns a new instance of the invoicesrpc Invoices 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) { + // We don't create any new macaroons for this subserver, instead reuse + // existing onchain/offchain permissions. + 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 { + 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 { + 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.SubServer interface. +func (s *Server) 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. + RegisterInvoicesServer(grpcServer, s) + + log.Debugf("Invoices RPC server successfully register with root " + + "gRPC server") + + return nil +} diff --git a/lnrpc/invoicesrpc/log.go b/lnrpc/invoicesrpc/log.go new file mode 100644 index 00000000..d29c7378 --- /dev/null +++ b/lnrpc/invoicesrpc/log.go @@ -0,0 +1,45 @@ +package invoicesrpc + +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 + +// The default amount of logging is none. +func init() { + UseLogger(build.NewSubLogger("IRPC", 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 + +// 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 { + return logClosure(c) +} diff --git a/log.go b/log.go index ed06ad69..b066485a 100644 --- a/log.go +++ b/log.go @@ -21,6 +21,7 @@ import ( "github.com/lightningnetwork/lnd/invoices" "github.com/lightningnetwork/lnd/lnrpc/autopilotrpc" "github.com/lightningnetwork/lnd/lnrpc/chainrpc" + "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" "github.com/lightningnetwork/lnd/lnrpc/signrpc" "github.com/lightningnetwork/lnd/lnrpc/walletrpc" "github.com/lightningnetwork/lnd/lnwallet" @@ -78,6 +79,7 @@ var ( nannLog = build.NewSubLogger("NANN", backendLog.Logger) wtwrLog = build.NewSubLogger("WTWR", backendLog.Logger) ntfrLog = build.NewSubLogger("NTFR", backendLog.Logger) + irpcLog = build.NewSubLogger("IRPC", backendLog.Logger) ) // Initialize package-global logger variables. @@ -102,6 +104,7 @@ func init() { netann.UseLogger(nannLog) watchtower.UseLogger(wtwrLog) chainrpc.UseLogger(ntfrLog) + invoicesrpc.UseLogger(irpcLog) } // subsystemLoggers maps each subsystem identifier to its associated logger. @@ -132,6 +135,7 @@ var subsystemLoggers = map[string]btclog.Logger{ "NANN": nannLog, "WTWR": wtwrLog, "NTFR": ntfnLog, + "IRPC": irpcLog, } // initLogRotator initializes the logging rotator to write logs to logFile and diff --git a/rpcserver.go b/rpcserver.go index 5c8bfa85..813675c3 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -397,6 +397,7 @@ var _ lnrpc.LightningServer = (*rpcServer)(nil) func newRPCServer(s *server, macService *macaroons.Service, subServerCgs *subRPCServerConfigs, serverOpts []grpc.ServerOption, restServerOpts []grpc.DialOption, atpl *autopilot.Manager, + invoiceRegistry *invoices.InvoiceRegistry, tlsCfg *tls.Config) (*rpcServer, error) { var ( @@ -408,7 +409,7 @@ func newRPCServer(s *server, macService *macaroons.Service, // the dependencies they need are properly populated within each sub // server configuration struct. err := subServerCgs.PopulateDependencies( - s.cc, networkDir, macService, atpl, + s.cc, networkDir, macService, atpl, invoiceRegistry, ) if err != nil { return nil, err diff --git a/subrpcserver_config.go b/subrpcserver_config.go index 9d39732c..722cd788 100644 --- a/subrpcserver_config.go +++ b/subrpcserver_config.go @@ -5,8 +5,10 @@ import ( "reflect" "github.com/lightningnetwork/lnd/autopilot" + "github.com/lightningnetwork/lnd/invoices" "github.com/lightningnetwork/lnd/lnrpc/autopilotrpc" "github.com/lightningnetwork/lnd/lnrpc/chainrpc" + "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" "github.com/lightningnetwork/lnd/lnrpc/signrpc" "github.com/lightningnetwork/lnd/lnrpc/walletrpc" "github.com/lightningnetwork/lnd/macaroons" @@ -37,6 +39,10 @@ type subRPCServerConfigs struct { // client to be notified of certain on-chain events (new blocks, // confirmations, spends). ChainRPC *chainrpc.Config `group:"chainrpc" namespace:"chainrpc"` + + // InvoicesRPC is a sub-RPC server that exposes invoice related methods + // as a gRPC service. + InvoicesRPC *invoicesrpc.Config `group:"invoicesrpc" namespace:"invoicesrpc"` } // PopulateDependencies attempts to iterate through all the sub-server configs @@ -47,7 +53,8 @@ type subRPCServerConfigs struct { // FetchConfig method. func (s *subRPCServerConfigs) PopulateDependencies(cc *chainControl, networkDir string, macService *macaroons.Service, - atpl *autopilot.Manager) error { + atpl *autopilot.Manager, + invoiceRegistry *invoices.InvoiceRegistry) error { // First, we'll use reflect to obtain a version of the config struct // that allows us to programmatically inspect its fields. @@ -125,6 +132,13 @@ func (s *subRPCServerConfigs) PopulateDependencies(cc *chainControl, reflect.ValueOf(cc.chainNotifier), ) + case *invoicesrpc.Config: + subCfgValue := extractReflectValue(cfg) + + subCfgValue.FieldByName("InvoiceRegistry").Set( + reflect.ValueOf(invoiceRegistry), + ) + default: return fmt.Errorf("unknown field: %v, %T", fieldName, cfg)