Repository: lightningnetwork/lnd (language: go)

Issue:
Make `RPCServer` gRPC methods context aware
The `RPCServer` within `lnd` acts as a `gRPC` server that serves clients such as `lncli`. Currently some of the long running methods in the `RPCServer` will keep executing until they finish or encounter an error, even though the client has canceled the API call.

We should make the `RPCServer` context aware, ceasing execution when the `gRPC` context is canceled.

## Steps to completion
1. Make the server properly listen for stream closes in case of streaming APIs, or context cancelation in case of non-streaming. 
2. Stop any long running task in case of cancelation, and clean up (more information: https://grpc.io/docs/tutorials/basic/go.html).

Companion issue: https://github.com/lightningnetwork/lnd/issues/2133


Discussion / hints:
@halseth should listening to stream closes/context cancellation be done manually on a per-handler basis (i.e. just add `case <-ctx.Done` for all `select` statements in RPC handlers)? Another option is to implement a generic task killing mechanism but since we can't normally kill goroutines in Golang, this will be too hacky and dangerous. Maybe there are other options I'm not aware of?

Yet another option is to implement a conventional `errNonNil(error, context.Context)` function that should be used in all error checks withing RPC handlers thus allowing to abort in case of context cancellation at every error handling check point (which sounds like an overkill now that I've written it down).
I was thinking more like the first option. For select statements within the `rpcServer` we should listen on `ctx.Done()`, while if we call into other (long-running) parts of LND, we should supply a `quit` channel.

Fix patch:
diff --git a/lnrpc/invoicesrpc/invoices_server.go b/lnrpc/invoicesrpc/invoices_server.go
index 2fa881db0e..0c76b1d7b5 100644
--- a/lnrpc/invoicesrpc/invoices_server.go
+++ b/lnrpc/invoicesrpc/invoices_server.go
@@ -253,6 +253,9 @@ func (s *Server) SubscribeSingleInvoice(req *SubscribeSingleInvoiceRequest,
 				return nil
 			}
 
+		case <-updateStream.Context().Done():
+			return updateStream.Context().Err()
+
 		case <-s.quit:
 			return nil
 		}
diff --git a/rpcserver.go b/rpcserver.go
index dd7e42c9f9..acb460768b 100644
--- a/rpcserver.go
+++ b/rpcserver.go
@@ -2756,6 +2756,10 @@ func (r *rpcServer) SubscribePeerEvents(req *lnrpc.PeerEventSubscription,
 			if err := eventStream.Send(event); err != nil {
 				return err
 			}
+
+		case <-eventStream.Context().Done():
+			return eventStream.Context().Err()
+
 		case <-r.quit:
 			return nil
 		}
@@ -4029,6 +4033,10 @@ func (r *rpcServer) SubscribeChannelEvents(req *lnrpc.ChannelEventSubscription,
 			if err := updateStream.Send(update); err != nil {
 				return err
 			}
+
+		case <-updateStream.Context().Done():
+			return updateStream.Context().Err()
+
 		case <-r.quit:
 			return nil
 		}
@@ -4946,6 +4954,9 @@ func (r *rpcServer) SubscribeInvoices(req *lnrpc.InvoiceSubscription,
 				return err
 			}
 
+		case <-updateStream.Context().Done():
+			return updateStream.Context().Err()
+
 		case <-r.quit:
 			return nil
 		}
@@ -5003,6 +5014,9 @@ func (r *rpcServer) SubscribeTransactions(req *lnrpc.GetTransactionsRequest,
 				return err
 			}
 
+		case <-updateStream.Context().Done():
+			return updateStream.Context().Err()
+
 		case <-r.quit:
 			return nil
 		}
@@ -5524,6 +5538,11 @@ func (r *rpcServer) SubscribeChannelGraph(req *lnrpc.GraphTopologySubscription,
 				return err
 			}
 
+		// The context was cancelled so we report a cancellation error
+		// and exit immediately.
+		case <-updateStream.Context().Done():
+			return updateStream.Context().Err()
+
 		// The server is quitting, so we'll exit immediately. Returning
 		// nil will close the clients read end of the stream.
 		case <-r.quit:
@@ -6446,6 +6465,9 @@ func (r *rpcServer) SubscribeChannelBackups(req *lnrpc.ChannelBackupSubscription
 				return err
 			}
 
+		case <-updateStream.Context().Done():
+			return updateStream.Context().Err()
+
 		case <-r.quit:
 			return nil
 		}


Test patch:
diff --git a/lntest/itest/log_error_whitelist.txt b/lntest/itest/log_error_whitelist.txt
index 89dd0f3516..08b3419f26 100644
--- a/lntest/itest/log_error_whitelist.txt
+++ b/lntest/itest/log_error_whitelist.txt
@@ -269,3 +269,7 @@
 <time> [ERR] RPCS: WS: error writing message: websocket: close sent
 <time> [ERR] RPCS: [/routerrpc.Router/XImportMissionControl]: pair: <hex> -> <hex>: invalid failure: msat: <amt> and sat: 0.0000002 BTC values not equal
 <time> [ERR] BTCN: utxo scan failed: neutrino shutting down
+<time> [ERR] RPCS: [/lnrpc.Lightning/SubscribeChannelGraph]: context canceled
+<time> [ERR] RPCS: [/lnrpc.Lightning/SubscribeInvoices]: context canceled
+<time> [ERR] RPCS: [/lnrpc.Lightning/SubscribeChannelGraph]: context deadline exceeded
+<time> [ERR] RPCS: [/invoicesrpc.Invoices/SubscribeSingleInvoice]: context canceled