From 62dc0782718a0cfec26dba725cc1391f162a8576 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Sun, 16 Jan 2022 16:38:21 +0100 Subject: [PATCH] cln-grpc: Generate server dispatcher The server doesn't do much more than unwrapping the request from its grpc envelope, convert it into the matching JSON-RPC binding struct, initiate the RPC connection (until we have connection pooling), and then forwards the converted request. The inverse then happens for the result. --- cln-grpc/Makefile | 3 +- cln-grpc/src/server.rs | 240 ++++++++++++++++++++++++++++++ contrib/msggen/msggen/__main__.py | 6 +- contrib/msggen/msggen/grpc.py | 72 +++++++++ 4 files changed, 319 insertions(+), 2 deletions(-) create mode 100644 cln-grpc/src/server.rs diff --git a/cln-grpc/Makefile b/cln-grpc/Makefile index f7a42b85f..993286be7 100644 --- a/cln-grpc/Makefile +++ b/cln-grpc/Makefile @@ -3,7 +3,8 @@ cln-grpc-wrongdir: CLN_GRPC_EXAMPLES := CLN_GRPC_GENALL = cln-grpc/proto/node.proto \ - cln-grpc/src/convert.rs + cln-grpc/src/convert.rs \ + cln-grpc/src/server.rs DEFAULT_TARGETS += $(CLN_GRPC_EXAMPLES) $(CLN_GRPC_GENALL) diff --git a/cln-grpc/src/server.rs b/cln-grpc/src/server.rs new file mode 100644 index 000000000..b1bb9d8c1 --- /dev/null +++ b/cln-grpc/src/server.rs @@ -0,0 +1,240 @@ +use crate::pb::node_server::Node; +use crate::pb; +use cln_rpc::{Request, Response, ClnRpc}; +use anyhow::Result; +use std::path::{Path, PathBuf}; +use cln_rpc::model::requests; +use log::debug; +use crate::convert::*; +use tonic::{Code, Status}; + +#[derive(Clone)] +pub struct Server +{ + rpc_path: PathBuf, +} + +impl Server +{ + pub async fn new(path: &Path) -> Result + { + Ok(Self { + rpc_path: path.to_path_buf(), + }) + } +} + +#[tonic::async_trait] +impl Node for Server +{ +async fn getinfo( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::GetinfoRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::Getinfo(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method Getinfo: {:?}", e)))?; + match result { + Response::Getinfo(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call Getinfo", + r + ) + )), + } + +} + +async fn list_funds( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::ListfundsRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::ListFunds(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method ListFunds: {:?}", e)))?; + match result { + Response::ListFunds(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call ListFunds", + r + ) + )), + } + +} + +async fn list_channels( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::ListchannelsRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::ListChannels(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method ListChannels: {:?}", e)))?; + match result { + Response::ListChannels(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call ListChannels", + r + ) + )), + } + +} + +async fn add_gossip( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::AddgossipRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::AddGossip(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method AddGossip: {:?}", e)))?; + match result { + Response::AddGossip(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call AddGossip", + r + ) + )), + } + +} + +async fn auto_clean_invoice( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::AutocleaninvoiceRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::AutoCleanInvoice(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method AutoCleanInvoice: {:?}", e)))?; + match result { + Response::AutoCleanInvoice(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call AutoCleanInvoice", + r + ) + )), + } + +} + +async fn check_message( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::CheckmessageRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::CheckMessage(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method CheckMessage: {:?}", e)))?; + match result { + Response::CheckMessage(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call CheckMessage", + r + ) + )), + } + +} + +async fn close( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::CloseRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::Close(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method Close: {:?}", e)))?; + match result { + Response::Close(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call Close", + r + ) + )), + } + +} + +} diff --git a/contrib/msggen/msggen/__main__.py b/contrib/msggen/msggen/__main__.py index 98747a18b..df4addea0 100644 --- a/contrib/msggen/msggen/__main__.py +++ b/contrib/msggen/msggen/__main__.py @@ -1,5 +1,5 @@ from msggen.model import Method, CompositeField, Service -from msggen.grpc import GrpcGenerator, GrpcConverterGenerator, GrpcUnconverterGenerator +from msggen.grpc import GrpcGenerator, GrpcConverterGenerator, GrpcUnconverterGenerator, GrpcServerGenerator from msggen.rust import RustGenerator from pathlib import Path import subprocess @@ -138,6 +138,10 @@ def gengrpc(service): GrpcConverterGenerator(dest).generate(service) GrpcUnconverterGenerator(dest).generate(service) + fname = repo_root() / "cln-grpc" / "src" / "server.rs" + dest = open(fname, "w") + GrpcServerGenerator(dest).generate(service) + def genrustjsonrpc(service): fname = repo_root() / "cln-rpc" / "src" / "model.rs" diff --git a/contrib/msggen/msggen/grpc.py b/contrib/msggen/msggen/grpc.py index aed92378b..07347c3d7 100644 --- a/contrib/msggen/msggen/grpc.py +++ b/contrib/msggen/msggen/grpc.py @@ -304,3 +304,75 @@ class GrpcUnconverterGenerator(GrpcConverterGenerator): """) + +class GrpcServerGenerator(GrpcConverterGenerator): + def generate(self, service: Service) -> None: + self.write(f"""\ + use crate::pb::node_server::Node; + use crate::pb; + use cln_rpc::{{Request, Response, ClnRpc}}; + use anyhow::Result; + use std::path::{{Path, PathBuf}}; + use cln_rpc::model::requests; + use log::debug; + use crate::convert::*; + use tonic::{{Code, Status}}; + + #[derive(Clone)] + pub struct Server + {{ + rpc_path: PathBuf, + }} + + impl Server + {{ + pub async fn new(path: &Path) -> Result + {{ + Ok(Self {{ + rpc_path: path.to_path_buf(), + }}) + }} + }} + + #[tonic::async_trait] + impl Node for Server + {{ + """) + + for method in service.methods: + # Tonic will convert to snake-case, so we have to do it here too + name = re.sub(r'(?, + ) -> Result, tonic::Status> {{ + let req = request.into_inner(); + let req: requests::{method.request.typename} = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::{method.name}(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method {method.name}: {{:?}}", e)))?; + match result {{ + Response::{method.name}(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {{:?}} to method call {method.name}", + r + ) + )), + }} + + }}\n\n""", numindent=0) + + self.write(f"""\ + }} + """, numindent=0)