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.
This commit is contained in:
Christian Decker 2022-01-16 16:38:21 +01:00
parent 5d6e9d6dae
commit 62dc078271
4 changed files with 319 additions and 2 deletions

View file

@ -3,7 +3,8 @@ cln-grpc-wrongdir:
CLN_GRPC_EXAMPLES := CLN_GRPC_EXAMPLES :=
CLN_GRPC_GENALL = cln-grpc/proto/node.proto \ 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) DEFAULT_TARGETS += $(CLN_GRPC_EXAMPLES) $(CLN_GRPC_GENALL)

240
cln-grpc/src/server.rs Normal file
View file

@ -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<Self>
{
Ok(Self {
rpc_path: path.to_path_buf(),
})
}
}
#[tonic::async_trait]
impl Node for Server
{
async fn getinfo(
&self,
request: tonic::Request<pb::GetinfoRequest>,
) -> Result<tonic::Response<pb::GetinfoResponse>, 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<pb::ListfundsRequest>,
) -> Result<tonic::Response<pb::ListfundsResponse>, 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<pb::ListchannelsRequest>,
) -> Result<tonic::Response<pb::ListchannelsResponse>, 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<pb::AddgossipRequest>,
) -> Result<tonic::Response<pb::AddgossipResponse>, 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<pb::AutocleaninvoiceRequest>,
) -> Result<tonic::Response<pb::AutocleaninvoiceResponse>, 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<pb::CheckmessageRequest>,
) -> Result<tonic::Response<pb::CheckmessageResponse>, 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<pb::CloseRequest>,
) -> Result<tonic::Response<pb::CloseResponse>, 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
)
)),
}
}
}

View file

@ -1,5 +1,5 @@
from msggen.model import Method, CompositeField, Service 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 msggen.rust import RustGenerator
from pathlib import Path from pathlib import Path
import subprocess import subprocess
@ -138,6 +138,10 @@ def gengrpc(service):
GrpcConverterGenerator(dest).generate(service) GrpcConverterGenerator(dest).generate(service)
GrpcUnconverterGenerator(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): def genrustjsonrpc(service):
fname = repo_root() / "cln-rpc" / "src" / "model.rs" fname = repo_root() / "cln-rpc" / "src" / "model.rs"

View file

@ -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<Self>
{{
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'(?<!^)(?=[A-Z])', '_', method.name).lower()
self.write(f"""\
async fn {name}(
&self,
request: tonic::Request<pb::{method.request.typename}>,
) -> Result<tonic::Response<pb::{method.response.typename}>, 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)