diff --git a/docs/release-notes/release-notes-0.14.0.md b/docs/release-notes/release-notes-0.14.0.md index 7a6856e6d..eff5ce605 100644 --- a/docs/release-notes/release-notes-0.14.0.md +++ b/docs/release-notes/release-notes-0.14.0.md @@ -263,6 +263,10 @@ you. console](https://github.com/lightningnetwork/lnd/pull/5802) when `lnd` fails loading the user specified config. +* [Make it possible to add more than one RPC Listener when calling lnd.Main](https://github.com/lightningnetwork/lnd/pull/5777). And + add MacChan field for passing back lnd's admin macaroon back to the program + calling lnd, when needed. + ## Code Health ### Code cleanup, refactor, typo fixes diff --git a/lnd.go b/lnd.go index 23f9b835e..d5e431e85 100644 --- a/lnd.go +++ b/lnd.go @@ -174,14 +174,19 @@ type ListenerWithSignal struct { // Ready will be closed by the server listening on Listener. Ready chan struct{} + + // MacChan is an optional way to pass the admin macaroon to the program + // that started lnd. The channel should be buffered to avoid lnd being + // blocked on sending to the channel. + MacChan chan []byte } // ListenerCfg is a wrapper around custom listeners that can be passed to lnd // when calling its main method. type ListenerCfg struct { - // RPCListener can be set to the listener to use for the RPC server. If - // nil a regular network listener will be created. - RPCListener *ListenerWithSignal + // RPCListeners can be set to the listeners to use for the RPC server. + // If empty a regular network listener will be created. + RPCListeners []*ListenerWithSignal // ExternalRPCSubserverCfg is optional and specifies the registration // callback and permissions to register external gRPC subservers. @@ -326,8 +331,8 @@ func Main(cfg *Config, lisCfg ListenerCfg, interceptor signal.Interceptor) error // If we have chosen to start with a dedicated listener for the // rpc server, we set it directly. var grpcListeners []*ListenerWithSignal - if lisCfg.RPCListener != nil { - grpcListeners = []*ListenerWithSignal{lisCfg.RPCListener} + if len(lisCfg.RPCListeners) > 0 { + grpcListeners = append(grpcListeners, lisCfg.RPCListeners...) } else { // Otherwise we create listeners from the RPCListeners defined // in the config. @@ -619,6 +624,12 @@ func Main(cfg *Config, lisCfg ListenerCfg, interceptor signal.Interceptor) error // The channel is buffered by one element so writing // should not block here. walletInitParams.MacResponseChan <- adminMacBytes + + for _, lis := range grpcListeners { + if lis.MacChan != nil { + lis.MacChan <- adminMacBytes + } + } } // If the user requested a stateless initialization, no macaroon @@ -680,6 +691,14 @@ func Main(cfg *Config, lisCfg ListenerCfg, interceptor signal.Interceptor) error // --no-macaroons is used. close(walletInitParams.MacResponseChan) + // We'll also close all the macaroon channels since lnd is done sending + // macaroon data over it. + for _, lis := range grpcListeners { + if lis.MacChan != nil { + close(lis.MacChan) + } + } + // With the information parsed from the configuration, create valid // instances of the pertinent interfaces required to operate the // Lightning Network Daemon. diff --git a/mobile/bindings.go b/mobile/bindings.go index ca841d793..da771c4af 100644 --- a/mobile/bindings.go +++ b/mobile/bindings.go @@ -94,10 +94,10 @@ func Start(extraArgs string, rpcReady Callback) { // We call the main method with the custom in-memory listener called by // the mobile APIs, such that the grpc server will use it. cfg := lnd.ListenerCfg{ - RPCListener: &lnd.ListenerWithSignal{ + RPCListeners: []*lnd.ListenerWithSignal{{ Listener: lightningLis, Ready: rpcListening, - }, + }}, } // Call the "real" main in a nested manner so the defers will properly diff --git a/rpcserver.go b/rpcserver.go index 7afe48e23..bdf00cded 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -258,6 +258,42 @@ func calculateFeeRate(satPerByte, satPerVByte uint64, targetConf uint32, } +// GetAllPermissions returns all the permissions required to interact with lnd. +func GetAllPermissions() []bakery.Op { + allPerms := make([]bakery.Op, 0) + + // The map will help keep track of which specific permission pairs have + // already been added to the slice. + allPermsMap := make(map[string]map[string]struct{}) + + for _, perms := range MainRPCServerPermissions() { + for _, perm := range perms { + entity := perm.Entity + action := perm.Action + + // If this specific entity-action permission pair isn't + // in the map yet. Add it to map, and the permission + // slice. + if acts, ok := allPermsMap[entity]; ok { + if _, ok := acts[action]; !ok { + allPermsMap[entity][action] = struct{}{} + + allPerms = append( + allPerms, perm, + ) + } + + } else { + allPermsMap[entity] = make(map[string]struct{}) + allPermsMap[entity][action] = struct{}{} + allPerms = append(allPerms, perm) + } + } + } + + return allPerms +} + // MainRPCServerPermissions returns a mapping of the main RPC server calls to // the permissions they require. func MainRPCServerPermissions() map[string][]bakery.Op { diff --git a/rpcserver_test.go b/rpcserver_test.go new file mode 100644 index 000000000..ca70ad9df --- /dev/null +++ b/rpcserver_test.go @@ -0,0 +1,14 @@ +package lnd + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGetAllPermissions(t *testing.T) { + perms := GetAllPermissions() + + // Currently there are there are 16 entity:action pairs in use. + assert.Equal(t, len(perms), 16) +}