From 8b391fb08caf5f44c569414d0dd68b74e9ad36c5 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 17 Jan 2022 16:53:52 +0100 Subject: [PATCH 01/15] cln-grpc-plugin: Add scaffolding for the cln-grpc-plugin --- Cargo.toml | 1 + plugins/Makefile | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index b597a2cef789..d7161c019400 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,4 +3,5 @@ members = [ "cln-rpc", "cln-grpc", "plugins", + "plugins/grpc-plugin", ] diff --git a/plugins/Makefile b/plugins/Makefile index 70302311bd42..e3cacde4a498 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -179,8 +179,11 @@ CLN_PLUGIN_SRC = $(shell find plugins/src -name "*.rs") ${CLN_PLUGIN_EXAMPLES}: ${CLN_PLUGIN_SRC} (cd plugins; cargo build ${CARGO_OPTS} --examples) +target/${RUST_PROFILE}/grpc-plugin: ${CLN_PLUGIN_SRC} + cargo build ${CARGO_OPTS} --bin grpc-plugin + ifneq ($(RUST),0) -DEFAULT_TARGETS += $(CLN_PLUGIN_EXAMPLES) +DEFAULT_TARGETS += $(CLN_PLUGIN_EXAMPLES) target/${RUST_PROFILE}/grpc-plugin endif include plugins/test/Makefile From eb067cf8952bc315758375afba76b41d54f33517 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 17 Jan 2022 17:52:45 +0100 Subject: [PATCH 02/15] make: Add a hook for us to depend on generated files for tests We are about to generate the python grpc bindings, but only when we have Rust enabled. --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 6f541df029bc..67b6fd3d89c1 100644 --- a/Makefile +++ b/Makefile @@ -227,6 +227,7 @@ WIRE_GEN_DEPS := $(WIRE_GEN) $(wildcard tools/gen/*_template) # These are filled by individual Makefiles ALL_PROGRAMS := ALL_TEST_PROGRAMS := +ALL_TEST_GEN := ALL_FUZZ_TARGETS := ALL_C_SOURCES := ALL_C_HEADERS := header_versions_gen.h version_gen.h @@ -424,7 +425,7 @@ else endif endif -pytest: $(ALL_PROGRAMS) $(DEFAULT_TARGETS) $(ALL_TEST_PROGRAMS) +pytest: $(ALL_PROGRAMS) $(DEFAULT_TARGETS) $(ALL_TEST_PROGRAMS) $(ALL_TEST_GEN) ifeq ($(PYTEST),) @echo "py.test is required to run the integration tests, please install using 'pip3 install -r requirements.txt', and rerun 'configure'." exit 1 From fffa0af640a8241f6a93d0e8a109b0ca03b656af Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 17 Jan 2022 17:54:08 +0100 Subject: [PATCH 03/15] make: Generate grpc bindings if we want to test with rust enabled --- Makefile | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Makefile b/Makefile index 67b6fd3d89c1..778b532816de 100644 --- a/Makefile +++ b/Makefile @@ -359,6 +359,17 @@ endif ifneq ($(RUST),0) include cln-rpc/Makefile include cln-grpc/Makefile + +GRPC_GEN = tests/node_pb2.py \ + tests/node_pb2_grpc.py \ + tests/primitives_pb2.py + +ALL_TEST_GEN += $(GRPC_GEN) + +$(GRPC_GEN): cln-grpc/proto/node.proto cln-grpc/proto/primitives.proto + python -m grpc_tools.protoc -I cln-grpc/proto cln-grpc/proto/node.proto --python_out=tests/ --grpc_python_out=tests/ --experimental_allow_proto3_optional + python -m grpc_tools.protoc -I cln-grpc/proto cln-grpc/proto/primitives.proto --python_out=tests/ --grpc_python_out=tests/ --experimental_allow_proto3_optional + endif # We make pretty much everything depend on these. From 14cd5e42ec861a9696ad844905d418a8ea50eaa1 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 20 Jan 2022 13:44:28 +0100 Subject: [PATCH 04/15] cln-grpc-plugin: Add basic grpc-plugin --- plugins/grpc-plugin/Cargo.toml | 27 +++++++++++++++++ plugins/grpc-plugin/src/main.rs | 52 +++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 plugins/grpc-plugin/Cargo.toml create mode 100644 plugins/grpc-plugin/src/main.rs diff --git a/plugins/grpc-plugin/Cargo.toml b/plugins/grpc-plugin/Cargo.toml new file mode 100644 index 000000000000..21e56727d2f9 --- /dev/null +++ b/plugins/grpc-plugin/Cargo.toml @@ -0,0 +1,27 @@ +[package] +edition = "2021" +name = "grpc-plugin" +version = "0.1.0" + +[dependencies] +anyhow = "1.0" +log = "0.4" +prost = "0.8" +rcgen = { version = "0.8", features = ["pem", "x509-parser"] } + +[dependencies.cln-grpc] +path = "../../cln-grpc" + +[dependencies.cln-plugin] +path = "../../plugins" + +[dependencies.cln-rpc] +path = "../../cln-rpc" + +[dependencies.tokio] +features = ["net", "rt-multi-thread"] +version = "1" + +[dependencies.tonic] +features = ["tls", "transport"] +version = "^0.5" diff --git a/plugins/grpc-plugin/src/main.rs b/plugins/grpc-plugin/src/main.rs new file mode 100644 index 000000000000..c5028a02ae9d --- /dev/null +++ b/plugins/grpc-plugin/src/main.rs @@ -0,0 +1,52 @@ +use anyhow::{Context, Result}; +use cln_grpc::pb::node_server::NodeServer; +use cln_plugin::Builder; +use log::{debug, warn}; +use std::net::SocketAddr; +use std::path::{Path, PathBuf}; + +#[derive(Clone, Debug)] +struct PluginState { + rpc_path: PathBuf, + bind_address: SocketAddr, +} + +#[tokio::main] +async fn main() -> Result<()> { + debug!("Starting grpc plugin"); + let path = Path::new("lightning-rpc"); + let addr: SocketAddr = "0.0.0.0:50051".parse().unwrap(); + + let state = PluginState { + rpc_path: path.into(), + bind_address: addr, + }; + + let (plugin, i) = Builder::new(state.clone(), tokio::io::stdin(), tokio::io::stdout()).build(); + + tokio::spawn(async move { + if let Err(e) = run_interface(state).await { + warn!("Error running the grpc interface: {}", e); + } + }); + + plugin.join().await +} + +async fn run_interface(state: PluginState) -> Result<()> { + debug!( + "Connecting to {:?} and serving grpc on {:?}", + &state.rpc_path, &state.bind_address + ); + tonic::transport::Server::builder() + .add_service(NodeServer::new( + cln_grpc::Server::new(&state.rpc_path) + .await + .context("creating NodeServer instance")?, + )) + .serve(state.bind_address) + .await + .context("serving requests")?; + + Ok(()) +} From acff48e720aa05f3a09d79bc57132c0961c566b1 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 18 Jan 2022 10:44:20 +0100 Subject: [PATCH 05/15] pytest: Add a test for the grpc plugin Currently still unencrypted, but will get its mTLS authentication in the next commits. --- tests/test_cln_rs.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/test_cln_rs.py b/tests/test_cln_rs.py index 5c2d891089fc..acdde6a76bcd 100644 --- a/tests/test_cln_rs.py +++ b/tests/test_cln_rs.py @@ -1,9 +1,11 @@ from fixtures import * # noqa: F401,F403 +from node_pb2_grpc import NodeStub from pathlib import Path from pyln.testing.utils import env, TEST_NETWORK -import subprocess +import grpc +import node_pb2 as nodepb import pytest - +import subprocess # Skip the entire module if we don't have Rust. pytestmark = pytest.mark.skipif( @@ -27,6 +29,10 @@ def test_plugin_start(node_factory): l1 = node_factory.get_node(options={"plugin": str(bin_path), 'test-option': 31337}) l2 = node_factory.get_node() + # The plugin should be in the list of active plugins + plugins = l1.rpc.plugin('list')['plugins'] + assert len([p for p in plugins if 'cln-plugin-startup' in p['name'] and p['active']]) == 1 + cfg = l1.rpc.listconfigs() p = cfg['plugins'][0] p['path'] = None # The path is host-specific, so blank it. From ca1aa3a2158635e1677240ce0ad658c752c22a55 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 20 Jan 2022 15:09:21 +0100 Subject: [PATCH 06/15] grpc-plugin: Generate mTLS certificates and use them in grpc --- plugins/grpc-plugin/src/main.rs | 19 ++++++ plugins/grpc-plugin/src/tls.rs | 107 ++++++++++++++++++++++++++++++++ tests/test_cln_rs.py | 62 ++++++++++++++++++ 3 files changed, 188 insertions(+) create mode 100644 plugins/grpc-plugin/src/tls.rs diff --git a/plugins/grpc-plugin/src/main.rs b/plugins/grpc-plugin/src/main.rs index c5028a02ae9d..12031606fcbf 100644 --- a/plugins/grpc-plugin/src/main.rs +++ b/plugins/grpc-plugin/src/main.rs @@ -5,10 +5,14 @@ use log::{debug, warn}; use std::net::SocketAddr; use std::path::{Path, PathBuf}; +mod tls; + #[derive(Clone, Debug)] struct PluginState { rpc_path: PathBuf, bind_address: SocketAddr, + identity: tls::Identity, + ca_cert: Vec, } #[tokio::main] @@ -17,9 +21,14 @@ async fn main() -> Result<()> { let path = Path::new("lightning-rpc"); let addr: SocketAddr = "0.0.0.0:50051".parse().unwrap(); + let directory = std::env::current_dir()?; + let (identity, ca_cert) = tls::init(&directory)?; + let state = PluginState { rpc_path: path.into(), bind_address: addr, + identity, + ca_cert, }; let (plugin, i) = Builder::new(state.clone(), tokio::io::stdin(), tokio::io::stdout()).build(); @@ -38,7 +47,17 @@ async fn run_interface(state: PluginState) -> Result<()> { "Connecting to {:?} and serving grpc on {:?}", &state.rpc_path, &state.bind_address ); + + let identity = state.identity.to_tonic_identity(); + let ca_cert = tonic::transport::Certificate::from_pem(state.ca_cert); + + let tls = tonic::transport::ServerTlsConfig::new() + .identity(identity) + .client_ca_root(ca_cert); + tonic::transport::Server::builder() + .tls_config(tls) + .context("configuring tls")? .add_service(NodeServer::new( cln_grpc::Server::new(&state.rpc_path) .await diff --git a/plugins/grpc-plugin/src/tls.rs b/plugins/grpc-plugin/src/tls.rs new file mode 100644 index 000000000000..28a2972f7737 --- /dev/null +++ b/plugins/grpc-plugin/src/tls.rs @@ -0,0 +1,107 @@ +//! Utilities to manage TLS certificates. +use anyhow::{Context, Result}; +use log::debug; +use rcgen::{Certificate, KeyPair}; +use std::path::Path; + +/// Just a wrapper around a certificate and an associated keypair. +#[derive(Clone, Debug)] +pub(crate) struct Identity { + key: Vec, + certificate: Vec, +} + +impl Identity { + fn to_certificate(&self) -> Result { + let keystr = String::from_utf8_lossy(&self.key); + let key = KeyPair::from_pem(&keystr)?; + let certstr = String::from_utf8_lossy(&self.certificate); + let params = rcgen::CertificateParams::from_ca_cert_pem(&certstr, key)?; + let cert = Certificate::from_params(params)?; + Ok(cert) + } + + pub fn to_tonic_identity(&self) -> tonic::transport::Identity { + tonic::transport::Identity::from_pem(&self.certificate, &self.key) + } +} + +/// Ensure that we have a certificate authority, and child keypairs +/// and certificates for the server and the client. It'll generate +/// them in the provided `directory`. The following files are +/// included: +/// +/// - `ca.pem`: The self-signed certificate of the CA +/// - `ca-key.pem`: The key used by the CA to sign certificates +/// - `server.pem`: The server certificate, signed by the CA +/// - `server-key.pem`: The server private key +/// - `client.pem`: The client certificate, signed by the CA +/// - `client-key.pem`: The client private key +/// +/// The `grpc-plugin` will use the `server.pem` certificate, while a +/// client is supposed to use the `client.pem` and associated +/// keys. Notice that this isn't strictly necessary since the server +/// will accept any client that is signed by the CA. In future we +/// might add runes, making the distinction more important. +/// +/// Returns the server identity and the root CA certificate. +pub(crate) fn init(directory: &Path) -> Result<(Identity, Vec)> { + let ca = generate_or_load_identity("cln Root CA", directory, "ca", None)?; + let server = generate_or_load_identity("cln grpc Server", directory, "server", Some(&ca))?; + let _client = generate_or_load_identity("cln grpc Client", directory, "client", Some(&ca))?; + Ok((server, ca.certificate)) +} + +/// Generate a given identity +fn generate_or_load_identity( + name: &str, + directory: &Path, + filename: &str, + parent: Option<&Identity>, +) -> Result { + // Just our naming convention here. + let cert_path = directory.join(format!("{}.pem", filename)); + let key_path = directory.join(format!("{}-key.pem", filename)); + // Did we have to generate a new key? In that case we also need to + // regenerate the certificate + if !key_path.exists() || !cert_path.exists() { + debug!( + "Generating a new keypair in {:?}, it didn't exist", + &key_path + ); + let keypair = KeyPair::generate(&rcgen::PKCS_ECDSA_P256_SHA256)?; + std::fs::write(&key_path, keypair.serialize_pem())?; + debug!( + "Generating a new certificate for key {:?} at {:?}", + &key_path, &cert_path + ); + + // Configure the certificate we want. + let subject_alt_names = vec!["cln".to_string(), "localhost".to_string()]; + let mut params = rcgen::CertificateParams::new(subject_alt_names); + params.key_pair = Some(keypair); + params.alg = &rcgen::PKCS_ECDSA_P256_SHA256; + if parent.is_none() { + params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained); + } else { + params.is_ca = rcgen::IsCa::SelfSignedOnly; + } + params + .distinguished_name + .push(rcgen::DnType::CommonName, name); + + let cert = Certificate::from_params(params)?; + std::fs::write( + &cert_path, + match parent { + None => cert.serialize_pem()?, + Some(ca) => cert.serialize_pem_with_signer(&ca.to_certificate()?)?, + }, + ) + .context("writing certificate to file")?; + } + + let key = std::fs::read(&key_path)?; + let certificate = std::fs::read(cert_path)?; + Ok(Identity { certificate, key }) +} diff --git a/tests/test_cln_rs.py b/tests/test_cln_rs.py index acdde6a76bcd..c55d2fe5452e 100644 --- a/tests/test_cln_rs.py +++ b/tests/test_cln_rs.py @@ -63,3 +63,65 @@ def test_plugin_start(node_factory): l1.connect(l2) l1.daemon.wait_for_log(r'Got a connect hook call') l1.daemon.wait_for_log(r'Got a connect notification') + + +def test_grpc_connect(node_factory): + """Attempts to connect to the grpc interface and call getinfo""" + bin_path = Path.cwd() / "target" / "debug" / "grpc-plugin" + l1 = node_factory.get_node(options={"plugin": str(bin_path)}) + + p = Path(l1.daemon.lightning_dir) / TEST_NETWORK + cert_path = p / "client.pem" + key_path = p / "client-key.pem" + ca_cert_path = p / "ca.pem" + creds = grpc.ssl_channel_credentials( + root_certificates=ca_cert_path.open('rb').read(), + private_key=key_path.open('rb').read(), + certificate_chain=cert_path.open('rb').read() + ) + + channel = grpc.secure_channel( + "localhost:50051", + creds, + options=(('grpc.ssl_target_name_override', 'cln'),) + ) + stub = NodeStub(channel) + + response = stub.Getinfo(nodepb.GetinfoRequest()) + print(response) + + response = stub.ListFunds(nodepb.ListfundsRequest()) + print(response) + + +def test_grpc_generate_certificate(node_factory): + """Test whether we correctly generate the certificates. + + - If we have no certs, we need to generate them all + - If we have certs, we they should just get loaded + - If we delete one cert or its key it should get regenerated. + """ + bin_path = Path.cwd() / "target" / "debug" / "grpc-plugin" + l1 = node_factory.get_node(options={ + "plugin": str(bin_path), + }, start=False) + + p = Path(l1.daemon.lightning_dir) / TEST_NETWORK + files = [p / f for f in ['ca.pem', 'ca-key.pem', 'client.pem', 'client-key.pem', 'server-key.pem', 'server.pem']] + + # Before starting no files exist. + assert [f.exists() for f in files] == [False] * len(files) + + l1.start() + assert [f.exists() for f in files] == [True] * len(files) + + # The files exist, restarting should not change them + contents = [f.open().read() for f in files] + l1.restart() + assert contents == [f.open().read() for f in files] + + # Now we delete the last file, we should regenerate it as well as its key + files[-1].unlink() + l1.restart() + assert contents[-2] != files[-2].open().read() + assert contents[-1] != files[-1].open().read() From 3c42c0a3d411ee72ca83ba111d70ab358af5c3c8 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 20 Jan 2022 15:21:07 +0100 Subject: [PATCH 07/15] pytest: Add a test for incorrect credentials If we aren't using the correct certificates we should reject the connections during the mTLS connection setup. This test tries to connect with the wrong client cert to the node, and the server will reject it. --- tests/test_cln_rs.py | 57 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/tests/test_cln_rs.py b/tests/test_cln_rs.py index c55d2fe5452e..2d8581b78da6 100644 --- a/tests/test_cln_rs.py +++ b/tests/test_cln_rs.py @@ -107,7 +107,14 @@ def test_grpc_generate_certificate(node_factory): }, start=False) p = Path(l1.daemon.lightning_dir) / TEST_NETWORK - files = [p / f for f in ['ca.pem', 'ca-key.pem', 'client.pem', 'client-key.pem', 'server-key.pem', 'server.pem']] + files = [p / f for f in [ + 'ca.pem', + 'ca-key.pem', + 'client.pem', + 'client-key.pem', + 'server-key.pem', + 'server.pem', + ]] # Before starting no files exist. assert [f.exists() for f in files] == [False] * len(files) @@ -125,3 +132,51 @@ def test_grpc_generate_certificate(node_factory): l1.restart() assert contents[-2] != files[-2].open().read() assert contents[-1] != files[-1].open().read() + + +def test_grpc_wrong_auth(node_factory): + """An mTLS client certificate should only be usable with its node + + We create two instances, each generates its own certs and keys, + and then we try to cross the wires. + """ + bin_path = Path.cwd() / "target" / "debug" / "grpc-plugin" + l1, l2 = node_factory.get_nodes(2, opts={"plugin": str(bin_path), "start": False}) + l1.start() + l1.daemon.wait_for_log(r'serving grpc on 0.0.0.0:') + + def connect(node): + p = Path(node.daemon.lightning_dir) / TEST_NETWORK + cert, key, ca = [f.open('rb').read() for f in [ + p / 'client.pem', + p / 'client-key.pem', + p / "ca.pem"]] + + creds = grpc.ssl_channel_credentials( + root_certificates=ca, + private_key=key, + certificate_chain=cert, + ) + + channel = grpc.secure_channel( + "localhost:50051", + creds, + options=(('grpc.ssl_target_name_override', 'cln'),) + ) + return NodeStub(channel) + + stub = connect(l1) + # This should work, it's the correct node + stub.Getinfo(nodepb.GetinfoRequest()) + + l1.stop() + l2.start() + l2.daemon.wait_for_log(r'serving grpc on 0.0.0.0:') + + # This should not work, it's a different node + with pytest.raises(Exception, match=r'Socket closed|StatusCode.UNAVAILABLE'): + stub.Getinfo(nodepb.GetinfoRequest()) + + # Now load the correct ones and we should be good to go + stub = connect(l2) + stub.Getinfo(nodepb.GetinfoRequest()) From 1bd6950b1e7cf13f84f6532e4a009612055e33ea Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 26 Jan 2022 18:42:49 +0100 Subject: [PATCH 08/15] cln-grpc: Add the `connect` method --- cln-grpc/proto/node.proto | 33 +++++++++++++++++++ cln-grpc/src/convert.rs | 22 +++++++++++++ cln-grpc/src/server.rs | 30 +++++++++++++++++ cln-rpc/src/model.rs | 55 +++++++++++++++++++++++++++++++ contrib/msggen/msggen/__main__.py | 10 ++++-- doc/schemas/connect.request.json | 21 ++++++++++++ 6 files changed, 169 insertions(+), 2 deletions(-) create mode 100644 doc/schemas/connect.request.json diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index b834c9c34329..d03b924275c8 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -16,6 +16,7 @@ service Node { rpc AutoCleanInvoice(AutocleaninvoiceRequest) returns (AutocleaninvoiceResponse) {} rpc CheckMessage(CheckmessageRequest) returns (CheckmessageResponse) {} rpc Close(CloseRequest) returns (CloseResponse) {} + rpc ConnectPeer(ConnectRequest) returns (ConnectResponse) {} } message GetinfoRequest { @@ -339,3 +340,35 @@ message CloseResponse { optional bytes tx = 8; optional bytes txid = 9; } + +message ConnectRequest { + bytes id = 1; + optional string host = 2; + optional uint32 port = 3; +} + +message ConnectResponse { + // Connect.direction + enum ConnectDirection { + IN = 0; + OUT = 1; + } + bytes id = 1; + bytes features = 2; + ConnectDirection direction = 3; +} + +message ConnectAddress { + // Connect.address.type + enum ConnectAddressType { + LOCAL_SOCKET = 0; + IPV4 = 1; + IPV6 = 2; + TORV2 = 3; + TORV3 = 4; + } + ConnectAddressType item_type = 1; + optional string socket = 2; + optional string address = 3; + optional uint32 port = 4; +} diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index 5dbfd54eef97..0c2d78a2657a 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -289,6 +289,17 @@ impl From<&responses::CloseResponse> for pb::CloseResponse { } } +#[allow(unused_variables)] +impl From<&responses::ConnectResponse> for pb::ConnectResponse { + fn from(c: &responses::ConnectResponse) -> Self { + Self { + id: hex::decode(&c.id).unwrap(), + features: hex::decode(&c.features).unwrap(), + direction: c.direction as i32, + } + } +} + #[allow(unused_variables)] impl From<&pb::GetinfoRequest> for requests::GetinfoRequest { fn from(c: &pb::GetinfoRequest) -> Self { @@ -371,3 +382,14 @@ impl From<&pb::CloseRequest> for requests::CloseRequest { } } +#[allow(unused_variables)] +impl From<&pb::ConnectRequest> for requests::ConnectRequest { + fn from(c: &pb::ConnectRequest) -> Self { + Self { + id: hex::encode(&c.id), + host: c.host.clone(), + port: c.port.map(|i| i as u16), + } + } +} + diff --git a/cln-grpc/src/server.rs b/cln-grpc/src/server.rs index 3576bac86436..f492ed660086 100644 --- a/cln-grpc/src/server.rs +++ b/cln-grpc/src/server.rs @@ -266,4 +266,34 @@ async fn close( } +async fn connect_peer( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::ConnectRequest = (&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::ConnectPeer(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method ConnectPeer: {:?}", e)))?; + match result { + Response::ConnectPeer(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call ConnectPeer", + r + ) + )), + } + +} + } diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index 3e6a0fc76f93..a9494af74607 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -24,6 +24,7 @@ pub enum Request { AutoCleanInvoice(requests::AutocleaninvoiceRequest), CheckMessage(requests::CheckmessageRequest), Close(requests::CloseRequest), + ConnectPeer(requests::ConnectRequest), } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -38,6 +39,7 @@ pub enum Response { AutoCleanInvoice(responses::AutocleaninvoiceResponse), CheckMessage(responses::CheckmessageResponse), Close(responses::CloseResponse), + ConnectPeer(responses::ConnectResponse), } pub mod requests { @@ -114,6 +116,16 @@ pub mod requests { pub force_lease_closed: Option, } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ConnectRequest { + #[serde(alias = "id")] + pub id: String, + #[serde(alias = "host", skip_serializing_if = "Option::is_none")] + pub host: Option, + #[serde(alias = "port", skip_serializing_if = "Option::is_none")] + pub port: Option, + } + } @@ -731,5 +743,48 @@ pub mod responses { pub txid: Option, } + /// Whether they initiated connection or we did + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[serde(rename_all = "lowercase")] + pub enum ConnectDirection { + IN, + OUT, + } + + /// Type of connection (*torv2*/*torv3* only if **direction** is *out*) + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[serde(rename_all = "lowercase")] + pub enum ConnectAddressType { + LOCAL_SOCKET, + IPV4, + IPV6, + TORV2, + TORV3, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ConnectAddress { + // Path `Connect.address.type` + #[serde(rename = "type")] + pub item_type: ConnectAddressType, + #[serde(alias = "socket", skip_serializing_if = "Option::is_none")] + pub socket: Option, + #[serde(alias = "address", skip_serializing_if = "Option::is_none")] + pub address: Option, + #[serde(alias = "port", skip_serializing_if = "Option::is_none")] + pub port: Option, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ConnectResponse { + #[serde(alias = "id")] + pub id: String, + #[serde(alias = "features")] + pub features: String, + // Path `Connect.direction` + #[serde(rename = "direction")] + pub direction: ConnectDirection, + } + } diff --git a/contrib/msggen/msggen/__main__.py b/contrib/msggen/msggen/__main__.py index 5700b9764a28..b98b27f83493 100644 --- a/contrib/msggen/msggen/__main__.py +++ b/contrib/msggen/msggen/__main__.py @@ -6,6 +6,12 @@ import json +# Sometimes we want to rename a method, due to a name clash +method_name_override = { + "Connect": "ConnectPeer", +} + + def repo_root(): path = subprocess.check_output(["git", "rev-parse", "--show-toplevel"]) return Path(path.strip().decode('UTF-8')) @@ -26,7 +32,7 @@ def load_jsonrpc_method(name): response.typename += "Response" return Method( - name=name, + name=method_name_override.get(name, name), request=request, response=response, ) @@ -44,7 +50,7 @@ def load_jsonrpc_service(): "CheckMessage", # "check", # No point in mapping this one "Close", - # "connect", + "Connect", # "createinvoice", # "createonion", # "datastore", diff --git a/doc/schemas/connect.request.json b/doc/schemas/connect.request.json new file mode 100644 index 000000000000..97c40185348f --- /dev/null +++ b/doc/schemas/connect.request.json @@ -0,0 +1,21 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "id" + ], + "properties": { + "id": { + "type": "pubkey", + "description": "" + }, + "host": { + "type": "string", + "description": "The hostname of the node." + }, + "port": { + "type": "u16", + "description": "Port to try connecting to" + } + } +} From d52bec37e4dc678e70a86c37ed841849bd21c29b Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 10 Feb 2022 16:22:46 +0100 Subject: [PATCH 09/15] cln: Add grpc port as configurable option --- plugins/grpc-plugin/src/main.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/plugins/grpc-plugin/src/main.rs b/plugins/grpc-plugin/src/main.rs index 12031606fcbf..f0cd4b1e3e64 100644 --- a/plugins/grpc-plugin/src/main.rs +++ b/plugins/grpc-plugin/src/main.rs @@ -1,6 +1,6 @@ use anyhow::{Context, Result}; use cln_grpc::pb::node_server::NodeServer; -use cln_plugin::Builder; +use cln_plugin::{options, Builder}; use log::{debug, warn}; use std::net::SocketAddr; use std::path::{Path, PathBuf}; @@ -31,7 +31,14 @@ async fn main() -> Result<()> { ca_cert, }; - let (plugin, i) = Builder::new(state.clone(), tokio::io::stdin(), tokio::io::stdout()).build(); + let plugin = Builder::new(state.clone(), tokio::io::stdin(), tokio::io::stdout()) + .option(options::ConfigOption::new( + "grpc-port", + options::Value::Integer(29735), + "Which port should the grpc plugin listen for incoming connections?", + )) + .start() + .await?; tokio::spawn(async move { if let Err(e) = run_interface(state).await { From 6e65cf0f4a8ebd883e906ab21e0b6744b533a94a Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 9 Mar 2022 17:30:36 +0100 Subject: [PATCH 10/15] grpc-plugin: Make the grpc port to listen on configurable Changelog-Added: cln-grpc-plugin: The plugin can be configured to listen on a specific port using the `grpc-port` option --- plugins/grpc-plugin/src/main.rs | 36 ++++++++++++++++++--------------- plugins/src/lib.rs | 13 ++++++++++++ tests/test_cln_rs.py | 18 +++++++++++++---- 3 files changed, 47 insertions(+), 20 deletions(-) diff --git a/plugins/grpc-plugin/src/main.rs b/plugins/grpc-plugin/src/main.rs index f0cd4b1e3e64..8636fc5adec0 100644 --- a/plugins/grpc-plugin/src/main.rs +++ b/plugins/grpc-plugin/src/main.rs @@ -1,4 +1,4 @@ -use anyhow::{Context, Result}; +use anyhow::{anyhow, Context, Result}; use cln_grpc::pb::node_server::NodeServer; use cln_plugin::{options, Builder}; use log::{debug, warn}; @@ -10,7 +10,6 @@ mod tls; #[derive(Clone, Debug)] struct PluginState { rpc_path: PathBuf, - bind_address: SocketAddr, identity: tls::Identity, ca_cert: Vec, } @@ -19,14 +18,12 @@ struct PluginState { async fn main() -> Result<()> { debug!("Starting grpc plugin"); let path = Path::new("lightning-rpc"); - let addr: SocketAddr = "0.0.0.0:50051".parse().unwrap(); let directory = std::env::current_dir()?; let (identity, ca_cert) = tls::init(&directory)?; let state = PluginState { rpc_path: path.into(), - bind_address: addr, identity, ca_cert, }; @@ -34,14 +31,21 @@ async fn main() -> Result<()> { let plugin = Builder::new(state.clone(), tokio::io::stdin(), tokio::io::stdout()) .option(options::ConfigOption::new( "grpc-port", - options::Value::Integer(29735), + options::Value::Integer(50051), "Which port should the grpc plugin listen for incoming connections?", )) .start() .await?; + let bind_port = match plugin.option("grpc-port") { + Some(options::Value::Integer(i)) => i, + None => return Err(anyhow!("Missing 'grpc-port' option")), + Some(o) => return Err(anyhow!("grpc-port is not a valid integer: {:?}", o)), + }; + let bind_addr: SocketAddr = format!("0.0.0.0:{}", bind_port).parse().unwrap(); + tokio::spawn(async move { - if let Err(e) = run_interface(state).await { + if let Err(e) = run_interface(bind_addr, state).await { warn!("Error running the grpc interface: {}", e); } }); @@ -49,12 +53,7 @@ async fn main() -> Result<()> { plugin.join().await } -async fn run_interface(state: PluginState) -> Result<()> { - debug!( - "Connecting to {:?} and serving grpc on {:?}", - &state.rpc_path, &state.bind_address - ); - +async fn run_interface(bind_addr: SocketAddr, state: PluginState) -> Result<()> { let identity = state.identity.to_tonic_identity(); let ca_cert = tonic::transport::Certificate::from_pem(state.ca_cert); @@ -62,7 +61,7 @@ async fn run_interface(state: PluginState) -> Result<()> { .identity(identity) .client_ca_root(ca_cert); - tonic::transport::Server::builder() + let server = tonic::transport::Server::builder() .tls_config(tls) .context("configuring tls")? .add_service(NodeServer::new( @@ -70,9 +69,14 @@ async fn run_interface(state: PluginState) -> Result<()> { .await .context("creating NodeServer instance")?, )) - .serve(state.bind_address) - .await - .context("serving requests")?; + .serve(bind_addr); + + debug!( + "Connecting to {:?} and serving grpc on {:?}", + &state.rpc_path, &bind_addr + ); + + server.await.context("serving requests")?; Ok(()) } diff --git a/plugins/src/lib.rs b/plugins/src/lib.rs index dfa996210f57..66630244bc7b 100644 --- a/plugins/src/lib.rs +++ b/plugins/src/lib.rs @@ -331,6 +331,19 @@ where sender: tokio::sync::mpsc::Sender, } +impl Plugin +where + S: Clone + Send, +{ + pub fn option(&self, name: &str) -> Option { + self.options + .iter() + .filter(|o| o.name() == name) + .next() + .map(|co| co.value.clone().unwrap_or(co.default().clone())) + } +} + /// The [PluginDriver] is used to run the IO loop, reading messages /// from the Lightning daemon, dispatching calls and notifications to /// the plugin, and returning responses to the the daemon. We also use diff --git a/tests/test_cln_rs.py b/tests/test_cln_rs.py index 2d8581b78da6..e2b0ef53d974 100644 --- a/tests/test_cln_rs.py +++ b/tests/test_cln_rs.py @@ -2,6 +2,7 @@ from node_pb2_grpc import NodeStub from pathlib import Path from pyln.testing.utils import env, TEST_NETWORK +from ephemeral_port_reserve import reserve import grpc import node_pb2 as nodepb import pytest @@ -67,8 +68,9 @@ def test_plugin_start(node_factory): def test_grpc_connect(node_factory): """Attempts to connect to the grpc interface and call getinfo""" + grpc_port = reserve() bin_path = Path.cwd() / "target" / "debug" / "grpc-plugin" - l1 = node_factory.get_node(options={"plugin": str(bin_path)}) + l1 = node_factory.get_node(options={"plugin": str(bin_path), "grpc-port": str(grpc_port)}) p = Path(l1.daemon.lightning_dir) / TEST_NETWORK cert_path = p / "client.pem" @@ -80,8 +82,9 @@ def test_grpc_connect(node_factory): certificate_chain=cert_path.open('rb').read() ) + l1.daemon.wait_for_log(r'serving grpc on 0.0.0.0:') channel = grpc.secure_channel( - "localhost:50051", + f"localhost:{grpc_port}", creds, options=(('grpc.ssl_target_name_override', 'cln'),) ) @@ -101,9 +104,11 @@ def test_grpc_generate_certificate(node_factory): - If we have certs, we they should just get loaded - If we delete one cert or its key it should get regenerated. """ + grpc_port = reserve() bin_path = Path.cwd() / "target" / "debug" / "grpc-plugin" l1 = node_factory.get_node(options={ "plugin": str(bin_path), + "grpc-port": str(grpc_port), }, start=False) p = Path(l1.daemon.lightning_dir) / TEST_NETWORK @@ -140,8 +145,13 @@ def test_grpc_wrong_auth(node_factory): We create two instances, each generates its own certs and keys, and then we try to cross the wires. """ + grpc_port = reserve() bin_path = Path.cwd() / "target" / "debug" / "grpc-plugin" - l1, l2 = node_factory.get_nodes(2, opts={"plugin": str(bin_path), "start": False}) + l1, l2 = node_factory.get_nodes(2, opts={ + "plugin": str(bin_path), + "start": False, + "grpc-port": str(grpc_port), + }) l1.start() l1.daemon.wait_for_log(r'serving grpc on 0.0.0.0:') @@ -159,7 +169,7 @@ def connect(node): ) channel = grpc.secure_channel( - "localhost:50051", + f"localhost:{grpc_port}", creds, options=(('grpc.ssl_target_name_override', 'cln'),) ) From bdfd01bf6706712a317baaeebe33876e37cc0c0e Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 10 Mar 2022 16:46:56 +0100 Subject: [PATCH 11/15] py: Add grpcio-tools for the cln-grpc-plugin --- poetry.lock | 158 ++++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 1 + 2 files changed, 157 insertions(+), 2 deletions(-) diff --git a/poetry.lock b/poetry.lock index f6e2cbe80066..b18f9962924c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -192,6 +192,32 @@ Werkzeug = ">=2.0" async = ["asgiref (>=3.2)"] dotenv = ["python-dotenv"] +[[package]] +name = "grpcio" +version = "1.44.0" +description = "HTTP/2-based RPC framework" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +six = ">=1.5.2" + +[package.extras] +protobuf = ["grpcio-tools (>=1.44.0)"] + +[[package]] +name = "grpcio-tools" +version = "1.44.0" +description = "Protobuf code generator for gRPC" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +grpcio = ">=1.44.0" +protobuf = ">=3.5.0.post1,<4.0dev" + [[package]] name = "importlib-metadata" version = "4.2.0" @@ -408,6 +434,14 @@ importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] +[[package]] +name = "protobuf" +version = "3.19.4" +description = "Protocol Buffers" +category = "main" +optional = false +python-versions = ">=3.5" + [[package]] name = "psutil" version = "5.9.0" @@ -660,7 +694,7 @@ python-versions = "*" name = "six" version = "1.16.0" description = "Python 2 and 3 compatibility utilities" -category = "dev" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" @@ -727,7 +761,7 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest- [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "25c2960761882dd99f4a0ed3394354054a45905d811d80f6984a56495df9ac22" +content-hash = "45e8b4302761ec3b83181b506daba46f587227fab83bf597bc0228da82c24eb0" [metadata.files] asn1crypto = [ @@ -956,6 +990,98 @@ flask = [ {file = "Flask-2.0.3-py3-none-any.whl", hash = "sha256:59da8a3170004800a2837844bfa84d49b022550616070f7cb1a659682b2e7c9f"}, {file = "Flask-2.0.3.tar.gz", hash = "sha256:e1120c228ca2f553b470df4a5fa927ab66258467526069981b3eb0a91902687d"}, ] +grpcio = [ + {file = "grpcio-1.44.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:11f811c0fffd84fca747fbc742464575e5eb130fd4fb4d6012ccc34febd001db"}, + {file = "grpcio-1.44.0-cp310-cp310-macosx_10_10_universal2.whl", hash = "sha256:9a86a91201f8345502ea81dee0a55ae13add5fafadf109b17acd858fe8239651"}, + {file = "grpcio-1.44.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:5f3c54ebb5d9633a557335c01d88d3d4928e9b1b131692283b6184da1edbec0b"}, + {file = "grpcio-1.44.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3d47553b8e86ab1e59b0185ba6491a187f94a0239f414c8fc867a22b0405b798"}, + {file = "grpcio-1.44.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1e22d3a510438b7f3365c0071b810672d09febac6e8ca8a47eab657ae5f347b"}, + {file = "grpcio-1.44.0-cp310-cp310-win32.whl", hash = "sha256:41036a574cab3468f24d41d6ed2b52588fb85ed60f8feaa925d7e424a250740b"}, + {file = "grpcio-1.44.0-cp310-cp310-win_amd64.whl", hash = "sha256:4ee51964edfd0a1293a95bb0d72d134ecf889379d90d2612cbf663623ce832b4"}, + {file = "grpcio-1.44.0-cp36-cp36m-linux_armv7l.whl", hash = "sha256:e2149077d71e060678130644670389ddf1491200bcea16c5560d4ccdc65e3f2e"}, + {file = "grpcio-1.44.0-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:0ac72d4b953b76924f8fa21436af060d7e6d8581e279863f30ee14f20751ac27"}, + {file = "grpcio-1.44.0-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:5c30a9a7d3a05920368a60b080cbbeaf06335303be23ac244034c71c03a0fd24"}, + {file = "grpcio-1.44.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:05467acd391e3fffb05991c76cb2ed2fa1309d0e3815ac379764bc5670b4b5d4"}, + {file = "grpcio-1.44.0-cp36-cp36m-manylinux_2_17_aarch64.whl", hash = "sha256:b81dc7894062ed2d25b74a2725aaa0a6895ce97ce854f432fe4e87cad5a07316"}, + {file = "grpcio-1.44.0-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:46d4843192e7d36278884282e100b8f305cf37d1b3d8c6b4f736d4454640a069"}, + {file = "grpcio-1.44.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:898c159148f27e23c08a337fb80d31ece6b76bb24f359d83929460d813665b74"}, + {file = "grpcio-1.44.0-cp36-cp36m-win32.whl", hash = "sha256:b8d852329336c584c636caa9c2db990f3a332b19bc86a80f4646b58d27c142db"}, + {file = "grpcio-1.44.0-cp36-cp36m-win_amd64.whl", hash = "sha256:790d7493337558ae168477d1be3178f4c9b8f91d8cd9b8b719d06fd9b2d48836"}, + {file = "grpcio-1.44.0-cp37-cp37m-linux_armv7l.whl", hash = "sha256:cd61b52d9cf8fcf8d9628c0b640b9e44fdc5e93d989cc268086a858540ed370c"}, + {file = "grpcio-1.44.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:14eefcf623890f3f7dd7831decd2a2116652b5ce1e0f1d4b464b8f52110743b0"}, + {file = "grpcio-1.44.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:bebe90b8020b4248e5a2076b56154cc6ff45691bbbe980579fc9db26717ac968"}, + {file = "grpcio-1.44.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:89b390b1c0de909965280d175c53128ce2f0f4f5c0f011382243dd7f2f894060"}, + {file = "grpcio-1.44.0-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:c122dac5cb299b8ad7308d61bd9fe0413de13b0347cce465398436b3fdf1f609"}, + {file = "grpcio-1.44.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6641a28cc826a92ef717201cca9a035c34a0185e38b0c93f3ce5f01a01a1570a"}, + {file = "grpcio-1.44.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb0a3e0e64843441793923d9532a3a23907b07b2a1e0a7a31f186dc185bb772"}, + {file = "grpcio-1.44.0-cp37-cp37m-win32.whl", hash = "sha256:be857b7ec2ac43455156e6ba89262f7d7ae60227049427d01a3fecd218a3f88d"}, + {file = "grpcio-1.44.0-cp37-cp37m-win_amd64.whl", hash = "sha256:f6a9cf0e77f72f2ac30c9c6e086bc7446c984c51bebc6c7f50fbcd718037edba"}, + {file = "grpcio-1.44.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:19e54f0c7083c8332b5a75a9081fc5127f1dbb67b6c1a32bd7fe896ef0934918"}, + {file = "grpcio-1.44.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:bfd36b959c3c4e945119387baed1414ea46f7116886aa23de0172302b49d7ff1"}, + {file = "grpcio-1.44.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:ccd388b8f37b19d06e4152189726ce309e36dc03b53f2216a4ea49f09a7438e6"}, + {file = "grpcio-1.44.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:9075c0c003c1ff14ebce8f0ba55cc692158cb55c68da09cf8b0f9fc5b749e343"}, + {file = "grpcio-1.44.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:e898194f76212facbaeb6d7545debff29351afa23b53ff8f0834d66611af5139"}, + {file = "grpcio-1.44.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8fa6584046a7cf281649975a363673fa5d9c6faf9dc923f261cc0e56713b5892"}, + {file = "grpcio-1.44.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36a7bdd6ef9bca050c7ade8cba5f0e743343ea0756d5d3d520e915098a9dc503"}, + {file = "grpcio-1.44.0-cp38-cp38-win32.whl", hash = "sha256:dc3290d0411ddd2bd49adba5793223de8de8b01588d45e9376f1a9f7d25414f4"}, + {file = "grpcio-1.44.0-cp38-cp38-win_amd64.whl", hash = "sha256:13343e7b840c20f43b44f0e6d3bbdc037c964f0aec9735d7cb685c407731c9ff"}, + {file = "grpcio-1.44.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:c5c2f8417d13386e18ccc8c61467cb6a6f9667a1ff7000a2d7d378e5d7df693f"}, + {file = "grpcio-1.44.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:cf220199b7b4992729ad4d55d5d3f652f4ccfe1a35b5eacdbecf189c245e1859"}, + {file = "grpcio-1.44.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4201c597e5057a9bfef9ea5777a6d83f6252cb78044db7d57d941ec2300734a5"}, + {file = "grpcio-1.44.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:e2de61005118ae59d48d5d749283ebfd1ba4ca68cc1000f8a395cd2bdcff7ceb"}, + {file = "grpcio-1.44.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:871078218fa9117e2a378678f327e32fda04e363ed6bc0477275444273255d4d"}, + {file = "grpcio-1.44.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a8d610b7b557a7609fecee80b6dd793ecb7a9a3c3497fbdce63ce7d151cdd705"}, + {file = "grpcio-1.44.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4fcb53e4eb8c271032c91b8981df5fc1bb974bc73e306ec2c27da41bd95c44b5"}, + {file = "grpcio-1.44.0-cp39-cp39-win32.whl", hash = "sha256:e50ddea6de76c09b656df4b5a55ae222e2a56e625c44250e501ff3c904113ec1"}, + {file = "grpcio-1.44.0-cp39-cp39-win_amd64.whl", hash = "sha256:d2ec124a986093e26420a5fb10fa3f02b2c232f924cdd7b844ddf7e846c020cd"}, + {file = "grpcio-1.44.0.tar.gz", hash = "sha256:4bae1c99896045d3062ab95478411c8d5a52cb84b91a1517312629fa6cfeb50e"}, +] +grpcio-tools = [ + {file = "grpcio-tools-1.44.0.tar.gz", hash = "sha256:be37f458ea510c9a8f1caabbc2b258d12e55d189a567f5edcace90f27dc0efbf"}, + {file = "grpcio_tools-1.44.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:9f58529e24f613019a85c258a274d441d89e0cad8cf7fca21ef3807ba5840c5d"}, + {file = "grpcio_tools-1.44.0-cp310-cp310-macosx_10_10_universal2.whl", hash = "sha256:1d120082236f8d2877f8a19366476b82c3562423b877b7c471a142432e31c2c4"}, + {file = "grpcio_tools-1.44.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:65c2fe3cdc5425180f01dd303e28d4f363d38f4c2e3a7e1a87caedd5417e23bb"}, + {file = "grpcio_tools-1.44.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5caef118deb8cdee1978fd3d8e388a9b256cd8d34e4a8895731ac0e86fa5e47c"}, + {file = "grpcio_tools-1.44.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:121c9765cee8636201cf0d4e80bc7b509813194919bccdb66e9671c4ece6dac3"}, + {file = "grpcio_tools-1.44.0-cp310-cp310-win32.whl", hash = "sha256:90d1fac188bac838c4169eb3b67197887fa0572ea8a90519a20cddb080800549"}, + {file = "grpcio_tools-1.44.0-cp310-cp310-win_amd64.whl", hash = "sha256:3e16260dfe6e997330473863e01466b0992369ae2337a0249b390b4651cff424"}, + {file = "grpcio_tools-1.44.0-cp36-cp36m-linux_armv7l.whl", hash = "sha256:608414cc1093e1e9e5980c97a6ee78e51dffff359e7a3f123d1fb9d95b8763a5"}, + {file = "grpcio_tools-1.44.0-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:395609c06f69fbc79518b30a01931127088a3f9ef2cc2a35269c5f187eefd38c"}, + {file = "grpcio_tools-1.44.0-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f7ce16766b24b88ec0e4355f5dd66c2eee6af210e889fcb7961c9c4634c687de"}, + {file = "grpcio_tools-1.44.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:3c9abc4a40c62f46d5e43e49c7afc567dedf12eeef95933ac9ea2986baa2420b"}, + {file = "grpcio_tools-1.44.0-cp36-cp36m-manylinux_2_17_aarch64.whl", hash = "sha256:b73fd87a44ba1b91866b0254193c37cdb001737759b77b637cebe0c816d38342"}, + {file = "grpcio_tools-1.44.0-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b211f12e4cbc0fde8e0f982b0f581cce38874666a02ebfed93c23dcaeb8a4e0"}, + {file = "grpcio_tools-1.44.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b421dc9b27bcaff4c73644cd3801e4893b11ba3eb39729246fd3de98d9f685b"}, + {file = "grpcio_tools-1.44.0-cp36-cp36m-win32.whl", hash = "sha256:33d93027840a873c7b59402fe6db8263b88c56e2f84aa0b6281c05cc8bd314a1"}, + {file = "grpcio_tools-1.44.0-cp36-cp36m-win_amd64.whl", hash = "sha256:71fb6e7e66b918803b1bebd0231560981ab86c2546a3318a45822ce94de5e83d"}, + {file = "grpcio_tools-1.44.0-cp37-cp37m-linux_armv7l.whl", hash = "sha256:614c427ff235d92f103e9189f0230197c8f2f817d0dd9fd078f5d2ea4d920d02"}, + {file = "grpcio_tools-1.44.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:c13e0cb486cfa15320ddcd70452a4d736e6ce319c03d6b3c0c2513ec8d2748fb"}, + {file = "grpcio_tools-1.44.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:5ade6b13dc4e148f400c8f55a6ef0b14216a3371d7a9e559571d5981b6cec36b"}, + {file = "grpcio_tools-1.44.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6138d2c7eec7ed57585bc58e2dbcb65635a2d574ac632abd29949d3e68936bab"}, + {file = "grpcio_tools-1.44.0-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:3d6c8548b199591757dbfe89ed14e23782d6079d6d201c6c314c72f4086883aa"}, + {file = "grpcio_tools-1.44.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b41c419829f01734d65958ba9b01b759061d8f7e0698f9612ba6b8837269f7a9"}, + {file = "grpcio_tools-1.44.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9f0c5b4567631fec993826e694e83d86a972b3e2e9b05cb0c56839b0316d26c"}, + {file = "grpcio_tools-1.44.0-cp37-cp37m-win32.whl", hash = "sha256:3f0e1d1f3f5a6f0c9f8b5441819dbec831ce7e9ffe04768e4b0d965a95fbbe5e"}, + {file = "grpcio_tools-1.44.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1f87fc86d0b4181b6b4da6ec6a29511dca000e6b5694fdd6bbf87d125128bc41"}, + {file = "grpcio_tools-1.44.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:cb8baa1d4cea35ca662c24098377bdd9514c56f227da0e38b43cd9b8223bfcc6"}, + {file = "grpcio_tools-1.44.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:ea36a294f7c70fd2f2bfb5dcf08602006304aa65b055ebd4f7c709e2a89deba7"}, + {file = "grpcio_tools-1.44.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:1972caf8f695b91edc6444134445798692fe71276f0cde7604d55e65179adf93"}, + {file = "grpcio_tools-1.44.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:674fb8d9c0e2d75166c4385753962485b757897223fc92a19c9e513ab80b96f7"}, + {file = "grpcio_tools-1.44.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:37045ba850d423cdacede77b266b127025818a5a36d80f1fd7a5a1614a6a0de5"}, + {file = "grpcio_tools-1.44.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6cdf72947c6b0b03aa6dac06117a095947d02d43a5c6343051f4ce161fd0abcb"}, + {file = "grpcio_tools-1.44.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69bfa6fc1515c202fe428ba9f99e2b2f947b01bafc15d868798235b2e2d36baa"}, + {file = "grpcio_tools-1.44.0-cp38-cp38-win32.whl", hash = "sha256:2c516124356476d9afa126acce10ce568733120afbd9ae17ee01d44b9da20a67"}, + {file = "grpcio_tools-1.44.0-cp38-cp38-win_amd64.whl", hash = "sha256:ceb6441c24176705c5ab056e65a8b330e107107c5a492ba094d1b862a136d15d"}, + {file = "grpcio_tools-1.44.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:398eda759194d355eb09f7beabae6e4fb45b3877cf7efe505b49095fa4889cef"}, + {file = "grpcio_tools-1.44.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:a169bfd7a1fe8cc11472eeeeab3088b3c5d56caac12b2192a920b73adcbc974c"}, + {file = "grpcio_tools-1.44.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:a58aaaec0d846d142edd8e794ebb80aa429abfd581f4493a60a603aac0c50ac8"}, + {file = "grpcio_tools-1.44.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:c3253bee8b68fe422754faf0f286aa068861c926a7b11e4daeb44b9af767c7f1"}, + {file = "grpcio_tools-1.44.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:3c0be60721ae1ba09c4f29572a145f412e561b9201e19428758893709827f472"}, + {file = "grpcio_tools-1.44.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e44b9572c2226b85976e0d6054e22d7c59ebd6c9425ee71e5bc8910434aee3e1"}, + {file = "grpcio_tools-1.44.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c04ec47905c4f6d6dad34d29f6ace652cc1ddc986f55aaa5559b72104c3f5cf"}, + {file = "grpcio_tools-1.44.0-cp39-cp39-win32.whl", hash = "sha256:fb8c7b9d24e2c4dc77e7800e83b68081729ac6094b781b2afdabf08af18c3b28"}, + {file = "grpcio_tools-1.44.0-cp39-cp39-win_amd64.whl", hash = "sha256:4eb93619c8cb3773fb899504e3e30a0dc79d3904fd7a84091d15552178e1e920"}, +] importlib-metadata = [ {file = "importlib_metadata-4.2.0-py3-none-any.whl", hash = "sha256:057e92c15bc8d9e8109738a48db0ccb31b4d9d5cfbee5a8670879a30be66304b"}, {file = "importlib_metadata-4.2.0.tar.gz", hash = "sha256:b7e52a1f8dec14a75ea73e0891f3060099ca1d8e6a462a4dff11c3e119ea1b31"}, @@ -1077,6 +1203,34 @@ pluggy = [ {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, ] +protobuf = [ + {file = "protobuf-3.19.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f51d5a9f137f7a2cec2d326a74b6e3fc79d635d69ffe1b036d39fc7d75430d37"}, + {file = "protobuf-3.19.4-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:09297b7972da685ce269ec52af761743714996b4381c085205914c41fcab59fb"}, + {file = "protobuf-3.19.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:072fbc78d705d3edc7ccac58a62c4c8e0cec856987da7df8aca86e647be4e35c"}, + {file = "protobuf-3.19.4-cp310-cp310-win32.whl", hash = "sha256:7bb03bc2873a2842e5ebb4801f5c7ff1bfbdf426f85d0172f7644fcda0671ae0"}, + {file = "protobuf-3.19.4-cp310-cp310-win_amd64.whl", hash = "sha256:f358aa33e03b7a84e0d91270a4d4d8f5df6921abe99a377828839e8ed0c04e07"}, + {file = "protobuf-3.19.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:1c91ef4110fdd2c590effb5dca8fdbdcb3bf563eece99287019c4204f53d81a4"}, + {file = "protobuf-3.19.4-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c438268eebb8cf039552897d78f402d734a404f1360592fef55297285f7f953f"}, + {file = "protobuf-3.19.4-cp36-cp36m-win32.whl", hash = "sha256:835a9c949dc193953c319603b2961c5c8f4327957fe23d914ca80d982665e8ee"}, + {file = "protobuf-3.19.4-cp36-cp36m-win_amd64.whl", hash = "sha256:4276cdec4447bd5015453e41bdc0c0c1234eda08420b7c9a18b8d647add51e4b"}, + {file = "protobuf-3.19.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6cbc312be5e71869d9d5ea25147cdf652a6781cf4d906497ca7690b7b9b5df13"}, + {file = "protobuf-3.19.4-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:54a1473077f3b616779ce31f477351a45b4fef8c9fd7892d6d87e287a38df368"}, + {file = "protobuf-3.19.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:435bb78b37fc386f9275a7035fe4fb1364484e38980d0dd91bc834a02c5ec909"}, + {file = "protobuf-3.19.4-cp37-cp37m-win32.whl", hash = "sha256:16f519de1313f1b7139ad70772e7db515b1420d208cb16c6d7858ea989fc64a9"}, + {file = "protobuf-3.19.4-cp37-cp37m-win_amd64.whl", hash = "sha256:cdc076c03381f5c1d9bb1abdcc5503d9ca8b53cf0a9d31a9f6754ec9e6c8af0f"}, + {file = "protobuf-3.19.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:69da7d39e39942bd52848438462674c463e23963a1fdaa84d88df7fbd7e749b2"}, + {file = "protobuf-3.19.4-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:48ed3877fa43e22bcacc852ca76d4775741f9709dd9575881a373bd3e85e54b2"}, + {file = "protobuf-3.19.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd95d1dfb9c4f4563e6093a9aa19d9c186bf98fa54da5252531cc0d3a07977e7"}, + {file = "protobuf-3.19.4-cp38-cp38-win32.whl", hash = "sha256:b38057450a0c566cbd04890a40edf916db890f2818e8682221611d78dc32ae26"}, + {file = "protobuf-3.19.4-cp38-cp38-win_amd64.whl", hash = "sha256:7ca7da9c339ca8890d66958f5462beabd611eca6c958691a8fe6eccbd1eb0c6e"}, + {file = "protobuf-3.19.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:36cecbabbda242915529b8ff364f2263cd4de7c46bbe361418b5ed859677ba58"}, + {file = "protobuf-3.19.4-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:c1068287025f8ea025103e37d62ffd63fec8e9e636246b89c341aeda8a67c934"}, + {file = "protobuf-3.19.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96bd766831596d6014ca88d86dc8fe0fb2e428c0b02432fd9db3943202bf8c5e"}, + {file = "protobuf-3.19.4-cp39-cp39-win32.whl", hash = "sha256:84123274d982b9e248a143dadd1b9815049f4477dc783bf84efe6250eb4b836a"}, + {file = "protobuf-3.19.4-cp39-cp39-win_amd64.whl", hash = "sha256:3112b58aac3bac9c8be2b60a9daf6b558ca3f7681c130dcdd788ade7c9ffbdca"}, + {file = "protobuf-3.19.4-py2.py3-none-any.whl", hash = "sha256:8961c3a78ebfcd000920c9060a262f082f29838682b1f7201889300c1fbe0616"}, + {file = "protobuf-3.19.4.tar.gz", hash = "sha256:9df0c10adf3e83015ced42a9a7bd64e13d06c4cf45c340d2c63020ea04499d0a"}, +] psutil = [ {file = "psutil-5.9.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:55ce319452e3d139e25d6c3f85a1acf12d1607ddedea5e35fb47a552c051161b"}, {file = "psutil-5.9.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:7336292a13a80eb93c21f36bde4328aa748a04b68c13d01dfddd67fc13fd0618"}, diff --git a/pyproject.toml b/pyproject.toml index 2e29984de8d8..4d7b09c3e6c8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,6 +12,7 @@ pyln-proto = { path = "./contrib/pyln-proto", develop = true } Mako = "^1.1.6" mrkd = { git = "https://github.com/refi64/mrkd.git", rev = "781f05eb9898ca652f18eed29b3c956389e6a2a7" } websocket-client = "^1.2.3" +grpcio-tools = "^1.44.0" [tool.poetry.dev-dependencies] # Test dependencies and inherited dependencies belong here From b748e880d60c7e09da638f5274ed31cfa68cd2ee Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 22 Mar 2022 16:49:12 +0100 Subject: [PATCH 12/15] cln-grpc: Rename the grpc plugin and binary Suggested-by: Rusty Russell <@rustyrussell> Changelog-Added: plugins: `cln-grpc` first class GRPC interface for remotely controlling nodes over mTLS authentication --- plugins/Makefile | 6 +++--- plugins/grpc-plugin/Cargo.toml | 6 +++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/plugins/Makefile b/plugins/Makefile index e3cacde4a498..b30b988cabbb 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -179,11 +179,11 @@ CLN_PLUGIN_SRC = $(shell find plugins/src -name "*.rs") ${CLN_PLUGIN_EXAMPLES}: ${CLN_PLUGIN_SRC} (cd plugins; cargo build ${CARGO_OPTS} --examples) -target/${RUST_PROFILE}/grpc-plugin: ${CLN_PLUGIN_SRC} - cargo build ${CARGO_OPTS} --bin grpc-plugin +target/${RUST_PROFILE}/cln-grpc: ${CLN_PLUGIN_SRC} + cargo build ${CARGO_OPTS} --bin cln-grpc ifneq ($(RUST),0) -DEFAULT_TARGETS += $(CLN_PLUGIN_EXAMPLES) target/${RUST_PROFILE}/grpc-plugin +DEFAULT_TARGETS += $(CLN_PLUGIN_EXAMPLES) target/${RUST_PROFILE}/cln-grpc endif include plugins/test/Makefile diff --git a/plugins/grpc-plugin/Cargo.toml b/plugins/grpc-plugin/Cargo.toml index 21e56727d2f9..634248ef2a49 100644 --- a/plugins/grpc-plugin/Cargo.toml +++ b/plugins/grpc-plugin/Cargo.toml @@ -1,8 +1,12 @@ [package] edition = "2021" -name = "grpc-plugin" +name = "cln-grpc-plugin" version = "0.1.0" +[[bin]] +name = "cln-grpc" +path = "src/main.rs" + [dependencies] anyhow = "1.0" log = "0.4" From 21385e20b78177356281d205082e03e0aa2ed036 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 23 Mar 2022 11:50:36 +0100 Subject: [PATCH 13/15] cln-grpc: Do not start unless a `grpc-port` is specified For now we don't want to autostart. Suggested-by: Rusty Russell <@rustyrussell> --- plugins/grpc-plugin/src/main.rs | 6 +++++- tests/test_cln_rs.py | 20 ++++++++++++++++---- tools/build-release.sh | 2 +- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/plugins/grpc-plugin/src/main.rs b/plugins/grpc-plugin/src/main.rs index 8636fc5adec0..369373034c62 100644 --- a/plugins/grpc-plugin/src/main.rs +++ b/plugins/grpc-plugin/src/main.rs @@ -31,13 +31,17 @@ async fn main() -> Result<()> { let plugin = Builder::new(state.clone(), tokio::io::stdin(), tokio::io::stdout()) .option(options::ConfigOption::new( "grpc-port", - options::Value::Integer(50051), + options::Value::Integer(-1), "Which port should the grpc plugin listen for incoming connections?", )) .start() .await?; let bind_port = match plugin.option("grpc-port") { + Some(options::Value::Integer(-1)) => { + log::info!("`grpc-port` option is not configured, exiting."); + return Ok(()); + } Some(options::Value::Integer(i)) => i, None => return Err(anyhow!("Missing 'grpc-port' option")), Some(o) => return Err(anyhow!("grpc-port is not a valid integer: {:?}", o)), diff --git a/tests/test_cln_rs.py b/tests/test_cln_rs.py index e2b0ef53d974..edabc7c581eb 100644 --- a/tests/test_cln_rs.py +++ b/tests/test_cln_rs.py @@ -1,7 +1,7 @@ from fixtures import * # noqa: F401,F403 from node_pb2_grpc import NodeStub from pathlib import Path -from pyln.testing.utils import env, TEST_NETWORK +from pyln.testing.utils import env, TEST_NETWORK, wait_for from ephemeral_port_reserve import reserve import grpc import node_pb2 as nodepb @@ -69,7 +69,7 @@ def test_plugin_start(node_factory): def test_grpc_connect(node_factory): """Attempts to connect to the grpc interface and call getinfo""" grpc_port = reserve() - bin_path = Path.cwd() / "target" / "debug" / "grpc-plugin" + bin_path = Path.cwd() / "target" / "debug" / "cln-grpc" l1 = node_factory.get_node(options={"plugin": str(bin_path), "grpc-port": str(grpc_port)}) p = Path(l1.daemon.lightning_dir) / TEST_NETWORK @@ -105,7 +105,7 @@ def test_grpc_generate_certificate(node_factory): - If we delete one cert or its key it should get regenerated. """ grpc_port = reserve() - bin_path = Path.cwd() / "target" / "debug" / "grpc-plugin" + bin_path = Path.cwd() / "target" / "debug" / "cln-grpc" l1 = node_factory.get_node(options={ "plugin": str(bin_path), "grpc-port": str(grpc_port), @@ -139,6 +139,18 @@ def test_grpc_generate_certificate(node_factory): assert contents[-1] != files[-1].open().read() +def test_grpc_no_auto_start(node_factory): + """Ensure that we do not start cln-grpc unless a port is configured. + """ + bin_path = Path.cwd() / "target" / "debug" / "cln-grpc" + l1, = node_factory.get_nodes(1, opts={ + "plugin": str(bin_path), + }) + + wait_for(lambda: [p for p in l1.rpc.plugin('list')['plugins'] if 'cln-grpc' in p['name']] == []) + assert l1.daemon.is_in_log(r'plugin-cln-grpc: Killing plugin: exited during normal operation') + + def test_grpc_wrong_auth(node_factory): """An mTLS client certificate should only be usable with its node @@ -146,7 +158,7 @@ def test_grpc_wrong_auth(node_factory): and then we try to cross the wires. """ grpc_port = reserve() - bin_path = Path.cwd() / "target" / "debug" / "grpc-plugin" + bin_path = Path.cwd() / "target" / "debug" / "cln-grpc" l1, l2 = node_factory.get_nodes(2, opts={ "plugin": str(bin_path), "start": False, diff --git a/tools/build-release.sh b/tools/build-release.sh index cb3231b4896b..bd06d411949a 100755 --- a/tools/build-release.sh +++ b/tools/build-release.sh @@ -8,7 +8,7 @@ if [ "$1" = "--inside-docker" ]; then cd /build ./configure make - make install DESTDIR=/"$VER" + make install DESTDIR=/"$VER" RUST_PROFILE=release cd /"$VER" && tar cvfz /release/clightning-"$VER".tar.gz -- * exit 0 fi From 343961d1c430eb5072264276ef096d109f5091a1 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 29 Mar 2022 11:16:33 +1030 Subject: [PATCH 14/15] pytest: fix flake due to cln-grpc starting before "public key" message. e.g. ``` lightningd-1: 2022-03-28T11:02:12.476Z DEBUG plugin-cln-grpc: add_pem_file processed 1 valid and 0 invalid certs lightningd-1: 2022-03-28T11:02:12.478Z DEBUG plugin-cln-grpc: Connecting to \"lightning-rpc\" and serving grpc on 0.0.0.0:36331 lightningd-1: 2022-03-28T11:02:12.478Z DEBUG connectd: REPLY WIRE_CONNECTD_ACTIVATE_REPLY with 0 fds lightningd-1: 2022-03-28T11:02:12.478Z INFO lightningd: -------------------------------------------------- lightningd-1: 2022-03-28T11:02:12.478Z INFO lightningd: Server started with public key ``` Which means we don't see it, since start() swallows it: ``` > raise TimeoutError('Unable to find "{}" in logs.'.format(exs)) E TimeoutError: Unable to find "[re.compile('serving grpc on 0.0.0.0:')]" in logs. ``` Signed-off-by: Rusty Russell --- tests/test_cln_rs.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/test_cln_rs.py b/tests/test_cln_rs.py index edabc7c581eb..4fb62b81084c 100644 --- a/tests/test_cln_rs.py +++ b/tests/test_cln_rs.py @@ -15,6 +15,11 @@ ) +def wait_for_grpc_start(node): + """This can happen before "public key" which start() swallows""" + wait_for(lambda: node.daemon.is_in_log(r'serving grpc on 0.0.0.0:')) + + def test_rpc_client(node_factory): l1 = node_factory.get_node() bin_path = Path.cwd() / "target" / "debug" / "examples" / "cln-rpc-getinfo" @@ -82,7 +87,7 @@ def test_grpc_connect(node_factory): certificate_chain=cert_path.open('rb').read() ) - l1.daemon.wait_for_log(r'serving grpc on 0.0.0.0:') + wait_for_grpc_start(l1) channel = grpc.secure_channel( f"localhost:{grpc_port}", creds, @@ -165,7 +170,7 @@ def test_grpc_wrong_auth(node_factory): "grpc-port": str(grpc_port), }) l1.start() - l1.daemon.wait_for_log(r'serving grpc on 0.0.0.0:') + wait_for_grpc_start(l1) def connect(node): p = Path(node.daemon.lightning_dir) / TEST_NETWORK @@ -193,7 +198,7 @@ def connect(node): l1.stop() l2.start() - l2.daemon.wait_for_log(r'serving grpc on 0.0.0.0:') + wait_for_grpc_start(l2) # This should not work, it's a different node with pytest.raises(Exception, match=r'Socket closed|StatusCode.UNAVAILABLE'): From a47770dd4e7a8ea65d0e915fba25dc8731160797 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 29 Mar 2022 12:29:42 +0200 Subject: [PATCH 15/15] cln-grpc: Set cln-grpc version to 0.0.1 until it's complete Suggested-by: Vincenzo Palazzo <@vincenzopalazzo> --- cln-grpc/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cln-grpc/Cargo.toml b/cln-grpc/Cargo.toml index 0204e4237b48..c0bccbe599d5 100644 --- a/cln-grpc/Cargo.toml +++ b/cln-grpc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cln-grpc" -version = "0.1.0" +version = "0.0.1" edition = "2021" [dependencies]