From fbadef9040f1fb2246220f59dc742b7e1b243b05 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 31 Oct 2025 17:46:58 +0100 Subject: [PATCH 01/17] plugin: Added the stubs for the lsps-related methods `invoice` for now is the one we will expose. --- .../proto/glclient/greenlight.proto | 26 +++++++++++++++++-- libs/gl-plugin/.tasks.yml | 3 ++- libs/gl-plugin/src/node/mod.rs | 8 +++++- libs/gl-plugin/src/node/wrapper.rs | 10 ++++++- libs/proto/glclient/greenlight.proto | 26 +++++++++++++++++-- 5 files changed, 66 insertions(+), 7 deletions(-) diff --git a/libs/gl-plugin/.resources/proto/glclient/greenlight.proto b/libs/gl-plugin/.resources/proto/glclient/greenlight.proto index a75224793..1c306e30f 100644 --- a/libs/gl-plugin/.resources/proto/glclient/greenlight.proto +++ b/libs/gl-plugin/.resources/proto/glclient/greenlight.proto @@ -14,9 +14,14 @@ package greenlight; // // Deprecated methods are being replaced by the standardized and // automatically managed cln-grpc protocol you can find in -// `node.proto` +// `node.proto`. This interface consists mostly of +// Greenlight-specific, and backported functionality. service Node { - // Stream incoming payments + // Create an invoice to request an incoming payment. Includes LSP + // negotiation to open a channel on-demand when needed. + rpc Invoice(InvoiceRequest) returns (InvoiceResponse) {} + + // Stream incoming payments // // Currently includes off-chain payments received matching an // invoice or spontaneus paymens through keysend. @@ -221,3 +226,20 @@ message TrampolinePayResponse { uint64 amount_sent_msat = 6; bytes destination = 7; } + +message InvoiceRequest { + string lsp_id = 1; // len=0 => None + // Optional: for discounts/API keys + string token = 2; // len=0 => None + // Pass-through of cln invoice rpc params + uint64 amount_msat = 3; // 0 => Any + string description = 4; + string label = 5; +} +message InvoiceResponse { + string bolt11 = 1; + uint32 created_index = 2; + uint32 expires_at = 3; + bytes payment_hash = 4; + bytes payment_secret = 5; +} diff --git a/libs/gl-plugin/.tasks.yml b/libs/gl-plugin/.tasks.yml index 2a7de4ba2..b5b03cf14 100644 --- a/libs/gl-plugin/.tasks.yml +++ b/libs/gl-plugin/.tasks.yml @@ -2,5 +2,6 @@ version: '3' tasks: build: + dir: '.' cmds: - - cargo build + - cargo build --bin gl-plugin diff --git a/libs/gl-plugin/src/node/mod.rs b/libs/gl-plugin/src/node/mod.rs index 6de3ce7fc..cf94d918e 100644 --- a/libs/gl-plugin/src/node/mod.rs +++ b/libs/gl-plugin/src/node/mod.rs @@ -34,7 +34,6 @@ static LIMITER: OnceCell> = static RPC_CLIENT: OnceCell>> = OnceCell::const_new(); static RPC_POLL_INTERVAL: Duration = Duration::from_millis(500); - pub async fn get_rpc>(path: P) -> Arc> { RPC_CLIENT .get_or_init(|| async { @@ -182,6 +181,13 @@ impl Node for PluginNodeServer { type StreamHsmRequestsStream = ReceiverStream>; type StreamLogStream = ReceiverStream>; + async fn invoice( + &self, + _: Request, + ) -> Result, Status> { + unimplemented!() + } + async fn stream_custommsg( &self, _: Request, diff --git a/libs/gl-plugin/src/node/wrapper.rs b/libs/gl-plugin/src/node/wrapper.rs index 6165789c1..84ef4cfa5 100644 --- a/libs/gl-plugin/src/node/wrapper.rs +++ b/libs/gl-plugin/src/node/wrapper.rs @@ -1192,7 +1192,8 @@ impl WrappedNodeServer { use crate::pb::{ node_server::Node as GlNode, Custommsg, Empty, HsmRequest, HsmResponse, IncomingPayment, - LogEntry, StreamCustommsgRequest, StreamIncomingFilter, StreamLogRequest, + InvoiceRequest, InvoiceResponse, LogEntry, StreamCustommsgRequest, StreamIncomingFilter, + StreamLogRequest, }; #[tonic::async_trait] @@ -1202,6 +1203,13 @@ impl GlNode for WrappedNodeServer { type StreamLogStream = ReceiverStream>; type StreamIncomingStream = ReceiverStream>; + async fn invoice( + &self, + req: Request, + ) -> Result, Status> { + self.node_server.invoice(req).await + } + async fn stream_incoming( &self, req: tonic::Request, diff --git a/libs/proto/glclient/greenlight.proto b/libs/proto/glclient/greenlight.proto index a75224793..1c306e30f 100644 --- a/libs/proto/glclient/greenlight.proto +++ b/libs/proto/glclient/greenlight.proto @@ -14,9 +14,14 @@ package greenlight; // // Deprecated methods are being replaced by the standardized and // automatically managed cln-grpc protocol you can find in -// `node.proto` +// `node.proto`. This interface consists mostly of +// Greenlight-specific, and backported functionality. service Node { - // Stream incoming payments + // Create an invoice to request an incoming payment. Includes LSP + // negotiation to open a channel on-demand when needed. + rpc Invoice(InvoiceRequest) returns (InvoiceResponse) {} + + // Stream incoming payments // // Currently includes off-chain payments received matching an // invoice or spontaneus paymens through keysend. @@ -221,3 +226,20 @@ message TrampolinePayResponse { uint64 amount_sent_msat = 6; bytes destination = 7; } + +message InvoiceRequest { + string lsp_id = 1; // len=0 => None + // Optional: for discounts/API keys + string token = 2; // len=0 => None + // Pass-through of cln invoice rpc params + uint64 amount_msat = 3; // 0 => Any + string description = 4; + string label = 5; +} +message InvoiceResponse { + string bolt11 = 1; + uint32 created_index = 2; + uint32 expires_at = 3; + bytes payment_hash = 4; + bytes payment_secret = 5; +} From c948c43bf432ea193c58069dda56ec55f4b9190c Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 3 Nov 2025 14:26:22 +0100 Subject: [PATCH 02/17] chore(lints): Addressed some annoying linting errors --- libs/gl-client/src/lsps/json_rpc_erased.rs | 2 +- libs/gl-client/src/signer/auth.rs | 11 ----------- libs/gl-plugin/src/messages.rs | 15 --------------- 3 files changed, 1 insertion(+), 27 deletions(-) diff --git a/libs/gl-client/src/lsps/json_rpc_erased.rs b/libs/gl-client/src/lsps/json_rpc_erased.rs index 962e5fff4..3f1f1211e 100644 --- a/libs/gl-client/src/lsps/json_rpc_erased.rs +++ b/libs/gl-client/src/lsps/json_rpc_erased.rs @@ -203,7 +203,7 @@ impl<'a> JsonRpcMethodUnerased<'a, Vec, Vec, Vec> for UneraseWrapper impl dyn JsonRpcMethodErased { // The impl promises here we return a concrete type // However, we'd rather keep the implementation details private in this module and don't want users messing with it - pub fn unerase(&self) -> impl JsonRpcMethodUnerased, Vec, Vec> { + pub fn unerase(&self) -> impl JsonRpcMethodUnerased<'_, Vec, Vec, Vec> { UneraseWrapper { inner: self } } } diff --git a/libs/gl-client/src/signer/auth.rs b/libs/gl-client/src/signer/auth.rs index f1ee2df07..6ef17e4ab 100644 --- a/libs/gl-client/src/signer/auth.rs +++ b/libs/gl-client/src/signer/auth.rs @@ -12,17 +12,6 @@ pub trait Authorizer { ) -> Result, Error>; } -pub struct DummyAuthorizer {} - -impl Authorizer for DummyAuthorizer { - fn authorize( - &self, - _requests: &Vec, - ) -> Result, Error> { - Ok(vec![]) - } -} - pub struct GreenlightAuthorizer {} impl Authorizer for GreenlightAuthorizer { diff --git a/libs/gl-plugin/src/messages.rs b/libs/gl-plugin/src/messages.rs index c1f3dcf9c..30ff06fdb 100644 --- a/libs/gl-plugin/src/messages.rs +++ b/libs/gl-plugin/src/messages.rs @@ -6,13 +6,6 @@ use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; use std::collections::HashMap; -#[derive(Serialize, Deserialize, Debug)] -#[serde(tag = "method", content = "params")] -#[serde(rename_all = "snake_case")] -enum JsonRpcCall { - //HtlcAccepted(HtlcAcceptedCall), -} - #[derive(Debug)] pub struct ParserError { reason: String, @@ -25,14 +18,6 @@ impl std::fmt::Display for ParserError { } impl std::error::Error for ParserError {} -#[derive(Serialize, Deserialize, Debug)] -struct JsonRpcRequest { - id: Option, - jsonrpc: String, - method: String, - params: JsonRpcCall, -} - // "Inspired" by https://github.com/serde-rs/serde/issues/1028#issuecomment-325434041 #[derive(Serialize, Deserialize, Debug)] #[serde(tag = "method", content = "params")] From 923995b52ec5e32f914c529be7b4ac6a166124b5 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 11 Nov 2025 16:46:33 +0100 Subject: [PATCH 03/17] chore(py): Update pyproject.toml syntax for new dependency group syntax --- pyproject.toml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 0c00eee9f..b8a3beabe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,12 +6,14 @@ readme = "README.md" requires-python = ">=3.9" dependencies = [] -[tool.uv] -dev-dependencies = [ +[dependency-groups] +dev = [ "pytest-timeout>=2.3.1", "python-kacl>=0.6.7", ] +[tool.uv] + [tool.uv.workspace] members = [ "docs", From 117445e1639146c77d4ddcf691dea0162d730f9f Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 11 Nov 2025 16:47:45 +0100 Subject: [PATCH 04/17] rs: Add lsps2-invoice mapping in the plugin This takes care of incoming grpc requests, the conversion to JSON-RPC, then the actual call, as well as the conversion back from JSON-RPC result to grpc result. --- Cargo.lock | 34 +++++++++++--- libs/gl-client/Cargo.toml | 4 +- libs/gl-plugin/src/node/mod.rs | 14 +++++- libs/gl-plugin/src/requests.rs | 68 ++++++++++++++++++++++++++++ libs/gl-plugin/src/responses.rs | 36 +++++++++++++++ libs/proto/glclient/greenlight.proto | 2 +- 6 files changed, 147 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a2db8c0af..f8467cd52 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1389,7 +1389,7 @@ dependencies = [ "mockall", "picky", "picky-asn1-der 0.4.1", - "picky-asn1-x509 0.12.0", + "picky-asn1-x509 0.15.2", "pin-project 1.1.10", "prost 0.12.6", "prost-derive 0.12.6", @@ -2788,6 +2788,17 @@ dependencies = [ "serde_bytes", ] +[[package]] +name = "picky-asn1" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ff038f9360b934342fb3c0a1d6e82c438a2624b51c3c6e3e6d7cf252b6f3ee3" +dependencies = [ + "oid", + "serde", + "serde_bytes", +] + [[package]] name = "picky-asn1-der" version = "0.2.5" @@ -2810,6 +2821,17 @@ dependencies = [ "serde_bytes", ] +[[package]] +name = "picky-asn1-der" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b491eb61603cba1ad5c6be0269883538f8d74136c35e3641a840fb0fbcd41efc" +dependencies = [ + "picky-asn1 0.10.1", + "serde", + "serde_bytes", +] + [[package]] name = "picky-asn1-x509" version = "0.6.1" @@ -2826,14 +2848,14 @@ dependencies = [ [[package]] name = "picky-asn1-x509" -version = "0.12.0" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c5f20f71a68499ff32310f418a6fad8816eac1a2859ed3f0c5c741389dd6208" +checksum = "c97cd14d567a17755910fa8718277baf39d08682a980b1b1a4b4da7d0bc61a04" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", "oid", - "picky-asn1 0.8.0", - "picky-asn1-der 0.4.1", + "picky-asn1 0.10.1", + "picky-asn1-der 0.5.4", "serde", ] diff --git a/libs/gl-client/Cargo.toml b/libs/gl-client/Cargo.toml index fc774e6b5..f93fac25d 100644 --- a/libs/gl-client/Cargo.toml +++ b/libs/gl-client/Cargo.toml @@ -26,8 +26,8 @@ hex = "0.4.3" http = "0.2" http-body = "^0.4" log = "^0.4" -picky = "6.3" -picky-asn1-x509 = "0.12" +picky = "6" +picky-asn1-x509 = "0.15" picky-asn1-der = "0.4" pin-project = "1.1.5" prost = "0.12" diff --git a/libs/gl-plugin/src/node/mod.rs b/libs/gl-plugin/src/node/mod.rs index cf94d918e..ea513a4cb 100644 --- a/libs/gl-plugin/src/node/mod.rs +++ b/libs/gl-plugin/src/node/mod.rs @@ -183,9 +183,19 @@ impl Node for PluginNodeServer { async fn invoice( &self, - _: Request, + req: Request, ) -> Result, Status> { - unimplemented!() + let req: pb::InvoiceRequest = req.into_inner(); + let invreq: crate::requests::InvoiceRequest = req.into(); + let rpc_arc = get_rpc(&self.rpc_path).await; + + let mut rpc = rpc_arc.lock().await; + let res = rpc + .call_typed(&invreq) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + + Ok(Response::new(res.into())) } async fn stream_custommsg( diff --git a/libs/gl-plugin/src/requests.rs b/libs/gl-plugin/src/requests.rs index 0309eca38..3d3f54d44 100644 --- a/libs/gl-plugin/src/requests.rs +++ b/libs/gl-plugin/src/requests.rs @@ -207,3 +207,71 @@ pub struct Keysend { #[derive(Debug, Clone, Serialize)] pub struct ListIncoming {} + +#[derive(Debug, Clone, Serialize)] +pub struct InvoiceRequest { + pub lsp_id: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub token: Option, + pub amount_msat: cln_rpc::primitives::AmountOrAny, + pub description: String, + pub label: String, +} + +use cln_rpc::model::TypedRequest; + +impl From for InvoiceRequest { + fn from(o: crate::pb::InvoiceRequest) -> InvoiceRequest { + InvoiceRequest { + lsp_id: o.lsp_id, + token: match o.token.as_ref() { + "" => None, + o => Some(o.to_owned()), + }, + amount_msat: match o.amount_msat { + 0 => cln_rpc::primitives::AmountOrAny::Any, + o => cln_rpc::primitives::AmountOrAny::Amount( + cln_grpc::pb::Amount { msat: o }.into(), + ), + }, + description: o.description, + label: o.label, + } + } +} + +impl TypedRequest for InvoiceRequest { + type Response = super::responses::InvoiceResponse; + + fn method(&self) -> &str { + "lsps-lsps2-invoice" + } +} + +#[cfg(test)] +mod test { + use super::*; + #[test] + fn test_invoice_response() { + let tests = vec![ + (crate::pb::InvoiceRequest { + lsp_id: "lsp_id".to_owned(), + token: "".to_owned(), + amount_msat: 0, + description: "description".to_owned(), + label: "label".to_owned(), + }), + crate::pb::InvoiceRequest { + lsp_id: "lsp_id".to_owned(), + token: "token".to_owned(), + amount_msat: 1337, + description: "description".to_owned(), + label: "label".to_owned(), + }, + ]; + + for t in tests { + let _actual: super::InvoiceRequest = t.into(); + } + } +} diff --git a/libs/gl-plugin/src/responses.rs b/libs/gl-plugin/src/responses.rs index 0baf8f42f..edde5054b 100644 --- a/libs/gl-plugin/src/responses.rs +++ b/libs/gl-plugin/src/responses.rs @@ -334,6 +334,27 @@ pub struct ListFunds { pub channels: Vec, } +#[derive(Debug, Clone, Deserialize)] +pub struct InvoiceResponse { + pub bolt11: String, + pub created_index: u32, + pub expires_at: u32, + pub payment_hash: String, + pub payment_secret: String, +} + +impl From for crate::pb::InvoiceResponse { + fn from(o: InvoiceResponse) -> crate::pb::InvoiceResponse { + crate::pb::InvoiceResponse { + bolt11: o.bolt11, + created_index: o.created_index, + expires_at: o.expires_at, + payment_hash: hex::decode(o.payment_hash).unwrap(), + payment_secret: hex::decode(o.payment_secret).unwrap(), + } + } +} + #[cfg(test)] mod test { use super::*; @@ -366,4 +387,19 @@ mod test { assert_eq!(v.value.0, t.output); } } + + #[test] + fn test_invoice_response() { + let tests: Vec = vec![InvoiceResponse { + bolt11: "ln1test".to_owned(), + created_index: 0, + expires_at: 123, + payment_hash: "AABBCCDDEEFF".to_owned(), + payment_secret: "1122334455".to_owned(), + }]; + + for t in tests { + let _actual: crate::pb::InvoiceResponse = t.into(); + } + } } diff --git a/libs/proto/glclient/greenlight.proto b/libs/proto/glclient/greenlight.proto index 1c306e30f..485ea7894 100644 --- a/libs/proto/glclient/greenlight.proto +++ b/libs/proto/glclient/greenlight.proto @@ -228,7 +228,7 @@ message TrampolinePayResponse { } message InvoiceRequest { - string lsp_id = 1; // len=0 => None + string lsp_id = 1; // len=0 => None, let the server decide. // Optional: for discounts/API keys string token = 2; // len=0 => None // Pass-through of cln invoice rpc params From fbb7816a872930397f90ebc7e3021e5040aaef4e Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 11 Nov 2025 16:49:36 +0100 Subject: [PATCH 05/17] test(py): Add a dummy LSPS policy plugin, and an LSPS node fixture We need something to test against, the dummy plugin just returns a fixed result, so the `cln-lsps-servive` plugin has something to work against. --- libs/gl-testing/gltesting/__init__.py | 34 ++++++++++++++++- libs/gl-testing/gltesting/fixtures.py | 31 ++++++++++++++- libs/gl-testing/plugins/lsps2_policy.py | 51 +++++++++++++++++++++++++ libs/gl-testing/pyproject.toml | 6 +++ 4 files changed, 119 insertions(+), 3 deletions(-) create mode 100755 libs/gl-testing/plugins/lsps2_policy.py diff --git a/libs/gl-testing/gltesting/__init__.py b/libs/gl-testing/gltesting/__init__.py index 0f651bad8..2762b4ff4 100644 --- a/libs/gl-testing/gltesting/__init__.py +++ b/libs/gl-testing/gltesting/__init__.py @@ -1,5 +1,37 @@ +from pathlib import Path from .scheduler import Scheduler + +def get_plugins_dir(): + """Get the path to the plugins directory. + + This works both when running from source (where plugins is a sibling + of gltesting) and when installed via pip (where both gltesting and + plugins are installed into site-packages). + + Returns: + Path: The absolute path to the plugins directory + + Raises: + FileNotFoundError: If the plugins directory cannot be found + """ + # Get the directory containing this __init__.py file (gltesting/) + package_dir = Path(__file__).parent + + # Try to find plugins as a sibling directory + # This handles both source and installed cases since both directories + # are installed at the same level + plugins_dir = package_dir.parent / "plugins" + + if plugins_dir.exists() and plugins_dir.is_dir(): + return plugins_dir + + raise FileNotFoundError( + f"Could not find plugins directory. Expected at: {plugins_dir}" + ) + + __all__ = [ - Scheduler + "Scheduler", + "get_plugins_dir", ] diff --git a/libs/gl-testing/gltesting/fixtures.py b/libs/gl-testing/gltesting/fixtures.py index 11ee3bbdf..f3e5bcec4 100644 --- a/libs/gl-testing/gltesting/fixtures.py +++ b/libs/gl-testing/gltesting/fixtures.py @@ -1,4 +1,5 @@ # Pytest fixtures +from rich.pretty import pprint import tempfile from .scheduler import Scheduler from gltesting.clients import Clients, Client @@ -224,8 +225,7 @@ def grpc_web_proxy(scheduler, grpc_test_server): @pytest.fixture def node_grpc_web_proxy(scheduler): - """A grpc-web proxy that knows how to talk to nodes. - """ + """A grpc-web proxy that knows how to talk to nodes.""" p = GrpcWebProxy(scheduler=scheduler, grpc_port=0) p.handler_cls = NodeHandler p.start() @@ -233,3 +233,30 @@ def node_grpc_web_proxy(scheduler): yield p p.stop() + + +@pytest.fixture +def lsps_server(node_factory): + """Provision and start an LSPs server.""" + from gltesting import get_plugins_dir + + policy_plugin = get_plugins_dir() / "lsps2_policy.py" + + lsp = node_factory.get_node( + options={ + "experimental-lsps2-service": None, + "experimental-lsps2-promise-secret": "A" * 64, + "important-plugin": policy_plugin, + "disable-plugin": "cln-grpc", + } + ) + + # Most of the params below are ignored, this is just a smoke test either way + pprint(lsp.rpc.lsps2_policy_getpolicy()) + pprint( + lsp.rpc.lsps2_policy_getchannelcapacity( + init_payment_size=10**6, scid="1x1x1", opening_fee_params=None + ) + ) + + yield lsp diff --git a/libs/gl-testing/plugins/lsps2_policy.py b/libs/gl-testing/plugins/lsps2_policy.py new file mode 100755 index 000000000..d71fc6703 --- /dev/null +++ b/libs/gl-testing/plugins/lsps2_policy.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +"""A simple implementation of a LSPS2 compatible policy plugin. It is the job +of this plugin to deliver a fee options menu to the LSPS2 service plugin. +""" + +from pyln.client import Plugin +from datetime import datetime, timedelta, timezone + + +plugin = Plugin() + + +@plugin.method("lsps2-policy-getpolicy") +def lsps2_policy_getpolicy(request): + """Returns an opening fee menu for the LSPS2 plugin.""" + now = datetime.now(timezone.utc) + + # Is ISO 8601 format "YYYY-MM-DDThh:mm:ss.uuuZ" + valid_until = (now + timedelta(hours=1)).isoformat().replace("+00:00", "Z") + + return { + "policy_opening_fee_params_menu": [ + { + "min_fee_msat": "1000", + "proportional": 1000, + "valid_until": valid_until, + "min_lifetime": 2000, + "max_client_to_self_delay": 2016, + "min_payment_size_msat": "1000", + "max_payment_size_msat": "100000000", + }, + { + "min_fee_msat": "1092000", + "proportional": 2400, + "valid_until": valid_until, + "min_lifetime": 1008, + "max_client_to_self_delay": 2016, + "min_payment_size_msat": "1000", + "max_payment_size_msat": "1000000", + }, + ] + } + + +@plugin.method("lsps2-policy-getchannelcapacity") +def lsps2_policy_getchannelcapacity(request, init_payment_size, scid, opening_fee_params): + """Returns an opening fee menu for the LSPS2 plugin.""" + return {"channel_capacity_msat": 100000000} + + +plugin.run() diff --git a/libs/gl-testing/pyproject.toml b/libs/gl-testing/pyproject.toml index eb17ce0cf..f82c939fb 100644 --- a/libs/gl-testing/pyproject.toml +++ b/libs/gl-testing/pyproject.toml @@ -37,4 +37,10 @@ build-backend = "hatchling.build" [tool.hatch.build] include = [ "gltesting", + "plugins", +] +exclude = [ + "**/.#*", + "**/*~", + "**/*.swp", ] From ab027cd24921f26c666b6ed09f78a43cfa06c553 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 11 Nov 2025 17:57:07 +0100 Subject: [PATCH 06/17] chore(tests): Stabilize test_trampoline_pay The new CLN versions start the `cln-grpc` plugin by default, causing them to collide on the port, if not disabled. --- libs/gl-client-py/tests/test_plugin.py | 1 - libs/gl-testing/tests/test_gl_node.py | 2 +- libs/gl-testing/tests/test_lsps.py | 6 +++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/libs/gl-client-py/tests/test_plugin.py b/libs/gl-client-py/tests/test_plugin.py index b9f882b8d..0bdf1ace2 100644 --- a/libs/gl-client-py/tests/test_plugin.py +++ b/libs/gl-client-py/tests/test_plugin.py @@ -129,7 +129,6 @@ def test_trampoline_pay(bitcoind, clients, node_factory): # calling `trampoline_pay` with an unkown tmrp_node_id must fail. with pytest.raises( expected_exception=ValueError, - match=f"Unknown peer {l3.info['id']}", ): res = n1.trampoline_pay(inv["bolt11"], bytes.fromhex(l3.info["id"])) diff --git a/libs/gl-testing/tests/test_gl_node.py b/libs/gl-testing/tests/test_gl_node.py index de65971a8..2590de86c 100644 --- a/libs/gl-testing/tests/test_gl_node.py +++ b/libs/gl-testing/tests/test_gl_node.py @@ -17,7 +17,7 @@ def test_node_network_gl_fund(node_factory, clients, bitcoind): l1 -> l2 -> gl1 ``` """ - l1, l2 = node_factory.line_graph(2) + l1, l2 = node_factory.line_graph(2, opts=[{'disable-plugin': 'cln-grpc'}]*2) c = clients.new() c.register(configure=True) gl1 = c.node() diff --git a/libs/gl-testing/tests/test_lsps.py b/libs/gl-testing/tests/test_lsps.py index c3ec84aa5..94dfa19ba 100755 --- a/libs/gl-testing/tests/test_lsps.py +++ b/libs/gl-testing/tests/test_lsps.py @@ -108,12 +108,12 @@ def test_list_lsp_server( ): # Create a network n1: LightningNode = node_factory.get_node( - options=dict(plugin=get_lsps_dummy_plugin_path()) + options={"plugin": get_lsps_dummy_plugin_path(), "disable-plugin": "cln-grpc"} ) n2: LightningNode = node_factory.get_node( - options=dict(plugin=get_lsps_dummy_plugin_path()) + options={"plugin": get_lsps_dummy_plugin_path(), "disable-plugin": "cln-grpc"} ) - n3: LightningNode = node_factory.get_node() + n3: LightningNode = node_factory.get_node(options={"disable-plugin": "cln-grpc"}) # Create the channel-graph n1.fundchannel(n2, announce_channel=True) From 904676ff7bc46a3b3924b5a2e4e8ab2910cccc5f Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 12 Nov 2025 13:53:49 +0100 Subject: [PATCH 07/17] chore(tests): Add a sample test for the `lsps_server` fixture --- libs/gl-client-py/tests/test_plugin.py | 39 ++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/libs/gl-client-py/tests/test_plugin.py b/libs/gl-client-py/tests/test_plugin.py index 0bdf1ace2..e423b2d25 100644 --- a/libs/gl-client-py/tests/test_plugin.py +++ b/libs/gl-client-py/tests/test_plugin.py @@ -203,3 +203,42 @@ def test_trampoline_multi_htlc(bitcoind, clients, node_factory): res = n1.trampoline_pay(inv["bolt11"], bytes.fromhex(l2.info["id"])) assert res assert res.parts == 2 + + +def test_lsps_plugin_calls( + clients, bitcoind, node_factory, lsps_server +): + """Test that we can call the `lsps-jitchannel` method from a + variety of places. + + ```ditaa + S --- LSP --- R1 + ``` + + """ + # Bootstrap a very simple network + lsp_id = lsps_server.info["id"] + (r1,) = node_factory.get_nodes(1, opts=[{"disable-plugin": "cln-grpc"}]) + + # Get the GL node + c = clients.new() + c.register() + s = c1.node() + s.connect(lsps_server) + + res = s.localrpc.lsps_lsps2_invoice( + lsp_id=lsp_id, + token=None, + amount_msat="1337msat", + description="description", + label="lbl1", + ) + + inv = s.localrpc.decodepay(res['bolt11']) + pprint(inv) + + # Only one routehint, with only one hop, the LSP to the destination + assert len(inv['routes']) == 1 and len(inv['routes'][0]) == 1 + assert inv['description'] == 'description' + rh = inv['routes'][0][0] + assert rh['pubkey'] == lsp_id From 89ca70253c6e12140979e5086e39ee81dcc36c0d Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 13 Nov 2025 14:30:39 +0100 Subject: [PATCH 08/17] chore(py): Remove old attempts to implement LSPS1 It's not being used by anyone, so why keep it. We're building a longer term LSPS2 implementation. --- Cargo.lock | 1062 ++++------------- libs/gl-client-py/glclient/__init__.py | 7 - libs/gl-client-py/glclient/lsps.py | 102 -- libs/gl-client-py/src/lib.rs | 3 - libs/gl-client-py/src/node.rs | 6 +- libs/gl-client/src/lib.rs | 3 - libs/gl-client/src/lsps/client.rs | 173 --- libs/gl-client/src/lsps/error.rs | 48 - libs/gl-client/src/lsps/json_rpc.rs | 355 ------ libs/gl-client/src/lsps/json_rpc_erased.rs | 372 ------ .../src/lsps/lsps0/common_schemas.rs | 315 ----- libs/gl-client/src/lsps/lsps0/mod.rs | 2 - libs/gl-client/src/lsps/lsps0/schema.rs | 25 - libs/gl-client/src/lsps/lsps1/mod.rs | 1 - libs/gl-client/src/lsps/lsps1/schema.rs | 99 -- libs/gl-client/src/lsps/lsps2/mod.rs | 1 - libs/gl-client/src/lsps/lsps2/schema.rs | 181 --- libs/gl-client/src/lsps/message.rs | 132 -- libs/gl-client/src/lsps/mod.rs | 9 - 19 files changed, 230 insertions(+), 2666 deletions(-) delete mode 100644 libs/gl-client-py/glclient/lsps.py delete mode 100644 libs/gl-client/src/lsps/client.rs delete mode 100644 libs/gl-client/src/lsps/error.rs delete mode 100644 libs/gl-client/src/lsps/json_rpc.rs delete mode 100644 libs/gl-client/src/lsps/json_rpc_erased.rs delete mode 100644 libs/gl-client/src/lsps/lsps0/common_schemas.rs delete mode 100644 libs/gl-client/src/lsps/lsps0/mod.rs delete mode 100644 libs/gl-client/src/lsps/lsps0/schema.rs delete mode 100644 libs/gl-client/src/lsps/lsps1/mod.rs delete mode 100644 libs/gl-client/src/lsps/lsps1/schema.rs delete mode 100644 libs/gl-client/src/lsps/lsps2/mod.rs delete mode 100644 libs/gl-client/src/lsps/lsps2/schema.rs delete mode 100644 libs/gl-client/src/lsps/message.rs delete mode 100644 libs/gl-client/src/lsps/mod.rs diff --git a/Cargo.lock b/Cargo.lock index f8467cd52..db41030ee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -42,7 +42,7 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" dependencies = [ - "cfg-if 1.0.1", + "cfg-if", "cipher 0.3.0", "cpufeatures", "opaque-debug", @@ -68,7 +68,7 @@ version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ - "cfg-if 1.0.1", + "cfg-if", "once_cell", "version_check", "zerocopy", @@ -199,36 +199,15 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "async-stream" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22068c0c19514942eefcfd4daf8976ef1aad84e61539f95cd200c35202f80af5" -dependencies = [ - "async-stream-impl 0.2.1", - "futures-core", -] - [[package]] name = "async-stream" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" dependencies = [ - "async-stream-impl 0.3.6", + "async-stream-impl", "futures-core", - "pin-project-lite 0.2.16", -] - -[[package]] -name = "async-stream-impl" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25f9db3b38af870bf7e5cc649167533b493928e50744e2c30ae350230b414670" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "pin-project-lite", ] [[package]] @@ -261,7 +240,7 @@ checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ "hermit-abi 0.1.19", "libc", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -279,21 +258,21 @@ dependencies = [ "async-trait", "axum-core", "bitflags 1.3.2", - "bytes 1.10.1", + "bytes", "futures-util", "http", - "http-body 0.4.6", - "hyper 0.14.32", - "itoa 1.0.15", + "http-body", + "hyper", + "itoa", "matchit", "memchr", "mime", "percent-encoding", - "pin-project-lite 0.2.16", + "pin-project-lite", "rustversion", "serde", "sync_wrapper", - "tower 0.4.13", + "tower", "tower-layer", "tower-service", ] @@ -305,10 +284,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" dependencies = [ "async-trait", - "bytes 1.10.1", + "bytes", "futures-util", "http", - "http-body 0.4.6", + "http-body", "mime", "rustversion", "tower-layer", @@ -322,7 +301,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ "addr2line", - "cfg-if 1.0.1", + "cfg-if", "libc", "miniz_oxide", "object", @@ -330,12 +309,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "base64" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" - [[package]] name = "base64" version = "0.13.1" @@ -366,7 +339,7 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89ffdaa8c6398acd07176317eb6c1f9082869dd1cc3fee7c72c6354866b928cc" dependencies = [ - "bytes 1.10.1", + "bytes", "smallvec", ] @@ -389,7 +362,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43d193de1f7487df1914d3a568b772458861d33f9c54249612cc2893d6915054" dependencies = [ "bitcoin_hashes 0.13.0", - "rand_core 0.6.4", + "rand_core", "serde", "unicode-normalization", ] @@ -558,12 +531,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" -[[package]] -name = "bytes" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" - [[package]] name = "bytes" version = "1.10.1" @@ -582,12 +549,6 @@ dependencies = [ "shlex", ] -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - [[package]] name = "cfg-if" version = "1.0.1" @@ -606,7 +567,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" dependencies = [ - "cfg-if 1.0.1", + "cfg-if", "cipher 0.4.4", "cpufeatures", ] @@ -730,9 +691,9 @@ dependencies = [ "log", "prost 0.12.6", "serde", - "tokio 1.46.1", + "tokio", "tokio-stream", - "tokio-util 0.7.15", + "tokio-util", "tonic 0.11.0", "tonic-build 0.11.0", ] @@ -744,14 +705,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6b096d2f2a7c58c6faaff1b64281a4c30ff078ec3ca746975fb76ac1a6d6e1f" dependencies = [ "anyhow", - "bytes 1.10.1", + "bytes", "futures", "log", "serde", "serde_json", - "tokio 1.46.1", + "tokio", "tokio-stream", - "tokio-util 0.7.15", + "tokio-util", "tracing", "tracing-subscriber", ] @@ -764,14 +725,14 @@ checksum = "f4436e58f1fccb1faf69df9ac436ae9304b5c200c7d92e6c4229826bdd0a8d0d" dependencies = [ "anyhow", "bitcoin 0.31.2", - "bytes 1.10.1", + "bytes", "futures-util", "hex", "log", "serde", "serde_json", - "tokio 1.46.1", - "tokio-util 0.7.15", + "tokio", + "tokio-util", ] [[package]] @@ -817,7 +778,7 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ - "cfg-if 1.0.1", + "cfg-if", ] [[package]] @@ -860,7 +821,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", - "rand_core 0.6.4", + "rand_core", "typenum", ] @@ -1022,7 +983,7 @@ version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ - "cfg-if 1.0.1", + "cfg-if", ] [[package]] @@ -1115,18 +1076,12 @@ version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" dependencies = [ - "cfg-if 1.0.1", + "cfg-if", "libc", "libredox", "windows-sys 0.59.0", ] -[[package]] -name = "fixedbitset" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" - [[package]] name = "fixedbitset" version = "0.4.2" @@ -1170,25 +1125,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" dependencies = [ "libc", - "winapi 0.3.9", -] - -[[package]] -name = "fuchsia-zircon" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" -dependencies = [ - "bitflags 1.3.2", - "fuchsia-zircon-sys", + "winapi", ] -[[package]] -name = "fuchsia-zircon-sys" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" - [[package]] name = "futures" version = "0.3.31" @@ -1279,7 +1218,7 @@ dependencies = [ "futures-sink", "futures-task", "memchr", - "pin-project-lite 0.2.16", + "pin-project-lite", "pin-utils", "slab", ] @@ -1303,24 +1242,13 @@ dependencies = [ "version_check", ] -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if 1.0.1", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - [[package]] name = "getrandom" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ - "cfg-if 1.0.1", + "cfg-if", "libc", "wasi 0.11.1+wasi-snapshot-preview1", ] @@ -1331,7 +1259,7 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ - "cfg-if 1.0.1", + "cfg-if", "libc", "r-efi", "wasi 0.14.2+wasi-0.2.4", @@ -1364,7 +1292,7 @@ dependencies = [ "gl-client", "hex", "thiserror 2.0.12", - "tokio 1.46.1", + "tokio", "vls-core", ] @@ -1373,27 +1301,27 @@ name = "gl-client" version = "0.3.2" dependencies = [ "anyhow", - "async-stream 0.3.6", + "async-stream", "async-trait", "base64 0.21.7", "bech32 0.9.1", - "bytes 1.10.1", + "bytes", "chacha20poly1305", "chrono", "cln-grpc", "futures", "hex", "http", - "http-body 0.4.6", + "http-body", "log", "mockall", "picky", "picky-asn1-der 0.4.1", "picky-asn1-x509 0.15.2", - "pin-project 1.1.10", + "pin-project", "prost 0.12.6", "prost-derive 0.12.6", - "rand 0.8.5", + "rand", "rcgen", "reqwest", "ring 0.16.20", @@ -1406,11 +1334,11 @@ dependencies = [ "tempfile", "thiserror 1.0.69", "time", - "tokio 1.46.1", + "tokio", "tokio-stream", "tonic 0.11.0", "tonic-build 0.11.0", - "tower 0.4.13", + "tower", "url", "uuid", "vls-core", @@ -1425,7 +1353,7 @@ name = "gl-client-py" version = "0.3.0" dependencies = [ "anyhow", - "bytes 1.10.1", + "bytes", "env_logger 0.10.2", "gl-client", "hex", @@ -1436,7 +1364,7 @@ dependencies = [ "runeauth", "serde_json", "thiserror 1.0.69", - "tokio 1.46.1", + "tokio", "tonic 0.11.0", ] @@ -1448,9 +1376,9 @@ dependencies = [ "bip39", "gl-client", "hex", - "rand 0.8.5", + "rand", "runeauth", - "tokio 1.46.1", + "tokio", ] [[package]] @@ -1458,9 +1386,9 @@ name = "gl-plugin" version = "0.3.0" dependencies = [ "anyhow", - "async-stream 0.3.6", + "async-stream", "base64 0.21.7", - "bytes 1.10.1", + "bytes", "clightningrpc", "cln-grpc", "cln-plugin", @@ -1471,7 +1399,7 @@ dependencies = [ "gl-util", "governor", "hex", - "hyper 0.14.32", + "hyper", "lazy_static", "linemux", "log", @@ -1481,12 +1409,12 @@ dependencies = [ "serde_json", "sled", "thiserror 1.0.69", - "tokio 1.46.1", + "tokio", "tokio-stream", - "tokio-util 0.7.15", + "tokio-util", "tonic 0.11.0", "tonic-build 0.11.0", - "tower 0.4.13", + "tower", "vls-protocol", ] @@ -1499,19 +1427,19 @@ dependencies = [ "env_logger 0.10.2", "libc", "log", - "prost 0.6.1", - "tokio 0.2.25", - "tonic 0.3.1", - "tonic-build 0.3.1", - "tower 0.3.1", - "which 4.4.2", + "prost 0.11.9", + "tokio", + "tonic 0.8.3", + "tonic-build 0.8.4", + "tower", + "which", ] [[package]] name = "gl-util" version = "0.1.0" dependencies = [ - "bytes 1.10.1", + "bytes", "cln-rpc", "serde", "serde_json", @@ -1524,7 +1452,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c390a940a5d157878dd057c78680a33ce3415bcd05b4799509ea44210914b4d5" dependencies = [ - "cfg-if 1.0.1", + "cfg-if", "futures", "futures-timer", "no-std-compat", @@ -1533,33 +1461,13 @@ dependencies = [ "smallvec", ] -[[package]] -name = "h2" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e4728fd124914ad25e99e3d15a9361a879f6620f63cb56bbb08f95abb97a535" -dependencies = [ - "bytes 0.5.6", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap 1.9.3", - "slab", - "tokio 0.2.25", - "tokio-util 0.3.1", - "tracing", - "tracing-futures", -] - [[package]] name = "h2" version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" dependencies = [ - "bytes 1.10.1", + "bytes", "fnv", "futures-core", "futures-sink", @@ -1567,8 +1475,8 @@ dependencies = [ "http", "indexmap 2.10.0", "slab", - "tokio 1.46.1", - "tokio-util 0.7.15", + "tokio", + "tokio-util", "tracing", ] @@ -1595,12 +1503,9 @@ checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" [[package]] name = "heck" -version = "0.3.3" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "heck" @@ -1656,19 +1561,9 @@ version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ - "bytes 1.10.1", + "bytes", "fnv", - "itoa 1.0.15", -] - -[[package]] -name = "http-body" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" -dependencies = [ - "bytes 0.5.6", - "http", + "itoa", ] [[package]] @@ -1677,9 +1572,9 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ - "bytes 1.10.1", + "bytes", "http", - "pin-project-lite 0.2.16", + "pin-project-lite", ] [[package]] @@ -1688,12 +1583,6 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" -[[package]] -name = "httpdate" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47" - [[package]] name = "httpdate" version = "1.0.3" @@ -1715,49 +1604,25 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" -[[package]] -name = "hyper" -version = "0.13.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a6f157065790a3ed2f88679250419b5cdd96e714a0d65f7797fd337186e96bb" -dependencies = [ - "bytes 0.5.6", - "futures-channel", - "futures-core", - "futures-util", - "h2 0.2.7", - "http", - "http-body 0.3.1", - "httparse", - "httpdate 0.3.2", - "itoa 0.4.8", - "pin-project 1.1.10", - "socket2 0.3.19", - "tokio 0.2.25", - "tower-service", - "tracing", - "want", -] - [[package]] name = "hyper" version = "0.14.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" dependencies = [ - "bytes 1.10.1", + "bytes", "futures-channel", "futures-core", "futures-util", - "h2 0.3.27", + "h2", "http", - "http-body 0.4.6", + "http-body", "httparse", - "httpdate 1.0.3", - "itoa 1.0.15", - "pin-project-lite 0.2.16", - "socket2 0.5.10", - "tokio 1.46.1", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", "tower-service", "tracing", "want", @@ -1771,9 +1636,9 @@ checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http", - "hyper 0.14.32", + "hyper", "rustls 0.21.12", - "tokio 1.46.1", + "tokio", "tokio-rustls 0.24.1", ] @@ -1783,9 +1648,9 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" dependencies = [ - "hyper 0.14.32", - "pin-project-lite 0.2.16", - "tokio 1.46.1", + "hyper", + "pin-project-lite", + "tokio", "tokio-io-timeout", ] @@ -1987,7 +1852,7 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ - "cfg-if 1.0.1", + "cfg-if", ] [[package]] @@ -1997,16 +1862,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013" dependencies = [ "bitflags 2.9.1", - "cfg-if 1.0.1", - "libc", -] - -[[package]] -name = "iovec" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" -dependencies = [ + "cfg-if", "libc", ] @@ -2033,15 +1889,6 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" -[[package]] -name = "itertools" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.10.5" @@ -2060,12 +1907,6 @@ dependencies = [ "either", ] -[[package]] -name = "itoa" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" - [[package]] name = "itoa" version = "1.0.15" @@ -2115,16 +1956,6 @@ dependencies = [ "cpufeatures", ] -[[package]] -name = "kernel32-sys" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -dependencies = [ - "winapi 0.2.8", - "winapi-build", -] - [[package]] name = "kqueue" version = "1.1.1" @@ -2208,8 +2039,8 @@ checksum = "51157eba73f3dae3b17ae3ea5b29a8ad0346bdff3881e9a00646b827db066a83" dependencies = [ "futures-util", "notify", - "pin-project-lite 0.2.16", - "tokio 1.46.1", + "pin-project-lite", + "tokio", ] [[package]] @@ -2297,25 +2128,6 @@ dependencies = [ "adler2", ] -[[package]] -name = "mio" -version = "0.6.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" -dependencies = [ - "cfg-if 0.1.10", - "fuchsia-zircon", - "fuchsia-zircon-sys", - "iovec", - "kernel32-sys", - "libc", - "log", - "miow 0.2.2", - "net2", - "slab", - "winapi 0.2.8", -] - [[package]] name = "mio" version = "0.8.11" @@ -2339,57 +2151,13 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "mio-named-pipes" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0840c1c50fd55e521b247f949c241c9997709f23bd7f023b9762cd561e935656" -dependencies = [ - "log", - "mio 0.6.23", - "miow 0.3.7", - "winapi 0.3.9", -] - -[[package]] -name = "mio-uds" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" -dependencies = [ - "iovec", - "libc", - "mio 0.6.23", -] - -[[package]] -name = "miow" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" -dependencies = [ - "kernel32-sys", - "net2", - "winapi 0.2.8", - "ws2_32-sys", -] - -[[package]] -name = "miow" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" -dependencies = [ - "winapi 0.3.9", -] - [[package]] name = "mockall" version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c84490118f2ee2d74570d114f3d0493cbf02790df303d2707606c3e14e07c96" dependencies = [ - "cfg-if 1.0.1", + "cfg-if", "downcast", "fragile", "lazy_static", @@ -2404,7 +2172,7 @@ version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" dependencies = [ - "cfg-if 1.0.1", + "cfg-if", "proc-macro2", "quote", "syn 1.0.109", @@ -2422,17 +2190,6 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" -[[package]] -name = "net2" -version = "0.2.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b13b648036a2339d06de780866fbdfda0dde886de7b3af2ddeba8b14f4ee34ac" -dependencies = [ - "cfg-if 0.1.10", - "libc", - "winapi 0.3.9", -] - [[package]] name = "nix" version = "0.30.1" @@ -2440,7 +2197,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ "bitflags 2.9.1", - "cfg-if 1.0.1", + "cfg-if", "cfg_aliases", "libc", ] @@ -2487,7 +2244,7 @@ dependencies = [ "libc", "mio 0.8.11", "walkdir", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -2497,7 +2254,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" dependencies = [ "overload", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -2522,7 +2279,7 @@ dependencies = [ "num-integer", "num-iter", "num-traits", - "rand 0.8.5", + "rand", "serde", "smallvec", "zeroize", @@ -2564,16 +2321,6 @@ dependencies = [ "libm", ] -[[package]] -name = "num_cpus" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" -dependencies = [ - "hermit-abi 0.5.2", - "libc", -] - [[package]] name = "object" version = "0.36.7" @@ -2664,12 +2411,12 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" dependencies = [ - "cfg-if 1.0.1", + "cfg-if", "instant", "libc", "redox_syscall 0.2.16", "smallvec", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -2678,7 +2425,7 @@ version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ - "cfg-if 1.0.1", + "cfg-if", "libc", "redox_syscall 0.5.13", "smallvec", @@ -2721,23 +2468,13 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" -[[package]] -name = "petgraph" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7" -dependencies = [ - "fixedbitset 0.2.0", - "indexmap 1.9.3", -] - [[package]] name = "petgraph" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ - "fixedbitset 0.4.2", + "fixedbitset", "indexmap 2.10.0", ] @@ -2756,7 +2493,7 @@ dependencies = [ "picky-asn1 0.3.3", "picky-asn1-der 0.2.5", "picky-asn1-x509 0.6.1", - "rand 0.8.5", + "rand", "rsa", "serde", "serde_json", @@ -2859,33 +2596,13 @@ dependencies = [ "serde", ] -[[package]] -name = "pin-project" -version = "0.4.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ef0f924a5ee7ea9cbcea77529dba45f8a9ba9f622419fe3386ca581a3ae9d5a" -dependencies = [ - "pin-project-internal 0.4.30", -] - [[package]] name = "pin-project" version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" dependencies = [ - "pin-project-internal 1.1.10", -] - -[[package]] -name = "pin-project-internal" -version = "0.4.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "851c8d0ce9bebe43790dedfc86614c23494ac9f423dd618d3a61fc693eafe61e" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "pin-project-internal", ] [[package]] @@ -2899,12 +2616,6 @@ dependencies = [ "syn 2.0.104", ] -[[package]] -name = "pin-project-lite" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777" - [[package]] name = "pin-project-lite" version = "0.2.16" @@ -2934,7 +2645,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" dependencies = [ - "cfg-if 1.0.1", + "cfg-if", "cpufeatures", "opaque-debug", "universal-hash 0.4.0", @@ -3009,6 +2720,16 @@ dependencies = [ "termtree", ] +[[package]] +name = "prettyplease" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" +dependencies = [ + "proc-macro2", + "syn 1.0.109", +] + [[package]] name = "prettyplease" version = "0.2.35" @@ -3030,12 +2751,12 @@ dependencies = [ [[package]] name = "prost" -version = "0.6.1" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce49aefe0a6144a45de32927c77bd2859a5f7677b55f220ae5b744e87389c212" +checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" dependencies = [ - "bytes 0.5.6", - "prost-derive 0.6.1", + "bytes", + "prost-derive 0.11.9", ] [[package]] @@ -3044,26 +2765,30 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" dependencies = [ - "bytes 1.10.1", + "bytes", "prost-derive 0.12.6", ] [[package]] name = "prost-build" -version = "0.6.1" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b10678c913ecbd69350e8535c3aef91a8676c0773fc1d7b95cdd196d7f2f26" +checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" dependencies = [ - "bytes 0.5.6", - "heck 0.3.3", - "itertools 0.8.2", + "bytes", + "heck 0.4.1", + "itertools 0.10.5", + "lazy_static", "log", "multimap 0.8.3", - "petgraph 0.5.1", - "prost 0.6.1", - "prost-types 0.6.1", + "petgraph", + "prettyplease 0.1.25", + "prost 0.11.9", + "prost-types 0.11.9", + "regex", + "syn 1.0.109", "tempfile", - "which 3.1.1", + "which", ] [[package]] @@ -3072,14 +2797,14 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22505a5c94da8e3b7c2996394d1c933236c4d743e81a410bcca4e6989fc066a4" dependencies = [ - "bytes 1.10.1", + "bytes", "heck 0.5.0", "itertools 0.12.1", "log", "multimap 0.10.1", "once_cell", - "petgraph 0.6.5", - "prettyplease", + "petgraph", + "prettyplease 0.2.35", "prost 0.12.6", "prost-types 0.12.6", "regex", @@ -3089,12 +2814,12 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.6.1" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "537aa19b95acde10a12fec4301466386f757403de4cd4e5b4fa78fb5ecb18f72" +checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" dependencies = [ "anyhow", - "itertools 0.8.2", + "itertools 0.10.5", "proc-macro2", "quote", "syn 1.0.109", @@ -3115,12 +2840,11 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.6.1" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1834f67c0697c001304b75be76f67add9c89742eda3a085ad8ee0bb38c3417aa" +checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" dependencies = [ - "bytes 0.5.6", - "prost 0.6.1", + "prost 0.11.9", ] [[package]] @@ -3138,7 +2862,7 @@ version = "0.18.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3b1ac5b3731ba34fdaa9785f8d74d17448cd18f30cf19e0c7e7b1fdb5272109" dependencies = [ - "cfg-if 1.0.1", + "cfg-if", "indoc", "libc", "memoffset", @@ -3214,20 +2938,6 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", - "rand_pcg", -] - [[package]] name = "rand" version = "0.8.5" @@ -3235,18 +2945,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", + "rand_chacha", + "rand_core", ] [[package]] @@ -3256,16 +2956,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", + "rand_core", ] [[package]] @@ -3277,24 +2968,6 @@ dependencies = [ "getrandom 0.2.16", ] -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "rand_pcg" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" -dependencies = [ - "rand_core 0.5.1", -] - [[package]] name = "rcgen" version = "0.10.0" @@ -3388,14 +3061,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ "base64 0.21.7", - "bytes 1.10.1", + "bytes", "encoding_rs", "futures-core", "futures-util", - "h2 0.3.27", + "h2", "http", - "http-body 0.4.6", - "hyper 0.14.32", + "http-body", + "hyper", "hyper-rustls", "ipnet", "js-sys", @@ -3403,7 +3076,7 @@ dependencies = [ "mime", "once_cell", "percent-encoding", - "pin-project-lite 0.2.16", + "pin-project-lite", "rustls 0.21.12", "rustls-native-certs", "rustls-pemfile 1.0.4", @@ -3412,7 +3085,7 @@ dependencies = [ "serde_urlencoded", "sync_wrapper", "system-configuration", - "tokio 1.46.1", + "tokio", "tokio-rustls 0.24.1", "tower-service", "url", @@ -3434,7 +3107,7 @@ dependencies = [ "spin 0.5.2", "untrusted 0.7.1", "web-sys", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -3444,7 +3117,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", - "cfg-if 1.0.1", + "cfg-if", "getrandom 0.2.16", "libc", "untrusted 0.9.0", @@ -3465,7 +3138,7 @@ dependencies = [ "num-iter", "num-traits", "pem 0.8.3", - "rand 0.8.5", + "rand", "simple_asn1", "subtle", "zeroize", @@ -3530,14 +3203,13 @@ dependencies = [ [[package]] name = "rustls" -version = "0.18.1" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d1126dcf58e93cee7d098dbda643b5f92ed724f1f6a63007c1116eed6700c81" +checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" dependencies = [ - "base64 0.12.3", "log", "ring 0.16.20", - "sct 0.6.1", + "sct", "webpki", ] @@ -3550,7 +3222,7 @@ dependencies = [ "log", "ring 0.17.14", "rustls-webpki 0.101.7", - "sct 0.7.1", + "sct", ] [[package]] @@ -3663,16 +3335,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "sct" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" -dependencies = [ - "ring 0.16.20", - "untrusted 0.7.1", -] - [[package]] name = "sct" version = "0.7.1" @@ -3699,7 +3361,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25996b82292a7a57ed3508f052cfff8640d38d32018784acd714758b43da9c8f" dependencies = [ "bitcoin_hashes 0.12.0", - "rand 0.8.5", + "rand", "secp256k1-sys 0.8.2", "serde", ] @@ -3803,7 +3465,7 @@ version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ - "itoa 1.0.15", + "itoa", "memchr", "ryu", "serde", @@ -3816,7 +3478,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", - "itoa 1.0.15", + "itoa", "ryu", "serde", ] @@ -3856,7 +3518,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" dependencies = [ "block-buffer 0.9.0", - "cfg-if 1.0.1", + "cfg-if", "cpufeatures", "digest 0.9.0", "opaque-debug", @@ -3869,7 +3531,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ "block-buffer 0.9.0", - "cfg-if 1.0.1", + "cfg-if", "cpufeatures", "digest 0.9.0", "opaque-debug", @@ -3881,7 +3543,7 @@ version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ - "cfg-if 1.0.1", + "cfg-if", "cpufeatures", "digest 0.10.7", ] @@ -3893,10 +3555,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f880fc8562bdeb709793f00eb42a2ad0e672c4f883bbe59122b926eca935c8f6" dependencies = [ "async-trait", - "bytes 1.10.1", + "bytes", "hex", "sha2 0.10.9", - "tokio 1.46.1", + "tokio", ] [[package]] @@ -3941,7 +3603,7 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ - "rand_core 0.6.4", + "rand_core", ] [[package]] @@ -3984,17 +3646,6 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" -[[package]] -name = "socket2" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" -dependencies = [ - "cfg-if 1.0.1", - "libc", - "winapi 0.3.9", -] - [[package]] name = "socket2" version = "0.5.10" @@ -4197,7 +3848,7 @@ version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" dependencies = [ - "cfg-if 1.0.1", + "cfg-if", ] [[package]] @@ -4207,7 +3858,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" dependencies = [ "deranged", - "itoa 1.0.15", + "itoa", "num-conv", "powerfmt", "serde", @@ -4256,30 +3907,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" -[[package]] -name = "tokio" -version = "0.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6703a273949a90131b290be1fe7b039d0fc884aa1935860dfcbe056f28cd8092" -dependencies = [ - "bytes 0.5.6", - "fnv", - "futures-core", - "iovec", - "lazy_static", - "libc", - "memchr", - "mio 0.6.23", - "mio-named-pipes", - "mio-uds", - "num_cpus", - "pin-project-lite 0.1.12", - "signal-hook-registry", - "slab", - "tokio-macros 0.2.6", - "winapi 0.3.9", -] - [[package]] name = "tokio" version = "1.46.1" @@ -4287,16 +3914,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17" dependencies = [ "backtrace", - "bytes 1.10.1", + "bytes", "io-uring", "libc", "mio 1.0.4", "parking_lot 0.12.4", - "pin-project-lite 0.2.16", + "pin-project-lite", "signal-hook-registry", "slab", - "socket2 0.5.10", - "tokio-macros 2.5.0", + "socket2", + "tokio-macros", "windows-sys 0.52.0", ] @@ -4306,19 +3933,8 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" dependencies = [ - "pin-project-lite 0.2.16", - "tokio 1.46.1", -] - -[[package]] -name = "tokio-macros" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e44da00bfc73a25f814cd8d7e57a68a5c31b74b3152a0a1d1f590c97ed06265a" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "pin-project-lite", + "tokio", ] [[package]] @@ -4334,13 +3950,12 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.14.1" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e12831b255bcfa39dc0436b01e19fea231a37db570686c06ee72c423479f889a" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" dependencies = [ - "futures-core", - "rustls 0.18.1", - "tokio 0.2.25", + "rustls 0.20.9", + "tokio", "webpki", ] @@ -4351,7 +3966,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ "rustls 0.21.12", - "tokio 1.46.1", + "tokio", ] [[package]] @@ -4362,7 +3977,7 @@ checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" dependencies = [ "rustls 0.22.4", "rustls-pki-types", - "tokio 1.46.1", + "tokio", ] [[package]] @@ -4372,23 +3987,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" dependencies = [ "futures-core", - "pin-project-lite 0.2.16", - "tokio 1.46.1", - "tokio-util 0.7.15", -] - -[[package]] -name = "tokio-util" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499" -dependencies = [ - "bytes 0.5.6", - "futures-core", - "futures-sink", - "log", - "pin-project-lite 0.1.12", - "tokio 0.2.25", + "pin-project-lite", + "tokio", + "tokio-util", ] [[package]] @@ -4397,39 +3998,42 @@ version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" dependencies = [ - "bytes 1.10.1", + "bytes", "futures-core", "futures-sink", - "pin-project-lite 0.2.16", - "tokio 1.46.1", + "pin-project-lite", + "tokio", ] [[package]] name = "tonic" -version = "0.3.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74a5d6e7439ecf910463667080de772a9c7ddf26bc9fb4f3252ac3862e43337d" +checksum = "8f219fad3b929bef19b1f86fbc0358d35daed8f2cac972037ac0dc10bbb8d5fb" dependencies = [ - "async-stream 0.2.1", + "async-stream", "async-trait", - "base64 0.12.3", - "bytes 0.5.6", + "axum", + "base64 0.13.1", + "bytes", "futures-core", "futures-util", + "h2", "http", - "http-body 0.3.1", - "hyper 0.13.10", + "http-body", + "hyper", + "hyper-timeout", "percent-encoding", - "pin-project 0.4.30", - "prost 0.6.1", - "prost-derive 0.6.1", - "tokio 0.2.25", - "tokio-rustls 0.14.1", - "tokio-util 0.3.1", - "tower 0.3.1", - "tower-balance", - "tower-load", - "tower-make", + "pin-project", + "prost 0.11.9", + "prost-derive 0.11.9", + "rustls-pemfile 1.0.4", + "tokio", + "tokio-rustls 0.23.4", + "tokio-stream", + "tokio-util", + "tower", + "tower-layer", "tower-service", "tracing", "tracing-futures", @@ -4441,25 +4045,25 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76c4eb7a4e9ef9d4763600161f12f5070b92a578e1b634db88a6887844c91a13" dependencies = [ - "async-stream 0.3.6", + "async-stream", "async-trait", "axum", "base64 0.21.7", - "bytes 1.10.1", - "h2 0.3.27", + "bytes", + "h2", "http", - "http-body 0.4.6", - "hyper 0.14.32", + "http-body", + "hyper", "hyper-timeout", "percent-encoding", - "pin-project 1.1.10", + "pin-project", "prost 0.12.6", "rustls-pemfile 2.2.0", "rustls-pki-types", - "tokio 1.46.1", + "tokio", "tokio-rustls 0.25.0", "tokio-stream", - "tower 0.4.13", + "tower", "tower-layer", "tower-service", "tracing", @@ -4467,12 +4071,13 @@ dependencies = [ [[package]] name = "tonic-build" -version = "0.3.1" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19970cf58f3acc820962be74c4021b8bbc8e8a1c4e3a02095d0aa60cde5f3633" +checksum = "5bf5e9b9c0f7e0a7c027dcfaba7b2c60816c7049171f679d99ee2ff65d0de8c4" dependencies = [ + "prettyplease 0.1.25", "proc-macro2", - "prost-build 0.6.1", + "prost-build 0.11.9", "quote", "syn 1.0.109", ] @@ -4483,31 +4088,13 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4ef6dd70a610078cb4e338a0f79d06bc759ff1b22d2120c2ff02ae264ba9c2" dependencies = [ - "prettyplease", + "prettyplease 0.2.35", "proc-macro2", "prost-build 0.12.6", "quote", "syn 2.0.104", ] -[[package]] -name = "tower" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3169017c090b7a28fce80abaad0ab4f5566423677c9331bb320af7e49cfe62" -dependencies = [ - "futures-core", - "tower-buffer", - "tower-discover", - "tower-layer", - "tower-limit", - "tower-load-shed", - "tower-retry", - "tower-service", - "tower-timeout", - "tower-util", -] - [[package]] name = "tower" version = "0.4.13" @@ -4517,177 +4104,29 @@ dependencies = [ "futures-core", "futures-util", "indexmap 1.9.3", - "pin-project 1.1.10", - "pin-project-lite 0.2.16", - "rand 0.8.5", - "slab", - "tokio 1.46.1", - "tokio-util 0.7.15", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-balance" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a792277613b7052448851efcf98a2c433e6f1d01460832dc60bef676bc275d4c" -dependencies = [ - "futures-core", - "futures-util", - "indexmap 1.9.3", - "pin-project 0.4.30", - "rand 0.7.3", + "pin-project", + "pin-project-lite", + "rand", "slab", - "tokio 0.2.25", - "tower-discover", - "tower-layer", - "tower-load", - "tower-make", - "tower-ready-cache", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-buffer" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4887dc2a65d464c8b9b66e0e4d51c2fd6cf5b3373afc72805b0a60bce00446a" -dependencies = [ - "futures-core", - "pin-project 0.4.30", - "tokio 0.2.25", + "tokio", + "tokio-util", "tower-layer", "tower-service", "tracing", ] -[[package]] -name = "tower-discover" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f6b5000c3c54d269cc695dff28136bb33d08cbf1df2c48129e143ab65bf3c2a" -dependencies = [ - "futures-core", - "pin-project 0.4.30", - "tower-service", -] - [[package]] name = "tower-layer" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" -[[package]] -name = "tower-limit" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92c3040c5dbed68abffaa0d4517ac1a454cd741044f33ab0eefab6b8d1361404" -dependencies = [ - "futures-core", - "pin-project 0.4.30", - "tokio 0.2.25", - "tower-layer", - "tower-load", - "tower-service", -] - -[[package]] -name = "tower-load" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cc79fc3afd07492b7966d7efa7c6c50f8ed58d768a6075dd7ae6591c5d2017b" -dependencies = [ - "futures-core", - "log", - "pin-project 0.4.30", - "tokio 0.2.25", - "tower-discover", - "tower-service", -] - -[[package]] -name = "tower-load-shed" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f021e23900173dc315feb4b6922510dae3e79c689b74c089112066c11f0ae4e" -dependencies = [ - "futures-core", - "pin-project 0.4.30", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-make" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce50370d644a0364bf4877ffd4f76404156a248d104e2cc234cd391ea5cdc965" -dependencies = [ - "tokio 0.2.25", - "tower-service", -] - -[[package]] -name = "tower-ready-cache" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eabb6620e5481267e2ec832c780b31cad0c15dcb14ed825df5076b26b591e1f" -dependencies = [ - "futures-core", - "futures-util", - "indexmap 1.9.3", - "log", - "tokio 0.2.25", - "tower-service", -] - -[[package]] -name = "tower-retry" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6727956aaa2f8957d4d9232b308fe8e4e65d99db30f42b225646e86c9b6a952" -dependencies = [ - "futures-core", - "pin-project 0.4.30", - "tokio 0.2.25", - "tower-layer", - "tower-service", -] - [[package]] name = "tower-service" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" -[[package]] -name = "tower-timeout" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "127b8924b357be938823eaaec0608c482d40add25609481027b96198b2e4b31e" -dependencies = [ - "pin-project 0.4.30", - "tokio 0.2.25", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-util" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1093c19826d33807c72511e68f73b4a0469a3f22c2bd5f7d5212178b4b89674" -dependencies = [ - "futures-core", - "futures-util", - "pin-project 0.4.30", - "tower-service", -] - [[package]] name = "tracing" version = "0.1.41" @@ -4695,7 +4134,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "log", - "pin-project-lite 0.2.16", + "pin-project-lite", "tracing-attributes", "tracing-core", ] @@ -4727,7 +4166,7 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" dependencies = [ - "pin-project 1.1.10", + "pin-project", "tracing", ] @@ -4799,12 +4238,6 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "unicode-segmentation" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" - [[package]] name = "unicode-xid" version = "0.2.6" @@ -5009,12 +4442,6 @@ dependencies = [ "try-lock", ] -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" @@ -5036,7 +4463,7 @@ version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ - "cfg-if 1.0.1", + "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", @@ -5062,7 +4489,7 @@ version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ - "cfg-if 1.0.1", + "cfg-if", "js-sys", "once_cell", "wasm-bindgen", @@ -5113,21 +4540,12 @@ dependencies = [ [[package]] name = "webpki" -version = "0.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" -dependencies = [ - "ring 0.16.20", - "untrusted 0.7.1", -] - -[[package]] -name = "which" -version = "3.1.1" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724" +checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" dependencies = [ - "libc", + "ring 0.17.14", + "untrusted 0.9.0", ] [[package]] @@ -5142,12 +4560,6 @@ dependencies = [ "rustix 0.38.44", ] -[[package]] -name = "winapi" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" - [[package]] name = "winapi" version = "0.3.9" @@ -5158,12 +4570,6 @@ dependencies = [ "winapi-x86_64-pc-windows-gnu", ] -[[package]] -name = "winapi-build" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" - [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" @@ -5471,7 +4877,7 @@ version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "cfg-if 1.0.1", + "cfg-if", "windows-sys 0.48.0", ] @@ -5490,16 +4896,6 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" -[[package]] -name = "ws2_32-sys" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" -dependencies = [ - "winapi 0.2.8", - "winapi-build", -] - [[package]] name = "x509-certificate" version = "0.23.1" @@ -5507,7 +4903,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66534846dec7a11d7c50a74b7cdb208b9a581cad890b7866430d438455847c85" dependencies = [ "bcder", - "bytes 1.10.1", + "bytes", "chrono", "der", "hex", diff --git a/libs/gl-client-py/glclient/__init__.py b/libs/gl-client-py/glclient/__init__.py index 1b7d26c96..b07362b40 100644 --- a/libs/gl-client-py/glclient/__init__.py +++ b/libs/gl-client-py/glclient/__init__.py @@ -9,7 +9,6 @@ from binascii import hexlify, unhexlify from typing import Optional, List, Iterable, Any, Type, TypeVar import logging -from glclient.lsps import LspClient from glclient.glclient import Credentials @@ -505,12 +504,6 @@ def list_datastore(self, key=None): res = clnpb.ListdatastoreResponse return res.FromString(bytes(self.inner.call(uri, bytes(req)))) - def get_lsp_client( - self, - ) -> LspClient: - native_lsps = self.inner.get_lsp_client() - return LspClient(native_lsps) - def configure(self, close_to_addr: str) -> None: req = nodepb.GlConfig(close_to_addr=close_to_addr).SerializeToString() diff --git a/libs/gl-client-py/glclient/lsps.py b/libs/gl-client-py/glclient/lsps.py deleted file mode 100644 index 45504c6b2..000000000 --- a/libs/gl-client-py/glclient/lsps.py +++ /dev/null @@ -1,102 +0,0 @@ -from dataclasses import dataclass, is_dataclass, asdict, field - -import typing as t -import json -import time -import binascii - -import glclient.glclient as native - -import logging - -logger = logging.getLogger(__name__) - -class EnhancedJSONEncoder(json.JSONEncoder): - def default(self, o): - if is_dataclass(o): - return asdict(o) - elif isinstance(o, NoParams): - return dict() - elif isinstance(o, type) and o.__name__ == "NoParams": - return dict() - return super().default(o) - - -class AsDataClassDescriptor: - """Descriptor that allows to initialize a nested dataclass from a nested directory""" - - def __init__(self, *, cls): - self._cls = cls - - def __set_name__(self, owner, name): - self._name = f"_{name}" - - def __get__(self, obj, type): - return getattr(obj, self._name, None) - - def __set__(self, obj, value): - if isinstance(value, self._cls): - setattr(obj, self._name, value) - else: - setattr(obj, self._name, self._cls(**value)) - - -def _dump_json_bytes(object: t.Any) -> bytes: - json_str: str = json.dumps(object, cls=EnhancedJSONEncoder) - json_bytes: bytes = json_str.encode("utf-8") - return json_bytes - - -@dataclass -class ProtocolList: - protocols: t.List[int] - - -@dataclass -class Lsps1Options: - minimum_channel_confirmations: t.Optional[int] - minimum_onchain_payment_confirmations: t.Optional[int] - supports_zero_channel_reserve: t.Optional[bool] - min_onchain_payment_size_sat: t.Optional[int] - max_channel_expiry_blocks: t.Optional[int] - min_initial_client_balance_sat: t.Optional[int] - min_initial_lsp_balance_sat: t.Optional[int] - max_initial_client_balance_sat: t.Optional[int] - min_channel_balance_sat: t.Optional[int] - max_channel_balance_sat: t.Optional[int] - - -class NoParams: - pass - - -class LspClient: - def __init__(self, native: native.LspClient): - self._native = native - - def _rpc_call( - self, - peer_id: str, - method_name: str, - param_json: bytes, - json_rpc_id: t.Optional[str] = None, - ) -> bytes: - logger.debug("Request lsp to peer %s and method %s", peer_id, method_name) - peer_id_bytes = bytes.fromhex(peer_id) - if json_rpc_id is None: - return self._native.rpc_call(peer_id_bytes, method_name, param_json) - else: - return self._native.rpc_call_with_json_rpc_id( - peer_id_bytes, method_name, param_json, json_rpc_id=json_rpc_id - ) - - def list_lsp_servers(self) -> t.List[str]: - return self._native.list_lsp_servers() - - def list_protocols(self, peer_id, json_rpc_id: t.Optional[str] = None) -> ProtocolList: - json_bytes = _dump_json_bytes(NoParams) - result = self._rpc_call( - peer_id, "lsps0.list_protocols", json_bytes, json_rpc_id=json_rpc_id - ) - response_dict = json.loads(result) - return ProtocolList(**response_dict) diff --git a/libs/gl-client-py/src/lib.rs b/libs/gl-client-py/src/lib.rs index 2542d2986..6e102020c 100644 --- a/libs/gl-client-py/src/lib.rs +++ b/libs/gl-client-py/src/lib.rs @@ -5,7 +5,6 @@ use pyo3::prelude::*; extern crate log; mod credentials; -mod lsps; mod node; mod pairing; mod runtime; @@ -13,7 +12,6 @@ mod scheduler; mod signer; mod tls; -pub use lsps::LspClient; pub use node::Node; pub use scheduler::Scheduler; pub use signer::{Signer, SignerHandle}; @@ -41,7 +39,6 @@ fn glclient(_py: Python, m: &PyModule) -> PyResult<()> { m.add_class::()?; m.add_class::()?; m.add_class::()?; - m.add_class::()?; m.add_class::()?; m.add_class::()?; m.add_class::()?; diff --git a/libs/gl-client-py/src/node.rs b/libs/gl-client-py/src/node.rs index 0ce1237c9..06418413c 100644 --- a/libs/gl-client-py/src/node.rs +++ b/libs/gl-client-py/src/node.rs @@ -1,6 +1,6 @@ +use crate::credentials::Credentials; use crate::runtime::exec; use crate::scheduler::convert; -use crate::{credentials::Credentials, lsps::LspClient}; use gl_client as gl; use gl_client::pb; use prost::Message; @@ -82,10 +82,6 @@ impl Node { convert(Ok(res)) } - fn get_lsp_client(&self) -> LspClient { - LspClient::new(self.client.clone(), self.cln_client.clone()) - } - fn configure(&self, payload: &[u8]) -> PyResult<()> { let req = pb::GlConfig::decode(payload).map_err(error_decoding_request)?; diff --git a/libs/gl-client/src/lib.rs b/libs/gl-client/src/lib.rs index 6e34f67f2..08655d864 100644 --- a/libs/gl-client/src/lib.rs +++ b/libs/gl-client/src/lib.rs @@ -53,9 +53,6 @@ pub mod utils; pub mod credentials; -/// Functionality to integrate greenlight with a Lightning Service Provider -pub mod lsps; - pub mod util; use thiserror::Error; diff --git a/libs/gl-client/src/lsps/client.rs b/libs/gl-client/src/lsps/client.rs deleted file mode 100644 index 251a6d460..000000000 --- a/libs/gl-client/src/lsps/client.rs +++ /dev/null @@ -1,173 +0,0 @@ -// -// This is an implementation of the LSPS1 specification (https://github.com/BitcoinAndLightningLayerSpecs/lsp/blob/main/OpenAPI/LSPS1.json) -// -// This specification describes how a lightning-node should communicate with -// a Lightning Service Provider. -// -// The key-idea is that all communication occurs using JSON-rpc and BOLT8-messages -// are used as the transport layer. All messages related to LSPS will start -// with the LSPS_MESSAGE_ID. -// -use super::json_rpc::{generate_random_rpc_id, JsonRpcResponse}; -use super::json_rpc_erased::JsonRpcMethodUnerased; -use crate::lsps::error::LspsError; -use crate::node::{Client, ClnClient}; -use crate::pb::{Custommsg, StreamCustommsgRequest}; -use crate::util::is_feature_bit_enabled; -use cln_grpc::pb::{ListnodesRequest, SendcustommsgRequest}; -use std::io::{Cursor, Read, Write}; - -// BOLT8 message ID 37913 -const LSPS_MESSAGE_ID: [u8; 2] = [0x94, 0x19]; -const TIMEOUT_MILLIS: u128 = 30_000; -const BITNUM_LSP_FEATURE: usize = 729; - -pub struct LspClient { - client: Client, // Used for receiving custom messages - cln_client: ClnClient, // USed for sending custom message -} - -impl LspClient { - pub fn new(client: Client, cln_client: ClnClient) -> Self { - Self { - client, - cln_client, - } - } - - /// Create a JSON-rpc request to a LSPS - /// - /// # Arguments: - /// - peer_id: the node_id of the lsps - /// - method: the request method as defined in the LSPS - /// - param: the request parameter - /// - pub async fn request<'a, I, O, E>( - &mut self, - peer_id: &[u8], - method: &impl JsonRpcMethodUnerased<'a, I, O, E>, - param: I, - ) -> Result, LspsError> - where - I: serde::Serialize, - E: serde::de::DeserializeOwned, - O: serde::de::DeserializeOwned, - { - let json_rpc_id = generate_random_rpc_id(); - self - .request_with_json_rpc_id(peer_id, method, param, json_rpc_id) - .await - } - - /// Makes the jsonrpc request and returns the response - /// - /// This method allows the user to specify a custom json_rpc_id. - /// To ensure compliance with LSPS0 the use of `[request`] is recommended. - /// For some testing scenario's it is useful to have a reproducable json_rpc_id - pub async fn request_with_json_rpc_id<'a, I, O, E>( - &mut self, - peer_id: &[u8], - method: &impl JsonRpcMethodUnerased<'a, I, O, E>, - param: I, - json_rpc_id: String, - ) -> Result, LspsError> - where - I: serde::Serialize, - E: serde::de::DeserializeOwned, - O: serde::de::DeserializeOwned, - { - // Constructs the JsonRpcRequest - let request = method - .create_request(param, json_rpc_id) - .map_err(LspsError::JsonParseRequestError)?; - - // Core-lightning uses the convention that the first two bytes are the BOLT-8 message id - let mut cursor: Cursor> = std::io::Cursor::new(Vec::new()); - cursor.write_all(&LSPS_MESSAGE_ID)?; - serde_json::to_writer(&mut cursor, &request) - .map_err(LspsError::JsonParseRequestError)?; - - let custom_message_request = SendcustommsgRequest { - node_id: peer_id.to_vec(), - msg: cursor.into_inner(), - }; - - // Here we start listening to the responses. - // It is important that we do this before we send the first message - // to prevent high-latency clients from missing the response - // - // The await ensures that we are ready to get incoming messages - let mut stream: tonic::Streaming = self - .client - .stream_custommsg(StreamCustommsgRequest {}) - .await? - .into_inner(); - - // Sends the JsonRpcRequest - // Once the await has completed our greenlight node has successfully send the message - // The LSPS did probably not respond yet - self.cln_client - .send_custom_msg(custom_message_request) - .await?; - - // We read all incoming messages one-by-one - // If the peer_id, LSPS_MESSAGE_ID and the id used in json_rpc matches we return the result - let loop_start_instant = std::time::Instant::now(); - while let Some(mut msg) = stream.message().await? { - // Skip if peer_id doesn't match - if msg.peer_id != peer_id { - continue; - } - - // Skip if LSPS_MESSAGE_ID (first 2 bytes) doesn't match - let mut msg_cursor: Cursor<&mut [u8]> = std::io::Cursor::new(msg.payload.as_mut()); - let mut msg_bolt8_id: [u8; 2] = [0, 0]; - msg_cursor.read_exact(&mut msg_bolt8_id)?; - - if msg_bolt8_id != LSPS_MESSAGE_ID { - continue; - } - - // Deserialize the JSON compare the json_rpc_id - // If it matches we return a typed JsonRpcRequest - let value: serde_json::Value = serde_json::from_reader(&mut msg_cursor) - .map_err(LspsError::JsonParseResponseError)?; - if value.get("id").and_then(|x| x.as_str()) == Some(&request.id) { - // There is a bug here. We need to do the parsing in the underlying trait - let rpc_response = method - .parse_json_response_value(value) - .map_err(LspsError::JsonParseResponseError)?; - return Ok(rpc_response); - } - - // Check if the connection timed-out - // TODO: - // This is implementation is somewhat flawed. - // If no message comes in it will wait forever. - // - // I might have to add a built-in time-out mechanism in StreamCustomMsg or come up with - // a better solution. - if loop_start_instant.elapsed().as_millis() >= TIMEOUT_MILLIS { - return Err(LspsError::Timeout); - } - } - - // If the stream was closed - Err(LspsError::ConnectionClosed) - } - - pub async fn list_lsp_servers(&mut self) -> Result>, LspsError> { - let request = ListnodesRequest { id: None }; - - // Query all known lightning-nodes - let response = self.cln_client.list_nodes(request).await?; - - return Ok(response - .into_inner() - .nodes - .into_iter() - .filter(|x| is_feature_bit_enabled(x.features(), BITNUM_LSP_FEATURE)) - .map(|n| n.nodeid) - .collect()); - } -} diff --git a/libs/gl-client/src/lsps/error.rs b/libs/gl-client/src/lsps/error.rs deleted file mode 100644 index 017176872..000000000 --- a/libs/gl-client/src/lsps/error.rs +++ /dev/null @@ -1,48 +0,0 @@ -use thiserror::Error; - -#[derive(Error, Debug)] -pub enum LspsError { - #[error("Unknown method")] - MethodUnknown(String), - #[error("Failed to parse json-request")] - JsonParseRequestError(serde_json::Error), - #[error("Failed to parse json-response")] - JsonParseResponseError(serde_json::Error), - #[error("Error while calling lightning grpc-method")] - GrpcError(#[from] tonic::Status), - #[error("Connection closed")] - ConnectionClosed, - #[error("Timeout")] - Timeout, - #[error("Something unexpected happened")] - Other(String), -} - -impl From for LspsError { - fn from(value: std::io::Error) -> Self { - Self::Other(value.to_string()) - } -} - -pub fn map_json_rpc_error_code_to_str(code: i64) -> &'static str { - match code { - -32700 => "parsing_error", - -32600 => "invalid_request", - -32601 => "method_not_found", - -32602 => "invalid_params", - -32603 => "internal_error", - -32099..=-32000 => "implementation_defined_server_error", - _ => "unknown_error_code", - } -} - -#[cfg(test)] -mod test { - use crate::lsps::error::map_json_rpc_error_code_to_str; - - #[test] - fn test_map_json_rpc_error_code_to_str() { - assert_eq!(map_json_rpc_error_code_to_str(12), "unknown_error_code"); - assert_eq!(map_json_rpc_error_code_to_str(-32603), "internal_error"); - } -} diff --git a/libs/gl-client/src/lsps/json_rpc.rs b/libs/gl-client/src/lsps/json_rpc.rs deleted file mode 100644 index 0ffe4fa7d..000000000 --- a/libs/gl-client/src/lsps/json_rpc.rs +++ /dev/null @@ -1,355 +0,0 @@ -use base64::Engine as _; -use serde::de::DeserializeOwned; -use serde::ser::SerializeMap; -use serde::{Deserialize, Serialize}; - -use super::error::map_json_rpc_error_code_to_str; - -/// Generate a random json_rpc_id string that follows the requirements of LSPS0 -/// -/// - Should be a String -/// - Should be at generated using at least 80 bits of randomness -pub fn generate_random_rpc_id() -> String { - // The specification requires an id using least 80 random bits of randomness - let seed: [u8; 10] = rand::random(); - base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(seed) -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct JsonRpcMethod -where - E: MapErrorCode, -{ - pub method: &'static str, - #[serde(skip_serializing)] - request: std::marker::PhantomData, - #[serde(skip_serializing)] - return_type: std::marker::PhantomData, - #[serde(skip_serializing)] - error_type: std::marker::PhantomData, -} - -impl JsonRpcMethod -where - E: MapErrorCode, -{ - pub const fn new(method: &'static str) -> Self { - Self { - method, - request: std::marker::PhantomData, - return_type: std::marker::PhantomData, - error_type: std::marker::PhantomData, - } - } - - pub const fn name(&self) -> &'static str { - self.method - } - - pub fn create_request(&self, params: I, json_rpc_id: String) -> JsonRpcRequest { - JsonRpcRequest:: { - jsonrpc: String::from("2.0"), - id: json_rpc_id, - method: self.method.into(), - params, - } - } -} - -impl JsonRpcMethod -where - E: MapErrorCode, -{ - pub fn create_request_no_params(&self, json_rpc_id: String) -> JsonRpcRequest { - self.create_request(NoParams::default(), json_rpc_id) - } -} - -impl std::convert::From<&JsonRpcMethod> for String -where - E: MapErrorCode - { - fn from(value: &JsonRpcMethod) -> Self { - value.method.into() - } -} - -impl<'de, I, O, E> JsonRpcMethod -where - O: Deserialize<'de>, - E: Deserialize<'de> + MapErrorCode, -{ - pub fn parse_json_response_str( - &self, - json_str: &'de str, - ) -> Result, serde_json::Error> { - serde_json::from_str(json_str) - } -} - -impl JsonRpcMethod -where - O: DeserializeOwned, - E: DeserializeOwned + MapErrorCode, -{ - pub fn parse_json_response_value( - &self, - json_value: serde_json::Value, - ) -> Result, serde_json::Error> { - serde_json::from_value(json_value) - } -} - -// We only intend to implement to implement an LSP-client and only intend on sending requests -// Therefore, we only implement the serialization of requests -// -// D is the data-type of the request-data -// R is the data-type of the result if the query is successful -#[derive(Serialize, Deserialize, Debug)] -pub struct JsonRpcRequest { - pub jsonrpc: String, - pub id: String, - pub method: String, - pub params: I, -} - -// LSPS0 specifies that the RPC-request must use a parameter-by-name structure. -// -// A JSONRpcRequest<(),()> will be serialized to a json where "params" : null -// A JsonRpcRequest will be serialized to "params" : {} which is compliant -#[derive(Debug, Default, Clone, Deserialize, PartialEq)] -pub struct NoParams {} - -// Serde serializes NoParams to null by default -// LSPS0 requires an empty dictionary in this situation -impl Serialize for NoParams { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_map(Some(0))?.end() - } -} - -impl JsonRpcRequest { - pub fn new(method: JsonRpcMethod, params: I) -> Self - where - E: MapErrorCode, - { - return Self { - jsonrpc: String::from("2.0"), - id: generate_random_rpc_id(), - method: method.method.into(), - params, - } - } -} - -impl JsonRpcRequest { - pub fn new_no_params(method: JsonRpcMethod) -> Self - where - E: MapErrorCode, - { - return Self { - jsonrpc: String::from("2.0"), - id: generate_random_rpc_id(), - method: method.method.into(), - params: NoParams::default(), - } - } -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct JsonRpcResponseSuccess { - pub id: String, - pub result: O, - pub jsonrpc: String, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct JsonRpcResponseFailure { - pub id: String, - pub error: ErrorData, - pub jsonrpc: String, -} - -#[derive(Debug, Serialize, Deserialize)] -#[serde(untagged)] -pub enum JsonRpcResponse { - Error(JsonRpcResponseFailure), - Ok(JsonRpcResponseSuccess), -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct ErrorData { - pub code: i64, - pub message: String, - pub data: Option, -} - -impl ErrorData -where - E: MapErrorCode, -{ - pub fn code_str(&self) -> &str { - return E::get_code_str(self.code); - } -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct DefaultError; - -pub trait MapErrorCode { - fn get_code_str(code: i64) -> &'static str; -} - -impl MapErrorCode for DefaultError { - fn get_code_str(code: i64) -> &'static str { - map_json_rpc_error_code_to_str(code) - } -} - -#[cfg(test)] -mod test { - - use super::*; - - #[test] - fn serialize_no_params() { - let no_params = NoParams::default(); - let json_str = serde_json::to_string(&no_params).unwrap(); - - assert_eq!(json_str, "{}") - } - - #[test] - fn deserialize_no_params() { - let _: NoParams = serde_json::from_str("{}").unwrap(); - } - - #[test] - fn serialize_json_rpc_request() { - let rpc_request = JsonRpcRequest { - id: "abcefg".into(), - jsonrpc: "2.0".into(), - params: NoParams::default(), - method: "test.method".into(), - }; - - let json_str = serde_json::to_string(&rpc_request).unwrap(); - - let value: serde_json::Value = serde_json::from_str(&json_str).unwrap(); - assert_eq!(value.get("jsonrpc").unwrap(), "2.0"); - assert_eq!(value.get("id").unwrap(), &rpc_request.id); - assert_eq!(value.get("method").unwrap(), "test.method"); - assert!(value.get("params").unwrap().as_object().unwrap().is_empty()) - } - - #[test] - fn serialize_json_rpc_response_success() { - let rpc_response_ok: JsonRpcResponseSuccess = JsonRpcResponseSuccess { - id: String::from("abc"), - result: String::from("result_data"), - jsonrpc: String::from("2.0"), - }; - - let rpc_response: JsonRpcResponse = JsonRpcResponse::Ok(rpc_response_ok); - - let json_str: String = serde_json::to_string(&rpc_response).unwrap(); - - let value: serde_json::Value = serde_json::from_str(&json_str).unwrap(); - assert_eq!(value.get("jsonrpc").unwrap(), "2.0"); - assert_eq!(value.get("id").unwrap(), "abc"); - assert_eq!(value.get("result").unwrap(), "result_data") - } - - #[test] - fn serialize_json_rpc_response_error() { - let rpc_response: JsonRpcResponse = - JsonRpcResponse::Error(JsonRpcResponseFailure { - jsonrpc: String::from("2.0"), - id: String::from("abc"), - error: ErrorData { - code: -32700, - message: String::from("Failed to parse data"), - data: None, - }, - }); - - let json_str: String = serde_json::to_string(&rpc_response).unwrap(); - - let value: serde_json::Value = serde_json::from_str(&json_str).unwrap(); - assert_eq!(value.get("jsonrpc").unwrap(), "2.0"); - assert_eq!(value.get("id").unwrap(), "abc"); - assert_eq!(value.get("error").unwrap().get("code").unwrap(), -32700); - assert_eq!( - value.get("error").unwrap().get("message").unwrap(), - "Failed to parse data" - ); - } - - #[test] - fn create_rpc_request_from_call() { - let rpc_method = JsonRpcMethod::::new("test.method"); - let json_rpc_id = generate_random_rpc_id(); - let rpc_request = rpc_method.create_request_no_params(json_rpc_id); - - assert_eq!(rpc_request.method, "test.method"); - assert_eq!(rpc_request.jsonrpc, "2.0"); - assert_eq!(rpc_request.params, NoParams::default()); - } - - #[test] - fn parse_rpc_response_success_from_call() { - let rpc_method = JsonRpcMethod::::new("test.return_string"); - - let json_value = serde_json::json!({ - "jsonrpc" : "2.0", - "result" : "result_data", - "id" : "request_id" - }); - - let json_str = serde_json::to_string(&json_value).unwrap(); - - let result = rpc_method.parse_json_response_str(&json_str).unwrap(); - - match result { - JsonRpcResponse::Error(_) => panic!("Deserialized a good response but got panic"), - JsonRpcResponse::Ok(ok) => { - assert_eq!(ok.jsonrpc, "2.0"); - assert_eq!(ok.id, "request_id"); - assert_eq!(ok.result, "result_data") - } - } - } - - #[test] - fn parse_rpc_response_failure_from_call() { - let rpc_method = JsonRpcMethod::::new("test.return_string"); - - let json_value = serde_json::json!({ - "jsonrpc" : "2.0", - "error" : { "code" : -32700, "message" : "Failed to parse response"}, - "id" : "request_id" - }); - - let json_str = serde_json::to_string(&json_value).unwrap(); - - let result = rpc_method.parse_json_response_str(&json_str).unwrap(); - - match result { - JsonRpcResponse::Error(err) => { - assert_eq!(err.jsonrpc, "2.0"); - - assert_eq!(err.error.code, -32700); - assert_eq!(err.error.code_str(), "parsing_error"); - - assert_eq!(err.error.message, "Failed to parse response"); - assert_eq!(err.id, "request_id"); - } - JsonRpcResponse::Ok(_ok) => { - panic!("Failure deserialized as Ok") - } - } - } -} diff --git a/libs/gl-client/src/lsps/json_rpc_erased.rs b/libs/gl-client/src/lsps/json_rpc_erased.rs deleted file mode 100644 index 3f1f1211e..000000000 --- a/libs/gl-client/src/lsps/json_rpc_erased.rs +++ /dev/null @@ -1,372 +0,0 @@ -// The json-rpc implementation used in json_rpc is strongly typed and heavily -// relies on Generics. In Rust all the required types are created at compile type. -// -// This is, Vec and Vec are considered 2 different types. -// -// When creating language bindings we must either -// - explicitly implement ffi for every type. -// - use a trait that erases tye type. In this case we replace the type by Vec. This byte-array can be parsed by the foreign language -// -// Note, that this -// -// If you are a rust-developer you probably want to use the json_rpc-module directly -// If you are building a foreign function interface you probably want to use the type-erased version -// -// The JsonRpcMethodErased wraps the JsonRpcMethod into a type that works with Vec. -// The JsonRpcMethodErased is object-safe and can be owned using a Box. -// -// The JsonRpcMethodErased method -// - does not do strict type-checking at compile-time -// - comes at a small runtime cost (requires Box, serializes and deserializes some objects twice, unwrapping results) -// - comes at a small dev-cost for requiring a bit more error-handling - -use crate::lsps::json_rpc::{ - ErrorData, JsonRpcMethod, JsonRpcRequest, JsonRpcResponse, JsonRpcResponseFailure, - JsonRpcResponseSuccess, MapErrorCode, -}; -use serde::Serialize; - -pub type JsonRpcRequestErased = JsonRpcRequest>; -pub type JsonRpcResponseErased = JsonRpcResponse, Vec>; -pub type JsonRpcResponseSuccessErased = JsonRpcResponseSuccess>; -pub type JsonRpcResponseFailureErased = JsonRpcResponseFailure>; -pub type JsonRpcErrorDataErased = ErrorData>; - -pub trait JsonRpcMethodErased { - fn name(&self) -> &str; - - fn create_request( - &self, - params: Vec, - json_rpc_id: String, - ) -> Result; - - fn parse_json_response_str( - &self, - json_str: &str, - ) -> Result; - - fn parse_json_response_value( - &self, - json_str: serde_json::Value, - ) -> Result; -} - -impl JsonRpcMethodErased for JsonRpcMethod -where - I: serde::de::DeserializeOwned + Serialize, - O: serde::de::DeserializeOwned + Serialize, - E: serde::de::DeserializeOwned + Serialize + MapErrorCode, -{ - fn name(&self) -> &str { - self.method - } - - fn create_request( - &self, - params: Vec, - json_rpc_id: String, - ) -> Result { - let typed_params: I = serde_json::from_slice(¶ms)?; - JsonRpcMethod::create_request(self, typed_params, json_rpc_id).erase() - } - - fn parse_json_response_str( - &self, - json_str: &str, - ) -> Result { - // Check if the json-struct matches the expected type - JsonRpcMethod::::parse_json_response_str(self, json_str)?.erase() - } - - fn parse_json_response_value( - &self, - json_value: serde_json::Value, - ) -> Result { - JsonRpcMethod::::parse_json_response_value(self, json_value)?.erase() - } -} - -impl JsonRpcMethod -where - I: serde::de::DeserializeOwned + Serialize + 'static, - O: serde::de::DeserializeOwned + Serialize + 'static, - E: serde::de::DeserializeOwned + Serialize + 'static + MapErrorCode, -{ - pub fn erase_box(self) -> Box { - Box::new(self) - } - - pub fn ref_erase(&self) -> &dyn JsonRpcMethodErased { - self - } -} - -// The trait JsonRpcUnerased is only intended to be used by library developers -// -// The user of this library might want to use the strongly typed generic version -// or the fully type-erased version -// -// As a library developer, we don't want to implement the same functionality twice -// for the same RPC-call. -// -// That is why we introduce the JsonRpcUnerased trait. -// It fills in the serde_json::Value type wherever either I, O or E should be. -// -// By using this trait, functionality will work for both type of users - -pub trait JsonRpcMethodUnerased<'a, I, O, E> { - fn name(&self) -> &str; - - fn create_request( - &self, - params: I, - json_rpc_id: String, - ) -> Result, serde_json::Error>; - - fn parse_json_response_str( - &self, - json_str: &str, - ) -> Result, serde_json::Error>; - - fn parse_json_response_value( - &self, - json_value: serde_json::Value, - ) -> Result, serde_json::Error>; -} - -// Dummy implementation for when the user uses the generic api -impl<'a, I, O, E> JsonRpcMethodUnerased<'a, I, O, E> for JsonRpcMethod -where - O: serde::de::DeserializeOwned, - E: serde::de::DeserializeOwned + MapErrorCode, -{ - fn name(&self) -> &str { - JsonRpcMethod::name(self) - } - - fn create_request( - &self, - params: I, - json_rpc_id: String, - ) -> Result, serde_json::Error> { - Ok(JsonRpcMethod::create_request(self, params, json_rpc_id)) - } - - fn parse_json_response_str( - &self, - json_str: &str, - ) -> Result, serde_json::Error> { - JsonRpcMethod::parse_json_response_str(self, json_str) - } - - fn parse_json_response_value( - &self, - json_value: serde_json::Value, - ) -> Result, serde_json::Error> { - JsonRpcMethod::parse_json_response_value(self, json_value) - } -} - -struct UneraseWrapper<'a> { - inner: &'a dyn JsonRpcMethodErased, -} - -impl<'a> JsonRpcMethodUnerased<'a, Vec, Vec, Vec> for UneraseWrapper<'a> { - fn name(&self) -> &str { - self.inner.name() - } - - fn create_request( - &self, - params: Vec, - json_rpc_id: String, - ) -> Result { - self.inner.create_request(params, json_rpc_id) - } - - fn parse_json_response_str( - &self, - json_str: &str, - ) -> Result { - self.inner.parse_json_response_str(json_str) - } - - fn parse_json_response_value( - &self, - json_value: serde_json::Value, - ) -> Result { - self.inner.parse_json_response_value(json_value) - } -} - -impl dyn JsonRpcMethodErased { - // The impl promises here we return a concrete type - // However, we'd rather keep the implementation details private in this module and don't want users messing with it - pub fn unerase(&self) -> impl JsonRpcMethodUnerased<'_, Vec, Vec, Vec> { - UneraseWrapper { inner: self } - } -} - -impl JsonRpcRequest -where - I: Serialize, -{ - fn erase(self) -> Result { - let value = serde_json::to_vec(&self.params)?; - Ok(JsonRpcRequest { - jsonrpc: self.jsonrpc, - id: self.id, - method: self.method, - params: value, - }) - } -} - -impl JsonRpcResponseSuccess -where - O: Serialize, -{ - fn erase(self) -> Result { - Ok(JsonRpcResponseSuccessErased { - id: self.id, - result: serde_json::to_vec(&self.result)?, - jsonrpc: self.jsonrpc, - }) - } -} - -impl JsonRpcResponseFailure -where - E: Serialize, -{ - fn erase(self) -> Result { - Ok(JsonRpcResponseFailureErased { - id: self.id, - error: self.error.erase()?, - jsonrpc: self.jsonrpc, - }) - } -} - -impl ErrorData -where - E: Serialize, -{ - fn erase(self) -> Result { - let error_data = if let Some(error) = &self.data { - Some(serde_json::to_vec(error)?) - } else { - None - }; - - let x = JsonRpcErrorDataErased { - code: self.code, - data: error_data, - message: self.message, - }; - - Ok(x) - } -} - -impl JsonRpcResponse -where - O: Serialize, - E: Serialize, -{ - fn erase(self) -> Result { - let result = match self { - Self::Ok(ok) => JsonRpcResponseErased::Ok(ok.erase()?), - Self::Error(err) => JsonRpcResponseErased::Error(err.erase()?), - }; - - Ok(result) - } -} - -#[cfg(test)] -mod test { - use super::*; - use crate::lsps::json_rpc::{generate_random_rpc_id, DefaultError, JsonRpcMethod}; - - #[derive(Serialize, serde::Deserialize)] - struct TestRequestStruct { - test: String, - } - - #[derive(Serialize, serde::Deserialize)] - struct TestResponseStruct { - response: String, - } - - #[test] - fn create_rpc_request_from_method_erased() { - let rpc_method = JsonRpcMethod::::new("test.method"); - let rpc_method_erased = rpc_method.erase_box(); - - // This rpc-request should work becasue the parameters match the schema - let json_data = serde_json::json!({"test" : "This should work"}); - let vec_data: Vec = serde_json::to_vec(&json_data).unwrap(); - - let json_rpc_id = generate_random_rpc_id(); - let rpc_request: JsonRpcRequest> = rpc_method_erased - .create_request(vec_data, json_rpc_id) - .unwrap(); - assert_eq!(rpc_request.method, "test.method"); - } - - #[test] - fn create_rpc_request_from_method_erased_checks_types() { - let rpc_method = JsonRpcMethod::::new("test.method"); - let rpc_method_erased = rpc_method.erase_box(); - - // This rpc-request should fail because the parameters do not match the schema - // The test field is missing - let param_vec = serde_json::to_vec(&serde_json::json!({})).unwrap(); - let json_rpc_id = generate_random_rpc_id(); - let rpc_request = rpc_method_erased.create_request(param_vec, json_rpc_id); - assert!(rpc_request.is_err()); - } - - #[test] - fn parse_rpc_request_from_method_erased() { - let rpc_method = JsonRpcMethod::::new( - "test.method", - ); - let rpc_method_erased = rpc_method.erase_box(); - - let json_value = serde_json::json!({ - "jsonrpc" : "2.0", - "id" : "abcdef", - "result" : {"response" : "content"} - }); - - rpc_method_erased - .parse_json_response_value(json_value) - .unwrap(); - } - - #[test] - fn parse_rpc_request_from_method_erased_fails() { - let rpc_method = JsonRpcMethod::::new( - "test.method", - ); - let rpc_method_erased = rpc_method.erase_box(); - - let json_value = serde_json::json!({ - "jsonrpd" : "2.0", // See the typo-here - "id" : "abcdef", - "result" : {"response" : "content"} - }); - - let result: Result = - rpc_method_erased.parse_json_response_value(json_value); - assert!(result.is_err()); - - // TODO: improve the error-message here - // It currently gives a vague error-message about not matching one of the enum scenarios in JsonRpcResponse - // It should at least mention that the field jsonrpc is missing - //assert!(format!("{:?}", result).contains("jsonrpc")); - } -} diff --git a/libs/gl-client/src/lsps/lsps0/common_schemas.rs b/libs/gl-client/src/lsps/lsps0/common_schemas.rs deleted file mode 100644 index e5d4bfe54..000000000 --- a/libs/gl-client/src/lsps/lsps0/common_schemas.rs +++ /dev/null @@ -1,315 +0,0 @@ -use core::str::FromStr; - -use std::fmt::{Display, Formatter}; - -use anyhow::{anyhow, Context}; - -use serde::de::Error as SeError; -use serde::ser::Error as DeError; -use serde::{Deserialize, Serialize, Deserializer, Serializer}; - -use time::format_description::FormatItem; -use time::macros::format_description; -use time::{OffsetDateTime, PrimitiveDateTime}; - -// Implements all the common schema's defined in LSPS0 common schema's - -// Initially I used serde_as for the parsing and serialization of this type. -// However, the spec is more strict. -// It requires a yyyy-mm-ddThh:mm:ss.uuuZ format -// -// The serde_as provides us options such as rfc_3339. -// Note, that this also allows formats that are not compliant to the LSP-spec such as dropping -// the fractional seconds or use non UTC timezones. -// -// For LSPS2 the `valid_until`-field must be copied verbatim. As a client this can only be -// achieved if the LSPS2 sends a fully compliant timestamp. -// -// I have decided to fail early if another timestamp is received -#[derive(Debug)] -pub struct IsoDatetime { - pub datetime: PrimitiveDateTime, -} - -const DATETIME_FORMAT: &[FormatItem] = - format_description!("[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond digits:3]Z"); - -impl IsoDatetime { - pub fn from_offset_date_time(datetime: OffsetDateTime) -> Self { - let offset = time::UtcOffset::from_whole_seconds(0).unwrap(); - let datetime_utc = datetime.to_offset(offset); - let primitive = PrimitiveDateTime::new(datetime_utc.date(), datetime.time()); - Self { - datetime: primitive, - } - } - - pub fn from_primitive_date_time(datetime: PrimitiveDateTime) -> Self { - Self { datetime } - } - - pub fn datetime(&self) -> OffsetDateTime { - self.datetime.assume_utc() - } -} - -impl Serialize for IsoDatetime { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let datetime_str = self - .datetime - .format(&DATETIME_FORMAT) - .map_err(|err| S::Error::custom(format!("Failed to format datetime {:?}", err)))?; - - serializer.serialize_str(&datetime_str) - } -} - -impl<'de> Deserialize<'de> for IsoDatetime { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let str_repr = ::deserialize(deserializer)?; - time::PrimitiveDateTime::parse(&str_repr, DATETIME_FORMAT) - .map_err(|err| D::Error::custom(format!("Failed to parse Datetime. {:?}", err))) - .map(Self::from_primitive_date_time) - } -} - -#[derive(Debug)] -pub struct SatAmount(u64); -#[derive(Debug)] -pub struct MsatAmount(u64); - -impl SatAmount { - pub fn sat_value(&self) -> u64 { - self.0 - } - - pub fn new(value: u64) -> Self { - SatAmount(value) - } -} - -impl MsatAmount { - pub fn msat_value(&self) -> u64 { - self.0 - } - - pub fn new(value: u64) -> Self { - MsatAmount(value) - } -} - -impl Serialize for SatAmount { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let amount_str = self.0.to_string(); - serializer.serialize_str(&amount_str) - } -} - -impl Serialize for MsatAmount { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let amount_str = self.0.to_string(); - serializer.serialize_str(&amount_str) - } -} - -impl<'de> Deserialize<'de> for SatAmount { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let str_repr = ::deserialize(deserializer)?; - let u64_repr: Result = str_repr - .parse() - .map_err(|_| D::Error::custom(String::from("Failed to parse sat_amount"))); - Ok(Self(u64_repr.unwrap())) - } -} - -impl<'de> Deserialize<'de> for MsatAmount { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let str_repr = ::deserialize(deserializer)?; - let u64_repr: Result = str_repr - .parse() - .map_err(|_| D::Error::custom(String::from("Failed to parse sat_amount"))); - Ok(Self(u64_repr.unwrap())) - } -} - - -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct ShortChannelId(u64); - -impl Serialize for ShortChannelId { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&self.to_string()) - } -} - -impl<'de> Deserialize<'de> for ShortChannelId { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - use serde::de::Error; - let s: String = Deserialize::deserialize(deserializer)?; - Ok(Self::from_str(&s).map_err(|e| Error::custom(e.to_string()))?) - } -} - -impl FromStr for ShortChannelId { - type Err = anyhow::Error; - fn from_str(s: &str) -> Result { - let parts: Result, _> = s.split('x').map(|p| p.parse()).collect(); - let parts = parts.with_context(|| format!("Malformed short_channel_id: {}", s))?; - if parts.len() != 3 { - return Err(anyhow!( - "Malformed short_channel_id: element count mismatch" - )); - } - - Ok(ShortChannelId( - (parts[0] << 40) | (parts[1] << 16) | (parts[2] << 0), - )) - } -} -impl Display for ShortChannelId { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}x{}x{}", self.block(), self.txindex(), self.outnum()) - } -} -impl ShortChannelId { - pub fn block(&self) -> u32 { - (self.0 >> 40) as u32 & 0xFFFFFF - } - pub fn txindex(&self) -> u32 { - (self.0 >> 16) as u32 & 0xFFFFFF - } - pub fn outnum(&self) -> u16 { - self.0 as u16 & 0xFFFF - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn parsing_amount_sats() { - // Pick a number which exceeds 2^32 to ensure internal representation exceeds 32 bits - let json_str_number = "\"10000000001\""; - - let int_number: u64 = 10000000001; - - let x = serde_json::from_str::(json_str_number).unwrap(); - assert_eq!(x.sat_value(), int_number); - } - - #[test] - fn serializing_amount_sats() { - // Pick a number which exceeds 2^32 to ensure internal representation exceeds 32 bits - // The json_str includes the " to indicate it is a string - let json_str_number = "\"10000000001\""; - let int_number: u64 = 10000000001; - - let sat_amount = SatAmount::new(int_number); - - let json_str = serde_json::to_string::(&sat_amount).unwrap(); - assert_eq!(json_str, json_str_number); - } - - #[test] - fn parse_and_serialize_datetime() { - let datetime_str = "\"2023-01-01T23:59:59.999Z\""; - - let dt = serde_json::from_str::(datetime_str).unwrap(); - - assert_eq!(dt.datetime.year(), 2023); - assert_eq!(dt.datetime.month(), time::Month::January); - assert_eq!(dt.datetime.day(), 1); - assert_eq!(dt.datetime.hour(), 23); - assert_eq!(dt.datetime.minute(), 59); - assert_eq!(dt.datetime.second(), 59); - - assert_eq!( - serde_json::to_string(&dt).expect("Can be serialized"), - datetime_str - ) - } - - #[test] - fn parse_datetime_that_doesnt_follow_spec() { - // The spec doesn't explicitly say that clients have to ignore datetimes that don't follow the spec - // However, in LSPS2 the datetime_str must be repeated verbatim - let datetime_str = "\"2023-01-01T23:59:59.99Z\""; - - let result = serde_json::from_str::(datetime_str); - result.expect_err("datetime_str should not be parsed if it doesn't follow spec"); - } - - #[test] - #[allow(clippy::unusual_byte_groupings)] - fn parse_scid_from_string() { - // How to read this test - // - // The shortchannel_id is 8 bytes long. - // The 3 first bytes are the blockheight, 3 next bytes are the txid and last 2 bytes are vout - // This explains the unusual byte groupings - - // The string representation are the same numbers separated by the letter x - - // Test the largest possible value - let scid_str = "16777215x16777215x65535"; - - let scid = ShortChannelId::from_str(scid_str).expect("The scid is parseable"); - assert_eq!(scid.to_string(), scid_str); - - // Test the smallest possible value - let scid_str = "0x0x0"; - - let scid = ShortChannelId::from_str(scid_str).expect("The scid is parseable"); - assert_eq!(scid.to_string(), scid_str); - - let scid_str = "1x2x3"; - - let scid = ShortChannelId::from_str(scid_str).expect("The scid is parseable"); - assert_eq!(scid.to_string(), scid_str); - // A couple of unparseable scids - assert!(ShortChannelId::from_str("xx").is_err()); - assert!(ShortChannelId::from_str("0x0").is_err()); - assert!(ShortChannelId::from_str("-2x-12x14").is_err()); - } - - #[test] - fn short_channel_id_is_serialized_as_str() { - let scid: ShortChannelId = ShortChannelId::from_str("10x5x8").unwrap(); - let scid_json_obj = serde_json::to_string(&scid).expect("Can be serialized"); - assert_eq!("\"10x5x8\"", scid_json_obj); - } - - #[test] - fn short_channel_id_can_be_deserialized_from_str() { - let scid_json = "\"11x12x13\""; - - let scid = serde_json::from_str::(scid_json).expect("scid can be parsed"); - - assert_eq!(scid, ShortChannelId::from_str("11x12x13").unwrap()); - } -} diff --git a/libs/gl-client/src/lsps/lsps0/mod.rs b/libs/gl-client/src/lsps/lsps0/mod.rs deleted file mode 100644 index cd182a592..000000000 --- a/libs/gl-client/src/lsps/lsps0/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod common_schemas; -pub mod schema; diff --git a/libs/gl-client/src/lsps/lsps0/schema.rs b/libs/gl-client/src/lsps/lsps0/schema.rs deleted file mode 100644 index 212aaa79d..000000000 --- a/libs/gl-client/src/lsps/lsps0/schema.rs +++ /dev/null @@ -1,25 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Serialize, Deserialize)] -pub struct ProtocolList { - pub protocols: Vec, -} - -#[cfg(test)] - - -mod test { - - use super::*; - - #[test] - fn serialize_protocol_list() { - let protocols = ProtocolList { - protocols: vec![1, 3], - }; - - - let json_str = serde_json::to_string(&protocols).unwrap(); - assert_eq!(json_str, "{\"protocols\":[1,3]}") - } -} diff --git a/libs/gl-client/src/lsps/lsps1/mod.rs b/libs/gl-client/src/lsps/lsps1/mod.rs deleted file mode 100644 index 1ce7e1766..000000000 --- a/libs/gl-client/src/lsps/lsps1/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod schema; diff --git a/libs/gl-client/src/lsps/lsps1/schema.rs b/libs/gl-client/src/lsps/lsps1/schema.rs deleted file mode 100644 index 4dd2de867..000000000 --- a/libs/gl-client/src/lsps/lsps1/schema.rs +++ /dev/null @@ -1,99 +0,0 @@ -use crate::lsps::lsps0::common_schemas::{IsoDatetime, SatAmount}; -use serde::{Deserialize, Serialize}; -use uuid::Uuid; - -pub type OnchainFeeRate = u64; - -#[derive(Debug, Serialize, Deserialize)] -pub struct Lsps1InfoResponse { - supported_versions: Vec, - website: Option, - options: Lsps1Options, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct Lsps1Options { - minimum_channel_confirmations: Option, - minimum_onchain_payment_confirmations: Option, - supports_zero_channel_reserve: bool, - min_onchain_payment_size_sat: Option, - max_channel_expiry_blocks: Option, - min_initial_client_balance_sat: Option, - min_initial_lsp_balance_sat: Option, - max_initial_client_balance_sat: Option, - min_channel_balance_sat: Option, - max_channel_balance_sat: Option, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct Lsps1GetOrderRequest { - pub api_version: u16, - pub lsp_balance_sat: SatAmount, - pub client_balance_sat: SatAmount, - pub confirms_within_blocks: u8, - pub channel_expiry_blocks: u32, - pub token: Option, - pub refund_onchain_address: Option, - #[serde(rename = "announceChannel")] - pub announce_channel: String, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct Lsps1GetOrderResponse { - uuid: Uuid, - api_version: u16, - lsp_balance_sat: SatAmount, - client_balance_sat: SatAmount, - confirms_within_blocks: u8, - channel_expiry_blocks: u8, - token: String, - #[serde(rename = "announceChannel")] - announce_channel: bool, - created_at: IsoDatetime, - expires_at: IsoDatetime, - order_state: OrderState, - payment: Payment, -} - -#[derive(Debug, Serialize, Deserialize)] -enum OrderState { - #[serde(rename = "CREATED")] - Created, - #[serde(rename = "COMPLETED")] - Completed, - #[serde(rename = "FAILED")] - Failed, -} - -#[derive(Debug, Serialize, Deserialize)] -enum PaymentState { - #[serde(rename = "EXPECT_PAYMENT")] - ExpectPayment, - #[serde(rename = "HOLD")] - Hold, - #[serde(rename = "STATE")] - State, - #[serde(rename = "REFUNDED")] - Refunded, -} - -#[derive(Debug, Serialize, Deserialize)] -struct OnchainPayment { - outpoint: String, - sat: SatAmount, - confirmed: bool, -} - -#[derive(Debug, Serialize, Deserialize)] -struct Payment { - state: PaymentState, - fee_total_sat: SatAmount, - order_total_sat: SatAmount, - - bolt11_invoice: String, - onchain_address: String, - required_onchain_block_confirmations: u8, - - minimum_fee_for_0conf: OnchainFeeRate, - on_chain_payments: Vec, -} diff --git a/libs/gl-client/src/lsps/lsps2/mod.rs b/libs/gl-client/src/lsps/lsps2/mod.rs deleted file mode 100644 index 1ce7e1766..000000000 --- a/libs/gl-client/src/lsps/lsps2/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod schema; diff --git a/libs/gl-client/src/lsps/lsps2/schema.rs b/libs/gl-client/src/lsps/lsps2/schema.rs deleted file mode 100644 index 09918b086..000000000 --- a/libs/gl-client/src/lsps/lsps2/schema.rs +++ /dev/null @@ -1,181 +0,0 @@ -use serde::de::Error as DeError; -use serde::{Deserialize, Serialize}; - -use crate::lsps::error::map_json_rpc_error_code_to_str; -use crate::lsps::json_rpc::MapErrorCode; -use crate::lsps::lsps0::common_schemas::{IsoDatetime, MsatAmount, ShortChannelId}; - -const MAX_PROMISE_LEN_BYTES: usize = 512; -#[derive(Debug)] -struct Promise { - promise: String, -} - -impl Promise { - fn new(promise: String) -> Result { - if promise.len() <= MAX_PROMISE_LEN_BYTES { - Ok(Promise { promise }) - } else { - Err(String::from("Promise exceeds maximum length")) - } - } -} - -impl Serialize for Promise { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_str(&self.promise) - } -} - -// We only accept promises with a max length of 512 to be compliant to the spec -// Note, that serde-json still forces us to parse the entire string fully. -// However, the client will not story the overly large json-file -impl<'de> Deserialize<'de> for Promise { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let str_repr = String::deserialize(deserializer)?; - Promise::new(str_repr.clone()).map_err(|_| D::Error::custom("promise exceeds max length")) - } -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct Lsps2GetVersionsResponse { - versions: Vec, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct Lsps2GetInfoRequest { - version: i64, - token: Option, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct Lsps2GetInfoResponse { - opening_fee_params_menu: Vec, - min_payment_size_msat: String, - max_payment_size_msat: String, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct Lsps2GetInfoError {} - -impl MapErrorCode for Lsps2GetInfoError { - fn get_code_str(code: i64) -> &'static str { - match code { - 1 => "unsupported_version", - 2 => "unrecognized_or_stale_token", - _ => map_json_rpc_error_code_to_str(code), - } - } -} - -#[derive(Debug, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct OpeningFeeParamsMenuItem { - min_fee_msat: MsatAmount, - proportional: u64, - valid_until: IsoDatetime, - min_lifetime: u64, - max_client_to_self_delay: u64, - promise: Promise, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct Lsps2BuyRequest { - version: String, - opening_fee_params: OpeningFeeParamsMenuItem, - payment_size_msat: MsatAmount, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct Lsps2BuyResponse { - jit_channel_scid: ShortChannelId, - lsp_cltv_expiry_delta: u64, - #[serde(default)] - client_trusts_lsp: bool, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct Lsps2BuyError {} - -impl MapErrorCode for Lsps2BuyError { - fn get_code_str(code: i64) -> &'static str { - match code { - 1 => "unsupported_version", - 2 => "invalid_opening_fee_params", - 3 => "payment_size_too_small", - 4 => "payment_size_too_large", - _ => map_json_rpc_error_code_to_str(code), - } - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn parsing_error_when_opening_fee_menu_has_extra_fields() { - // LSPS2 mentions - // Clients MUST fail and abort the flow if a opening_fee_params object has unrecognized fields. - let fee_menu_item = serde_json::json!( - { - "min_fee_msat": "546000", - "proportional": 1200, - "valid_until": "2023-02-23T08:47:30.511Z", - "min_lifetime": 1008, - "max_client_to_self_delay": 2016, - "promise": "abcdefghijklmnopqrstuvwxyz", - "extra_field" : "This shouldn't be their" - } - ); - - let parsed_opening_fee_menu_item: Result = - serde_json::from_value(fee_menu_item); - assert!( - parsed_opening_fee_menu_item.is_err_and(|x| format!("{}", x).contains("extra_field")) - ) - } - - #[test] - fn parse_valid_promise() { - let promise_json = "\"abcdefghijklmnopqrstuvwxyz\""; - let promise = serde_json::from_str::(promise_json).expect("Can parse promise"); - assert_eq!(promise.promise, "abcdefghijklmnopqrstuvwxyz"); - } - - #[test] - fn parse_too_long_promise_fails() { - // Each a char correspond to 1 byte - // We refuse to parse the promise if it is too long - // LSPS2 requires us to ignore Promise that are too long - // so the client cannot be burdened with unneeded storage requirements - let a_513_chars = "\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\""; - let a_512_chars = "\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\""; - - serde_json::from_str::(a_512_chars) - .expect("Should fail because 512 bytes is the max length"); - serde_json::from_str::(a_513_chars).expect_err("Should fail to parse promise "); - } - - #[test] - fn client_trust_lsp_defaults_to_false() { - let data = serde_json::json!({ - "jit_channel_scid" : "0x12x12", - "lsp_cltv_expiry_delta" : 144 - }); - - let buy_response = - serde_json::from_value::(data).expect("The response can be parsed"); - - assert!( - !buy_response.client_trusts_lsp, - "If the field is absent it assumed the client should not trust the LSP" - ) - } -} diff --git a/libs/gl-client/src/lsps/message.rs b/libs/gl-client/src/lsps/message.rs deleted file mode 100644 index 9bba0ef40..000000000 --- a/libs/gl-client/src/lsps/message.rs +++ /dev/null @@ -1,132 +0,0 @@ -// TODO: Implement parsing of error types for lsps2.getinfo -use crate::lsps::error::LspsError; -pub use crate::lsps::json_rpc::{DefaultError, JsonRpcMethod, NoParams}; -use crate::lsps::json_rpc_erased::{JsonRpcMethodErased, JsonRpcMethodUnerased}; - -use crate::lsps::lsps0::schema::ProtocolList; -use crate::lsps::lsps1::schema::{Lsps1GetOrderRequest, Lsps1GetOrderResponse, Lsps1InfoResponse}; -use crate::lsps::lsps2::schema::{ - Lsps2BuyError, Lsps2BuyRequest, Lsps2BuyResponse, Lsps2GetInfoError, Lsps2GetInfoRequest, - Lsps2GetInfoResponse, Lsps2GetVersionsResponse, -}; - -// All rpc-methods defined in the LSPS standard -// The generics are where -// - I represents the params -// - O represents the result data -// - E represents the error if present -// -// To create language bindings for a new rpc-call you must -// 1. Add it to the JsonRpcMethodEnum -// 2. Add it to the from_method_name function -// 3. Add it to the ref_erase function -pub type Lsps0ListProtocols = JsonRpcMethod; -pub type Lsps1Info = JsonRpcMethod; -pub type Lsps1Order = JsonRpcMethod; -pub type Lsps2GetVersions = JsonRpcMethod; -pub type Lsps2GetInfo = JsonRpcMethod; -pub type Lsps2Buy = JsonRpcMethod; - -pub const LSPS0_LIST_PROTOCOLS: Lsps0ListProtocols = - Lsps0ListProtocols::new("lsps0.list_protocols"); - -// LSPS1: Buy Channels -pub const LSPS1_GETINFO: Lsps1Info = Lsps1Info::new("lsps1.info"); -pub const LSPS1_GETORDER: Lsps1Order = Lsps1Order::new("lsps1.order"); - -// LSPS2: JIT-channels -pub const LSPS2_GET_VERSIONS: Lsps2GetVersions = Lsps2GetVersions::new("lsps2.get_versions"); -pub const LSPS2_GET_INFO: Lsps2GetInfo = Lsps2GetInfo::new("lsps2.get_info"); -pub const LSPS2_BUY: Lsps2Buy = Lsps2Buy::new("lsps2.buy"); - -pub enum JsonRpcMethodEnum { - Lsps0ListProtocols(Lsps0ListProtocols), - Lsps1Info(Lsps1Info), - Lsps1Order(Lsps1Order), - Lsp2GetVersions(Lsps2GetVersions), - Lsps2GetInfo(Lsps2GetInfo), - Lsps2Buy(Lsps2Buy), -} - -impl JsonRpcMethodEnum { - pub fn from_method_name(value: &str) -> Result { - match value { - "lsps0.list_protocols" => Ok(Self::Lsps0ListProtocols(LSPS0_LIST_PROTOCOLS)), - "lsps1.info" => Ok(Self::Lsps1Info(LSPS1_GETINFO)), - "lsps1.order" => Ok(Self::Lsps1Order(LSPS1_GETORDER)), - "lsps2.get_versions" => Ok(Self::Lsp2GetVersions(LSPS2_GET_VERSIONS)), - "lsps2.get_info" => Ok(Self::Lsps2GetInfo(LSPS2_GET_INFO)), - "lsps2.buy" => Ok(Self::Lsps2Buy(LSPS2_BUY)), - default => Err(LspsError::MethodUnknown(String::from(default))), - } - } - - // Useful for language bindings. - // The python code can - pub fn ref_erase(&self) -> &dyn JsonRpcMethodErased { - match self { - Self::Lsps0ListProtocols(list_protocol) => list_protocol.ref_erase(), - Self::Lsps1Info(info) => info.ref_erase(), - Self::Lsps1Order(order) => order.ref_erase(), - Self::Lsp2GetVersions(order) => order.ref_erase(), - Self::Lsps2GetInfo(order) => order.ref_erase(), - Self::Lsps2Buy(buy) => buy.ref_erase(), - } - } -} - -impl<'a> JsonRpcMethodUnerased<'a, Vec, Vec, Vec> for JsonRpcMethodEnum { - fn name(&self) -> &str { - self.ref_erase().name() - } - - fn create_request( - &self, - params: Vec, - json_rpc_id: String, - ) -> Result>, serde_json::Error> { - self.ref_erase().create_request(params, json_rpc_id) - } - - fn parse_json_response_str( - &self, - json_str: &str, - ) -> Result, Vec>, serde_json::Error> { - self.ref_erase().parse_json_response_str(json_str) - } - - fn parse_json_response_value( - &self, - json_value: serde_json::Value, - ) -> Result, Vec>, serde_json::Error> { - self.ref_erase().parse_json_response_value(json_value) - } -} - -#[cfg(test)] -mod test { - - use crate::lsps::json_rpc::generate_random_rpc_id; - - use super::*; - use serde_json::{from_str, to_string, Value}; - - #[test] - fn serialize_request_with_no_params() { - let method = LSPS0_LIST_PROTOCOLS; - let json_rpc_id = generate_random_rpc_id(); - let rpc_request = method.create_request_no_params(json_rpc_id); - let json_str = to_string(&rpc_request).unwrap(); - - // Test that params is an empty dict - // - // LSPS-0 spec demands that a parameter-by-name scheme is always followed - let v: Value = from_str(&json_str).unwrap(); - assert_eq!(v.get("jsonrpc").unwrap(), "2.0"); - assert_eq!( - v.get("method").unwrap().as_str().unwrap(), - "lsps0.list_protocols" - ); - assert!(v.get("params").unwrap().as_object().unwrap().is_empty()) - } -} diff --git a/libs/gl-client/src/lsps/mod.rs b/libs/gl-client/src/lsps/mod.rs deleted file mode 100644 index 989626450..000000000 --- a/libs/gl-client/src/lsps/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -// NOTE: Both the LSP-spec and this implementation are still moving heavily -pub mod error; -pub mod json_rpc; -pub mod json_rpc_erased; -pub mod message; -pub mod client; -pub mod lsps0; -pub mod lsps1; -pub mod lsps2; From 13d5942b4039a0c67c9b6d01d9e20fb615b845cf Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 13 Nov 2025 14:48:19 +0100 Subject: [PATCH 09/17] feat(py): Add the lsp_invoice method to gl-client-py --- libs/gl-client-py/glclient/greenlight.proto | 379 ++---------------- libs/gl-client-py/src/node.rs | 21 + .../proto/glclient/greenlight.proto | 26 +- libs/proto/glclient/greenlight.proto | 6 +- 4 files changed, 73 insertions(+), 359 deletions(-) diff --git a/libs/gl-client-py/glclient/greenlight.proto b/libs/gl-client-py/glclient/greenlight.proto index 4730e18b2..485ea7894 100644 --- a/libs/gl-client-py/glclient/greenlight.proto +++ b/libs/gl-client-py/glclient/greenlight.proto @@ -14,9 +14,14 @@ package greenlight; // // Deprecated methods are being replaced by the standardized and // automatically managed cln-grpc protocol you can find in -// `node.proto` +// `node.proto`. This interface consists mostly of +// Greenlight-specific, and backported functionality. service Node { - // Stream incoming payments + // Create an invoice to request an incoming payment. Includes LSP + // negotiation to open a channel on-demand when needed. + rpc Invoice(InvoiceRequest) returns (InvoiceResponse) {} + + // Stream incoming payments // // Currently includes off-chain payments received matching an // invoice or spontaneus paymens through keysend. @@ -98,233 +103,6 @@ service Hsm { rpc Ping(Empty) returns (Empty) {} } -enum NetAddressType { - Ipv4 = 0; - Ipv6 = 1; - TorV2 = 2; - TorV3 = 3; -} -message Address { - NetAddressType type = 1; - string addr = 2; - uint32 port = 3; -} - -message GetInfoRequest {} - -message GetInfoResponse { - bytes node_id = 1; - string alias = 2; - bytes color = 3; - uint32 num_peers = 4; - repeated Address addresses = 5; - string version = 6; - uint32 blockheight = 7; - string network = 8; -} - -message StopRequest {} -message StopResponse {} - -message ConnectRequest { - string node_id = 1; - string addr = 2; -} - -message ConnectResponse { - string node_id = 1; - string features = 2; -} - - -message ListPeersRequest { - string node_id = 1; -} - -message Htlc { - string direction = 1; - uint64 id = 2; - string amount = 3; - uint64 expiry = 4; - string payment_hash = 5; - string state = 6; - bool local_trimmed = 7; -} - -message Aliases { - string local = 1; - string remote = 2; -} - -message Channel { - string state = 1; - string owner = 2; - Aliases alias = 18; - string short_channel_id = 3; - uint32 direction = 4; - string channel_id = 5; - string funding_txid = 6; - string close_to_addr = 7; - string close_to = 8; - bool private = 9; - string total = 10; - string dust_limit = 11; - string spendable = 12; - string receivable = 13; - uint32 their_to_self_delay = 14; - uint32 our_to_self_delay = 15; - repeated string status = 16; - repeated Htlc htlcs = 17; -} - -message Peer { - bytes id = 1; - bool connected = 2; - repeated Address addresses = 3; - string features = 4; - repeated Channel channels = 5; -} - -message ListPeersResponse { - repeated Peer peers = 1; -} - -message DisconnectRequest { - string node_id = 1; - bool force = 2; -} - -message DisconnectResponse {} - -enum BtcAddressType { - BECH32 = 0; // Default - P2SH_SEGWIT = 1; -} - -message NewAddrRequest { - BtcAddressType address_type = 1; -} -message NewAddrResponse { - BtcAddressType address_type = 1; - string address = 2; -} - -message ListFundsRequest { - Confirmation minconf = 1; -} - -enum OutputStatus { - CONFIRMED = 0; - UNCONFIRMED = 1; -} - -message ListFundsOutput { - Outpoint output = 1; - Amount amount = 2; - string address = 4; - OutputStatus status = 5; - bool reserved = 6; - uint32 reserved_to_block = 7; -} - -message ListFundsChannel { - bytes peer_id = 1; - bool connected = 2; - uint64 short_channel_id = 3; - uint64 our_amount_msat = 4; - uint64 amount_msat = 5; - bytes funding_txid = 6; - uint32 funding_output = 7; -} - -message ListFundsResponse { - repeated ListFundsOutput outputs = 1; - repeated ListFundsChannel channels = 2; -} - -// Let the node decide what feerate to apply based on its internal -// view of the network's current feerate. -enum FeeratePreset { - NORMAL = 0; - SLOW = 1; - URGENT = 2; -} - -message Feerate { - oneof value { - FeeratePreset preset = 1; - uint64 perkw = 5; - uint64 perkb = 6; - } -} - -message Confirmation { - uint32 blocks = 1; -} - -message WithdrawRequest { - string destination = 1; - Amount amount = 2; - Feerate feerate = 3; - Confirmation minconf = 7; - repeated Outpoint utxos = 8; -} - -message WithdrawResponse { - bytes tx = 1; - bytes txid = 2; -} - -// TODO: Extract AmountOrAll into its own message -// TODO: Extract Feerate into its own message - -message FundChannelRequest { - bytes node_id = 1; - Amount amount = 2; - Feerate feerate = 3; - bool announce = 7; - Confirmation minconf = 8; - //TODO Maybe add UTXOS - string close_to = 10; -} - -message Outpoint { - bytes txid = 1; - uint32 outnum = 2; -} - -message FundChannelResponse { - bytes tx = 1; - Outpoint outpoint = 2; - bytes channel_id = 3; - string close_to = 4; -} - -message Timeout { - uint32 seconds = 1; -} - -message BitcoinAddress { - string address = 1; -} - -message CloseChannelRequest { - bytes node_id = 1; - Timeout unilateraltimeout = 2; - BitcoinAddress destination = 3; -} - -enum CloseChannelType { - MUTUAL = 0; - UNILATERAL = 1; -} - -message CloseChannelResponse { - CloseChannelType close_type = 1; - bytes tx = 2; - bytes txid = 3; -} - message Amount { oneof unit { uint64 millisatoshi = 1; @@ -335,113 +113,10 @@ message Amount { } } -message InvoiceRequest { - Amount amount = 1; - string label = 2; - string description = 3; - bytes preimage = 4; -} - -enum InvoiceStatus { - UNPAID = 0; - PAID = 1; - EXPIRED = 2; -} - -message Invoice { - string label = 1; - string description = 2; - Amount amount = 3; - Amount received = 4; - InvoiceStatus status = 5; - uint32 payment_time = 6; - uint32 expiry_time = 7; - string bolt11 = 8; - bytes payment_hash = 9; - bytes payment_preimage = 10; -} - -message PayRequest { - string bolt11 = 1; - - // Only needed when the invoice does not specify an amount. - Amount amount = 2; - - // Non-zero number of seconds before we should stop retrying - // the payment and return an error. - uint32 timeout = 3; - - double maxfeepercent = 4; - - Amount maxfee = 5; -} - -enum PayStatus { - PENDING = 0; - COMPLETE = 1; - FAILED = 2; -} - -message Payment { - bytes destination = 1; - bytes payment_hash = 2; - bytes payment_preimage = 3; - PayStatus status = 4; - Amount amount = 5; - Amount amount_sent = 6; - string bolt11 = 7; - - // UTC Unix timestamp of the time the invoice was created. - double created_at = 8; - // UTC Unix timestamp of the time the payment was completed - // (successfully or failed). 0 if not completed yet. - uint64 completed_at = 9; -} - -// A payment identifier is a way to reference a unique payment, either -// by its bolt11 string or just the payment hash. Only one of the two -// may be provided at once, since having multiple ones may conflict -// with each other. -message PaymentIdentifier { - oneof id { - string bolt11 = 1; - bytes payment_hash = 2; - } -} - -// Request a list of payments that this node has performed. Optionally -// the query can be narrowed to a single payment by providing an -// identifier. -message ListPaymentsRequest { - PaymentIdentifier identifier = 1; -} - -// The response matching `ListPaymentRequest`. It returns a list of -// PayResponses, i.e., the same format as `Pay` returned its result. -message ListPaymentsResponse { - repeated Payment payments = 1; -} - -message InvoiceIdentifier { - oneof id { - string label = 1; - string invstring = 2; - bytes payment_hash = 3; - } -} - -message ListInvoicesRequest { - InvoiceIdentifier identifier = 1; -} - // Options to stream_incoming to specify what to stream. message StreamIncomingFilter { } -message ListInvoicesResponse { - repeated Invoice invoices = 1; -} - message TlvField { uint64 type = 1; // length is implied since the value field carries its own @@ -464,27 +139,6 @@ message IncomingPayment { } } -// A single hop in a Routehint -message RoutehintHop { - bytes node_id = 1; - string short_channel_id = 2; - uint64 fee_base = 3; - uint32 fee_prop = 4; - uint32 cltv_expiry_delta = 5; -} - -message Routehint { - repeated RoutehintHop hops = 1; -} - -message KeysendRequest { - bytes node_id = 1; - Amount amount = 2; - string label = 3; - repeated Routehint routehints = 4; - repeated TlvField extratlvs = 5; -} - message StreamLogRequest {}; message LogEntry { string line = 1; @@ -571,4 +225,21 @@ message TrampolinePayResponse { uint64 amount_msat = 5; uint64 amount_sent_msat = 6; bytes destination = 7; -} \ No newline at end of file +} + +message InvoiceRequest { + string lsp_id = 1; // len=0 => None, let the server decide. + // Optional: for discounts/API keys + string token = 2; // len=0 => None + // Pass-through of cln invoice rpc params + uint64 amount_msat = 3; // 0 => Any + string description = 4; + string label = 5; +} +message InvoiceResponse { + string bolt11 = 1; + uint32 created_index = 2; + uint32 expires_at = 3; + bytes payment_hash = 4; + bytes payment_secret = 5; +} diff --git a/libs/gl-client-py/src/node.rs b/libs/gl-client-py/src/node.rs index 06418413c..ade59d805 100644 --- a/libs/gl-client-py/src/node.rs +++ b/libs/gl-client-py/src/node.rs @@ -91,6 +91,27 @@ impl Node { return Ok(()); } + + fn lsps_invoice( + &self, + amount_msat: Option, + description: String, + label: String, + token: Option, + ) -> PyResult> { + let req = pb::LspInvoiceRequest { + amount_msat: amount_msat.unwrap_or_default(), + description: description, + label: label, + lsp_id: "".to_owned(), + token: token.unwrap_or_default(), + }; + + let res = exec(async { self.client.clone().lsp_invoice(req).await }) + .map_err(error_calling_remote_method) + .map(|x| x.into_inner())?; + convert(Ok(res)) + } } fn error_decoding_request(e: D) -> PyErr { diff --git a/libs/gl-client/.resources/proto/glclient/greenlight.proto b/libs/gl-client/.resources/proto/glclient/greenlight.proto index a75224793..63dd182c0 100644 --- a/libs/gl-client/.resources/proto/glclient/greenlight.proto +++ b/libs/gl-client/.resources/proto/glclient/greenlight.proto @@ -14,9 +14,14 @@ package greenlight; // // Deprecated methods are being replaced by the standardized and // automatically managed cln-grpc protocol you can find in -// `node.proto` +// `node.proto`. This interface consists mostly of +// Greenlight-specific, and backported functionality. service Node { - // Stream incoming payments + // Create an invoice to request an incoming payment. Includes LSP + // negotiation to open a channel on-demand when needed. + rpc LspInvoice(LspInvoiceRequest) returns (LspInvoiceResponse) {} + + // Stream incoming payments // // Currently includes off-chain payments received matching an // invoice or spontaneus paymens through keysend. @@ -221,3 +226,20 @@ message TrampolinePayResponse { uint64 amount_sent_msat = 6; bytes destination = 7; } + +message LspInvoiceRequest { + string lsp_id = 1; // len=0 => None, let the server decide. + // Optional: for discounts/API keys + string token = 2; // len=0 => None + // Pass-through of cln invoice rpc params + uint64 amount_msat = 3; // 0 => Any + string description = 4; + string label = 5; +} +message LspInvoiceResponse { + string bolt11 = 1; + uint32 created_index = 2; + uint32 expires_at = 3; + bytes payment_hash = 4; + bytes payment_secret = 5; +} diff --git a/libs/proto/glclient/greenlight.proto b/libs/proto/glclient/greenlight.proto index 485ea7894..63dd182c0 100644 --- a/libs/proto/glclient/greenlight.proto +++ b/libs/proto/glclient/greenlight.proto @@ -19,7 +19,7 @@ package greenlight; service Node { // Create an invoice to request an incoming payment. Includes LSP // negotiation to open a channel on-demand when needed. - rpc Invoice(InvoiceRequest) returns (InvoiceResponse) {} + rpc LspInvoice(LspInvoiceRequest) returns (LspInvoiceResponse) {} // Stream incoming payments // @@ -227,7 +227,7 @@ message TrampolinePayResponse { bytes destination = 7; } -message InvoiceRequest { +message LspInvoiceRequest { string lsp_id = 1; // len=0 => None, let the server decide. // Optional: for discounts/API keys string token = 2; // len=0 => None @@ -236,7 +236,7 @@ message InvoiceRequest { string description = 4; string label = 5; } -message InvoiceResponse { +message LspInvoiceResponse { string bolt11 = 1; uint32 created_index = 2; uint32 expires_at = 3; From 923539a2e331883c2d2c8a7ecf7fc46a3cdb7683 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 13 Nov 2025 14:48:55 +0100 Subject: [PATCH 10/17] chore(py): Further pin down the versions and package details for py --- libs/gl-client-py/pyproject.toml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libs/gl-client-py/pyproject.toml b/libs/gl-client-py/pyproject.toml index 48e9e02d7..67854e843 100644 --- a/libs/gl-client-py/pyproject.toml +++ b/libs/gl-client-py/pyproject.toml @@ -3,7 +3,7 @@ name = "gl-client" version = "0.3.4" description = "" readme = "README.md" -requires-python = ">=3.8" +requires-python = ">=3.8,<4" dependencies = [ "protobuf>=4", "pyln-grpc-proto>=0.1", @@ -15,6 +15,10 @@ authors = [ requires = ["maturin>=1.0"] build-backend = "maturin" +[tool.maturin] +python-source = "." +module-name = "glclient" + [dependency-groups] dev = [ "gl-testing", From 89ada79f5c1fc246eaef40ff2b0c1f7d3c608f42 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 14 Nov 2025 11:33:07 +0100 Subject: [PATCH 11/17] chore(ci): Ensure `docker-ci-check` leaves the repo untouched We were running inside the repo, which meant that the `.venv` directory was owned by the docker daemon's user (root). By specifying a separate `.venv` directory we ensure we don't accidentally leave root-owned directories in the repo. --- docker/gl-testing/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/docker/gl-testing/Dockerfile b/docker/gl-testing/Dockerfile index bad2b21b2..c4e79f8e5 100644 --- a/docker/gl-testing/Dockerfile +++ b/docker/gl-testing/Dockerfile @@ -165,6 +165,7 @@ ENV PYTHONUNBUFFERED=1 # yet, but we will put the tools in those directories. ENV PATH=$HOME/.local/bin:/opt/cln-latest/usr/local/bin:/opt/bitcoin/bin:/opt/cln-latest/usr/local/bin:/usr/local/bin:$PATH ENV UV_INSTALL_DIR=/usr/local/bin/ +ENV UV_PROJECT_ENVIRONMENT=/tmp/venv/ # grpcio == 1.46 produces spammy log messages, silence them ENV GRPC_ENABLE_FORK_SUPPORT=0 From 82d7eb5444ac2af92fd79a018d186151b0ef4929 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 14 Nov 2025 11:35:05 +0100 Subject: [PATCH 12/17] chore(py) Just some small cleanups --- libs/gl-client-py/Makefile | 5 +- libs/gl-client-py/pyproject.toml | 1 + libs/gl-client-py/tests/test_lsps.py | 44 -------- libs/gl-testing/gltesting/fixtures.py | 5 - libs/gl-testing/tests/test_lsps.py | 146 -------------------------- 5 files changed, 3 insertions(+), 198 deletions(-) delete mode 100644 libs/gl-client-py/tests/test_lsps.py delete mode 100755 libs/gl-testing/tests/test_lsps.py diff --git a/libs/gl-client-py/Makefile b/libs/gl-client-py/Makefile index c1a120ee3..b0ebada24 100644 --- a/libs/gl-client-py/Makefile +++ b/libs/gl-client-py/Makefile @@ -35,9 +35,8 @@ ${PYPROTOS}: pygrpc pygrpc: ${PROTOSRC} cp ${PYDIR}/../proto/glclient/scheduler.proto ${PYDIR}/glclient/ cp ${PYDIR}/../proto/glclient/greenlight.proto ${PYDIR}/glclient/ - cd ${PYDIR}; poetry install - cd ${PYDIR}; poetry run python -m grpc_tools.protoc ${PYPROTOC_OPTS} glclient/scheduler.proto - cd ${PYDIR}; poetry run python -m grpc_tools.protoc ${PYPROTOC_OPTS} glclient/greenlight.proto + cd ${PYDIR}; uv run python -m grpc_tools.protoc ${PYPROTOC_OPTS} glclient/scheduler.proto + cd ${PYDIR}; uv run python -m grpc_tools.protoc ${PYPROTOC_OPTS} glclient/greenlight.proto check-py: #uv run --all-packages mypy ${PYDIR}/glclient diff --git a/libs/gl-client-py/pyproject.toml b/libs/gl-client-py/pyproject.toml index 67854e843..3addceb22 100644 --- a/libs/gl-client-py/pyproject.toml +++ b/libs/gl-client-py/pyproject.toml @@ -25,6 +25,7 @@ dev = [ "grpcio>=1.67", "maturin>=1", "mypy>=1.14.1", + "mypy-protobuf>=3.6.0", "patchelf>=0.17.2.2", "protobuf>=4", "pyln-grpc-proto>=0.1.2", diff --git a/libs/gl-client-py/tests/test_lsps.py b/libs/gl-client-py/tests/test_lsps.py deleted file mode 100644 index 9ed5ac2be..000000000 --- a/libs/gl-client-py/tests/test_lsps.py +++ /dev/null @@ -1,44 +0,0 @@ -from glclient.lsps import AsDataClassDescriptor, EnhancedJSONEncoder, NoParams -import json - -from dataclasses import dataclass - -@dataclass -class Nested: - value : str - -@dataclass -class Nester: - nested_field : Nested = AsDataClassDescriptor(cls=Nested) - - -def test_nested_serialization(): - nester = Nester(nested_field=Nested(value=0)) - json_str = json.dumps(nester, cls=EnhancedJSONEncoder) - - assert json_str == """{"nested_field": {"value": 0}}""" - -def test_nested_deserialization(): - - nested_dict = { - "nested_field" : {"value" : 0} - } - result = Nester(**nested_dict) - - assert isinstance(result.nested_field, Nested) - assert result.nested_field.value == 0 - -def test_serialize_no_params(): - no_params_1 = NoParams - no_params_2 = NoParams() - - assert json.dumps(no_params_1, cls=EnhancedJSONEncoder) == "{}" - assert json.dumps(no_params_2, cls=EnhancedJSONEncoder) == "{}" - -def test_deserialize_no_params(): - json_str = "{}" - - # Should not raise - # this behavior should be the same as for a dataclass - - NoParams(**json.loads(json_str)) diff --git a/libs/gl-testing/gltesting/fixtures.py b/libs/gl-testing/gltesting/fixtures.py index f3e5bcec4..bf7f62588 100644 --- a/libs/gl-testing/gltesting/fixtures.py +++ b/libs/gl-testing/gltesting/fixtures.py @@ -253,10 +253,5 @@ def lsps_server(node_factory): # Most of the params below are ignored, this is just a smoke test either way pprint(lsp.rpc.lsps2_policy_getpolicy()) - pprint( - lsp.rpc.lsps2_policy_getchannelcapacity( - init_payment_size=10**6, scid="1x1x1", opening_fee_params=None - ) - ) yield lsp diff --git a/libs/gl-testing/tests/test_lsps.py b/libs/gl-testing/tests/test_lsps.py deleted file mode 100755 index 94dfa19ba..000000000 --- a/libs/gl-testing/tests/test_lsps.py +++ /dev/null @@ -1,146 +0,0 @@ -from gltesting.fixtures import * -from gltesting.scheduler import Scheduler -from pyln.testing.utils import NodeFactory, BitcoinD, LightningNode -import json - -from glclient.lsps import ProtocolList -import time - -import threading -import subprocess -import pwd -from pathlib import Path - -logger = logging.getLogger(__name__) - - -def get_lsps_dummy_plugin_path() -> str: - # Find the fully specified path to the LSPS-plugin - # This plugin sets the feature flags and makes the node appear as an LSP - - base_path, _ = os.path.split(__file__) - return os.path.join(base_path, "util", "dummy_lsps_plugin.py") -class AwaitResult: - """A very poor implementation of an awaitable in python - - It is inefficient and uses Threads under the hood. - But it gives something like the `await` syntax which - makes it easier to write tests. - """ - - def __init__(self, function, args=None, kwargs=None): - self._result = None - self._thread = None - self._exception = None - - if args is None: - args = [] - if kwargs is None: - kwargs = dict() - - def wrap_function(*args, **kwargs): - try: - self._result = function(*args, **kwargs) - except Exception as e: - self._exception = e - - self._thread = threading.Thread(target=wrap_function, args=args, kwargs=kwargs) - self._thread.start() - - def await_result(self, timeout_seconds: float = 30.0): - self._thread.join(timeout=timeout_seconds) - if self._thread.is_alive(): - raise TimeoutError() - - if self._exception: - raise self._exception - - return self._result - - -def test_lsps_list_protocol( - clients: Clients, node_factory: NodeFactory, bitcoind: BitcoinD -): - # Create the LSP - n1: LightningNode = node_factory.get_node() - - # Create and configure the greenlight client and connect it to the LSP - c = clients.new() - c.register(configure=True) - gl1 = c.node() - s = c.signer().run_in_thread() - - # Connect our greenlight node (client ot the LSP) - lsp_ip = n1.info["binding"][0]["address"] - lsp_port = n1.info["binding"][0]["port"] - gl1.connect_peer(n1.info['id'], host=f"{lsp_ip}:{lsp_port}") - - # Get the lsp-client and do list-protocols - lsp_client = gl1.get_lsp_client() - - # The client sends a message - json_rpc_id = "abcdef" - protocol_fut = AwaitResult( - lambda: lsp_client.list_protocols( - peer_id=n1.info["id"], json_rpc_id=json_rpc_id - ) - ) - - # The sleep ensures the lsp-client has actually send the message and is ready to receive - # the response - time.sleep(1.0) - - # The n1.rpc.sendcustommsg expects that both the node_id and msg are hex encoded strings - msg_content = {"jsonrpc": "2.0", "id": json_rpc_id, "result": {"protocols": [1, 2]}} - - json_str = json.dumps(msg_content) - json_bytes = json_str.encode("utf-8") - msg_str = "9419" + json_bytes.hex() - - n1.rpc.sendcustommsg(gl1.get_info().id.hex(), msg_str) - - result = protocol_fut.await_result() - assert result == ProtocolList([1, 2]) - - -def test_list_lsp_server( - clients: Clients, node_factory: NodeFactory, bitcoind: BitcoinD -): - # Create a network - n1: LightningNode = node_factory.get_node( - options={"plugin": get_lsps_dummy_plugin_path(), "disable-plugin": "cln-grpc"} - ) - n2: LightningNode = node_factory.get_node( - options={"plugin": get_lsps_dummy_plugin_path(), "disable-plugin": "cln-grpc"} - ) - n3: LightningNode = node_factory.get_node(options={"disable-plugin": "cln-grpc"}) - - # Create the channel-graph - n1.fundchannel(n2, announce_channel=True) - n2.fundchannel(n3, announce_channel=True) - - # Generate some blocks to ensure the channels get confirmed - bitcoind.generate_block(6) - - # Initiate the greenlight node - c = clients.new() - c.register(configure=True) - gl1 = c.node() - s = c.signer().run_in_thread() - - n1_full_address = ( - f"{n1.info['binding'][0]['address']}:{n1.info['binding'][0]['port']}" - ) - _ = gl1.connect_peer(node_id=n1.info["id"], host=n1_full_address) - - # Await gossip - time.sleep(1.0) - - lsp_client = gl1.get_lsp_client() - lsp_servers = lsp_client.list_lsp_servers() - - assert len(lsp_servers) == 2, "Expected 2 lsp-servers defined" - assert n1.info["id"] in lsp_servers - assert n2.info["id"] in lsp_servers - assert n3.info["id"] not in lsp_servers - From 527e05750cc3fc7967a4b863b1a2482b20f16524 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 14 Nov 2025 12:21:09 +0100 Subject: [PATCH 13/17] chore(proto): Renamed the `greenlight.Invoice` to `LspInvoice` The hope is to signal in a more clear manner that this is an invoice that is negotiated with a peer LSP. --- libs/gl-client-py/glclient/__init__.py | 8 + libs/gl-client-py/glclient/greenlight.proto | 6 +- libs/gl-client-py/glclient/greenlight_pb2.py | 198 +-- libs/gl-client-py/glclient/greenlight_pb2.pyi | 1414 ++--------------- .../glclient/greenlight_pb2_grpc.py | 65 +- .../glclient/greenlight_pb2_grpc.pyi | 35 +- libs/gl-client-py/glclient/scheduler_pb2.py | 12 +- .../glclient/scheduler_pb2_grpc.py | 11 +- libs/gl-client-py/src/node.rs | 4 +- .../proto/glclient/greenlight.proto | 8 +- libs/gl-plugin/src/node/mod.rs | 8 +- libs/gl-plugin/src/node/wrapper.rs | 10 +- libs/gl-plugin/src/requests.rs | 8 +- libs/gl-plugin/src/responses.rs | 8 +- 14 files changed, 332 insertions(+), 1463 deletions(-) diff --git a/libs/gl-client-py/glclient/__init__.py b/libs/gl-client-py/glclient/__init__.py index b07362b40..b944433b3 100644 --- a/libs/gl-client-py/glclient/__init__.py +++ b/libs/gl-client-py/glclient/__init__.py @@ -570,6 +570,14 @@ def wait(self, subsystem, indexname, nextvalue: int) -> clnpb.WaitResponse: res = clnpb.WaitResponse return res.FromString(bytes(self.inner.call(uri, bytes(req)))) + def lsp_invoice(self, label: str, description: str, amount_msat: int | None = None): + uri = "/greenlight.Node/LspInvoice" + req = nodepb.LspInvoiceRequest( + label=label, description=description, amount_msat=amount_msat + ).SerializeToString() + res = nodepb.LspInvoiceResponse + return res.FromString(bytes(self.inner.call(uri, bytes(req)))) + def normalize_node_id(node_id, string=False): if len(node_id) == 66: diff --git a/libs/gl-client-py/glclient/greenlight.proto b/libs/gl-client-py/glclient/greenlight.proto index 485ea7894..63dd182c0 100644 --- a/libs/gl-client-py/glclient/greenlight.proto +++ b/libs/gl-client-py/glclient/greenlight.proto @@ -19,7 +19,7 @@ package greenlight; service Node { // Create an invoice to request an incoming payment. Includes LSP // negotiation to open a channel on-demand when needed. - rpc Invoice(InvoiceRequest) returns (InvoiceResponse) {} + rpc LspInvoice(LspInvoiceRequest) returns (LspInvoiceResponse) {} // Stream incoming payments // @@ -227,7 +227,7 @@ message TrampolinePayResponse { bytes destination = 7; } -message InvoiceRequest { +message LspInvoiceRequest { string lsp_id = 1; // len=0 => None, let the server decide. // Optional: for discounts/API keys string token = 2; // len=0 => None @@ -236,7 +236,7 @@ message InvoiceRequest { string description = 4; string label = 5; } -message InvoiceResponse { +message LspInvoiceResponse { string bolt11 = 1; uint32 created_index = 2; uint32 expires_at = 3; diff --git a/libs/gl-client-py/glclient/greenlight_pb2.py b/libs/gl-client-py/glclient/greenlight_pb2.py index fceacd3b7..3c2ab0bef 100644 --- a/libs/gl-client-py/glclient/greenlight_pb2.py +++ b/libs/gl-client-py/glclient/greenlight_pb2.py @@ -1,12 +1,22 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: glclient/greenlight.proto -# Protobuf Python Version: 5.26.1 +# Protobuf Python Version: 6.31.1 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'glclient/greenlight.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -14,27 +24,13 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x19glclient/greenlight.proto\x12\ngreenlight\"H\n\x11HsmRequestContext\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\x12\x0c\n\x04\x64\x62id\x18\x02 \x01(\x04\x12\x14\n\x0c\x63\x61pabilities\x18\x03 \x01(\x04\"q\n\x0bHsmResponse\x12\x12\n\nrequest_id\x18\x01 \x01(\r\x12\x0b\n\x03raw\x18\x02 \x01(\x0c\x12\x32\n\x0csigner_state\x18\x05 \x03(\x0b\x32\x1c.greenlight.SignerStateEntry\x12\r\n\x05\x65rror\x18\x06 \x01(\t\"\xbf\x01\n\nHsmRequest\x12\x12\n\nrequest_id\x18\x01 \x01(\r\x12.\n\x07\x63ontext\x18\x02 \x01(\x0b\x32\x1d.greenlight.HsmRequestContext\x12\x0b\n\x03raw\x18\x03 \x01(\x0c\x12\x32\n\x0csigner_state\x18\x04 \x03(\x0b\x32\x1c.greenlight.SignerStateEntry\x12,\n\x08requests\x18\x05 \x03(\x0b\x32\x1a.greenlight.PendingRequest\"\x07\n\x05\x45mpty\"O\n\x07\x41\x64\x64ress\x12(\n\x04type\x18\x01 \x01(\x0e\x32\x1a.greenlight.NetAddressType\x12\x0c\n\x04\x61\x64\x64r\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"\x10\n\x0eGetInfoRequest\"\xb2\x01\n\x0fGetInfoResponse\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\x12\r\n\x05\x61lias\x18\x02 \x01(\t\x12\r\n\x05\x63olor\x18\x03 \x01(\x0c\x12\x11\n\tnum_peers\x18\x04 \x01(\r\x12&\n\taddresses\x18\x05 \x03(\x0b\x32\x13.greenlight.Address\x12\x0f\n\x07version\x18\x06 \x01(\t\x12\x13\n\x0b\x62lockheight\x18\x07 \x01(\r\x12\x0f\n\x07network\x18\x08 \x01(\t\"\r\n\x0bStopRequest\"\x0e\n\x0cStopResponse\"/\n\x0e\x43onnectRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\t\x12\x0c\n\x04\x61\x64\x64r\x18\x02 \x01(\t\"4\n\x0f\x43onnectResponse\x12\x0f\n\x07node_id\x18\x01 \x01(\t\x12\x10\n\x08\x66\x65\x61tures\x18\x02 \x01(\t\"#\n\x10ListPeersRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\t\"\x81\x01\n\x04Htlc\x12\x11\n\tdirection\x18\x01 \x01(\t\x12\n\n\x02id\x18\x02 \x01(\x04\x12\x0e\n\x06\x61mount\x18\x03 \x01(\t\x12\x0e\n\x06\x65xpiry\x18\x04 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x05 \x01(\t\x12\r\n\x05state\x18\x06 \x01(\t\x12\x15\n\rlocal_trimmed\x18\x07 \x01(\x08\"(\n\x07\x41liases\x12\r\n\x05local\x18\x01 \x01(\t\x12\x0e\n\x06remote\x18\x02 \x01(\t\"\x8f\x03\n\x07\x43hannel\x12\r\n\x05state\x18\x01 \x01(\t\x12\r\n\x05owner\x18\x02 \x01(\t\x12\"\n\x05\x61lias\x18\x12 \x01(\x0b\x32\x13.greenlight.Aliases\x12\x18\n\x10short_channel_id\x18\x03 \x01(\t\x12\x11\n\tdirection\x18\x04 \x01(\r\x12\x12\n\nchannel_id\x18\x05 \x01(\t\x12\x14\n\x0c\x66unding_txid\x18\x06 \x01(\t\x12\x15\n\rclose_to_addr\x18\x07 \x01(\t\x12\x10\n\x08\x63lose_to\x18\x08 \x01(\t\x12\x0f\n\x07private\x18\t \x01(\x08\x12\r\n\x05total\x18\n \x01(\t\x12\x12\n\ndust_limit\x18\x0b \x01(\t\x12\x11\n\tspendable\x18\x0c \x01(\t\x12\x12\n\nreceivable\x18\r \x01(\t\x12\x1b\n\x13their_to_self_delay\x18\x0e \x01(\r\x12\x19\n\x11our_to_self_delay\x18\x0f \x01(\r\x12\x0e\n\x06status\x18\x10 \x03(\t\x12\x1f\n\x05htlcs\x18\x11 \x03(\x0b\x32\x10.greenlight.Htlc\"\x86\x01\n\x04Peer\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x11\n\tconnected\x18\x02 \x01(\x08\x12&\n\taddresses\x18\x03 \x03(\x0b\x32\x13.greenlight.Address\x12\x10\n\x08\x66\x65\x61tures\x18\x04 \x01(\t\x12%\n\x08\x63hannels\x18\x05 \x03(\x0b\x32\x13.greenlight.Channel\"4\n\x11ListPeersResponse\x12\x1f\n\x05peers\x18\x01 \x03(\x0b\x32\x10.greenlight.Peer\"3\n\x11\x44isconnectRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\t\x12\r\n\x05\x66orce\x18\x02 \x01(\x08\"\x14\n\x12\x44isconnectResponse\"B\n\x0eNewAddrRequest\x12\x30\n\x0c\x61\x64\x64ress_type\x18\x01 \x01(\x0e\x32\x1a.greenlight.BtcAddressType\"T\n\x0fNewAddrResponse\x12\x30\n\x0c\x61\x64\x64ress_type\x18\x01 \x01(\x0e\x32\x1a.greenlight.BtcAddressType\x12\x0f\n\x07\x61\x64\x64ress\x18\x02 \x01(\t\"=\n\x10ListFundsRequest\x12)\n\x07minconf\x18\x01 \x01(\x0b\x32\x18.greenlight.Confirmation\"\xc3\x01\n\x0fListFundsOutput\x12$\n\x06output\x18\x01 \x01(\x0b\x32\x14.greenlight.Outpoint\x12\"\n\x06\x61mount\x18\x02 \x01(\x0b\x32\x12.greenlight.Amount\x12\x0f\n\x07\x61\x64\x64ress\x18\x04 \x01(\t\x12(\n\x06status\x18\x05 \x01(\x0e\x32\x18.greenlight.OutputStatus\x12\x10\n\x08reserved\x18\x06 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x07 \x01(\r\"\xac\x01\n\x10ListFundsChannel\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12\x11\n\tconnected\x18\x02 \x01(\x08\x12\x18\n\x10short_channel_id\x18\x03 \x01(\x04\x12\x17\n\x0four_amount_msat\x18\x04 \x01(\x04\x12\x13\n\x0b\x61mount_msat\x18\x05 \x01(\x04\x12\x14\n\x0c\x66unding_txid\x18\x06 \x01(\x0c\x12\x16\n\x0e\x66unding_output\x18\x07 \x01(\r\"q\n\x11ListFundsResponse\x12,\n\x07outputs\x18\x01 \x03(\x0b\x32\x1b.greenlight.ListFundsOutput\x12.\n\x08\x63hannels\x18\x02 \x03(\x0b\x32\x1c.greenlight.ListFundsChannel\"a\n\x07\x46\x65\x65rate\x12+\n\x06preset\x18\x01 \x01(\x0e\x32\x19.greenlight.FeeratePresetH\x00\x12\x0f\n\x05perkw\x18\x05 \x01(\x04H\x00\x12\x0f\n\x05perkb\x18\x06 \x01(\x04H\x00\x42\x07\n\x05value\"\x1e\n\x0c\x43onfirmation\x12\x0e\n\x06\x62locks\x18\x01 \x01(\r\"\xc0\x01\n\x0fWithdrawRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\t\x12\"\n\x06\x61mount\x18\x02 \x01(\x0b\x32\x12.greenlight.Amount\x12$\n\x07\x66\x65\x65rate\x18\x03 \x01(\x0b\x32\x13.greenlight.Feerate\x12)\n\x07minconf\x18\x07 \x01(\x0b\x32\x18.greenlight.Confirmation\x12#\n\x05utxos\x18\x08 \x03(\x0b\x32\x14.greenlight.Outpoint\",\n\x10WithdrawResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"\xbe\x01\n\x12\x46undChannelRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\x12\"\n\x06\x61mount\x18\x02 \x01(\x0b\x32\x12.greenlight.Amount\x12$\n\x07\x66\x65\x65rate\x18\x03 \x01(\x0b\x32\x13.greenlight.Feerate\x12\x10\n\x08\x61nnounce\x18\x07 \x01(\x08\x12)\n\x07minconf\x18\x08 \x01(\x0b\x32\x18.greenlight.Confirmation\x12\x10\n\x08\x63lose_to\x18\n \x01(\t\"(\n\x08Outpoint\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06outnum\x18\x02 \x01(\r\"o\n\x13\x46undChannelResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12&\n\x08outpoint\x18\x02 \x01(\x0b\x32\x14.greenlight.Outpoint\x12\x12\n\nchannel_id\x18\x03 \x01(\x0c\x12\x10\n\x08\x63lose_to\x18\x04 \x01(\t\"\x1a\n\x07Timeout\x12\x0f\n\x07seconds\x18\x01 \x01(\r\"!\n\x0e\x42itcoinAddress\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\"\x87\x01\n\x13\x43loseChannelRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\x12.\n\x11unilateraltimeout\x18\x02 \x01(\x0b\x32\x13.greenlight.Timeout\x12/\n\x0b\x64\x65stination\x18\x03 \x01(\x0b\x32\x1a.greenlight.BitcoinAddress\"b\n\x14\x43loseChannelResponse\x12\x30\n\nclose_type\x18\x01 \x01(\x0e\x32\x1c.greenlight.CloseChannelType\x12\n\n\x02tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"l\n\x06\x41mount\x12\x16\n\x0cmillisatoshi\x18\x01 \x01(\x04H\x00\x12\x11\n\x07satoshi\x18\x02 \x01(\x04H\x00\x12\x11\n\x07\x62itcoin\x18\x03 \x01(\x04H\x00\x12\r\n\x03\x61ll\x18\x04 \x01(\x08H\x00\x12\r\n\x03\x61ny\x18\x05 \x01(\x08H\x00\x42\x06\n\x04unit\"j\n\x0eInvoiceRequest\x12\"\n\x06\x61mount\x18\x01 \x01(\x0b\x32\x12.greenlight.Amount\x12\r\n\x05label\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x03 \x01(\t\x12\x10\n\x08preimage\x18\x04 \x01(\x0c\"\x8d\x02\n\x07Invoice\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\"\n\x06\x61mount\x18\x03 \x01(\x0b\x32\x12.greenlight.Amount\x12$\n\x08received\x18\x04 \x01(\x0b\x32\x12.greenlight.Amount\x12)\n\x06status\x18\x05 \x01(\x0e\x32\x19.greenlight.InvoiceStatus\x12\x14\n\x0cpayment_time\x18\x06 \x01(\r\x12\x13\n\x0b\x65xpiry_time\x18\x07 \x01(\r\x12\x0e\n\x06\x62olt11\x18\x08 \x01(\t\x12\x14\n\x0cpayment_hash\x18\t \x01(\x0c\x12\x18\n\x10payment_preimage\x18\n \x01(\x0c\"\x8c\x01\n\nPayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\"\n\x06\x61mount\x18\x02 \x01(\x0b\x32\x12.greenlight.Amount\x12\x0f\n\x07timeout\x18\x03 \x01(\r\x12\x15\n\rmaxfeepercent\x18\x04 \x01(\x01\x12\"\n\x06maxfee\x18\x05 \x01(\x0b\x32\x12.greenlight.Amount\"\xfc\x01\n\x07Payment\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\x0c\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x18\n\x10payment_preimage\x18\x03 \x01(\x0c\x12%\n\x06status\x18\x04 \x01(\x0e\x32\x15.greenlight.PayStatus\x12\"\n\x06\x61mount\x18\x05 \x01(\x0b\x32\x12.greenlight.Amount\x12\'\n\x0b\x61mount_sent\x18\x06 \x01(\x0b\x32\x12.greenlight.Amount\x12\x0e\n\x06\x62olt11\x18\x07 \x01(\t\x12\x12\n\ncreated_at\x18\x08 \x01(\x01\x12\x14\n\x0c\x63ompleted_at\x18\t \x01(\x04\"C\n\x11PaymentIdentifier\x12\x10\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x12\x16\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x00\x42\x04\n\x02id\"H\n\x13ListPaymentsRequest\x12\x31\n\nidentifier\x18\x01 \x01(\x0b\x32\x1d.greenlight.PaymentIdentifier\"=\n\x14ListPaymentsResponse\x12%\n\x08payments\x18\x01 \x03(\x0b\x32\x13.greenlight.Payment\"W\n\x11InvoiceIdentifier\x12\x0f\n\x05label\x18\x01 \x01(\tH\x00\x12\x13\n\tinvstring\x18\x02 \x01(\tH\x00\x12\x16\n\x0cpayment_hash\x18\x03 \x01(\x0cH\x00\x42\x04\n\x02id\"H\n\x13ListInvoicesRequest\x12\x31\n\nidentifier\x18\x01 \x01(\x0b\x32\x1d.greenlight.InvoiceIdentifier\"\x16\n\x14StreamIncomingFilter\"=\n\x14ListInvoicesResponse\x12%\n\x08invoices\x18\x01 \x03(\x0b\x32\x13.greenlight.Invoice\"\'\n\x08TlvField\x12\x0c\n\x04type\x18\x01 \x01(\x04\x12\r\n\x05value\x18\x02 \x01(\x0c\"\xa5\x01\n\x0fOffChainPayment\x12\r\n\x05label\x18\x01 \x01(\t\x12\x10\n\x08preimage\x18\x02 \x01(\x0c\x12\"\n\x06\x61mount\x18\x03 \x01(\x0b\x32\x12.greenlight.Amount\x12\'\n\textratlvs\x18\x04 \x03(\x0b\x32\x14.greenlight.TlvField\x12\x14\n\x0cpayment_hash\x18\x05 \x01(\x0c\x12\x0e\n\x06\x62olt11\x18\x06 \x01(\t\"M\n\x0fIncomingPayment\x12/\n\x08offchain\x18\x01 \x01(\x0b\x32\x1b.greenlight.OffChainPaymentH\x00\x42\t\n\x07\x64\x65tails\"x\n\x0cRoutehintHop\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x02 \x01(\t\x12\x10\n\x08\x66\x65\x65_base\x18\x03 \x01(\x04\x12\x10\n\x08\x66\x65\x65_prop\x18\x04 \x01(\r\x12\x19\n\x11\x63ltv_expiry_delta\x18\x05 \x01(\r\"3\n\tRoutehint\x12&\n\x04hops\x18\x01 \x03(\x0b\x32\x18.greenlight.RoutehintHop\"\xa8\x01\n\x0eKeysendRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\x12\"\n\x06\x61mount\x18\x02 \x01(\x0b\x32\x12.greenlight.Amount\x12\r\n\x05label\x18\x03 \x01(\t\x12)\n\nroutehints\x18\x04 \x03(\x0b\x32\x15.greenlight.Routehint\x12\'\n\textratlvs\x18\x05 \x03(\x0b\x32\x14.greenlight.TlvField\"\x12\n\x10StreamLogRequest\"\x18\n\x08LogEntry\x12\x0c\n\x04line\x18\x01 \x01(\t\"?\n\x10SignerStateEntry\x12\x0f\n\x07version\x18\x01 \x01(\x04\x12\x0b\n\x03key\x18\x02 \x01(\t\x12\r\n\x05value\x18\x03 \x01(\x0c\"r\n\x0ePendingRequest\x12\x0f\n\x07request\x18\x01 \x01(\x0c\x12\x0b\n\x03uri\x18\x02 \x01(\t\x12\x11\n\tsignature\x18\x03 \x01(\x0c\x12\x0e\n\x06pubkey\x18\x04 \x01(\x0c\x12\x11\n\ttimestamp\x18\x05 \x01(\x04\x12\x0c\n\x04rune\x18\x06 \x01(\x0c\"=\n\nNodeConfig\x12/\n\x0bstartupmsgs\x18\x01 \x03(\x0b\x32\x1a.greenlight.StartupMessage\"!\n\x08GlConfig\x12\x15\n\rclose_to_addr\x18\x01 \x01(\t\"3\n\x0eStartupMessage\x12\x0f\n\x07request\x18\x01 \x01(\x0c\x12\x10\n\x08response\x18\x02 \x01(\x0c\"\x18\n\x16StreamCustommsgRequest\"-\n\tCustommsg\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"\xa4\x01\n\x14TrampolinePayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x1a\n\x12trampoline_node_id\x18\x02 \x01(\x0c\x12\x13\n\x0b\x61mount_msat\x18\x03 \x01(\x04\x12\r\n\x05label\x18\x04 \x01(\t\x12\x15\n\rmaxfeepercent\x18\x05 \x01(\x02\x12\x10\n\x08maxdelay\x18\x06 \x01(\r\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\"\xd5\x01\n\x15TrampolinePayResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x12\n\ncreated_at\x18\x03 \x01(\x01\x12\r\n\x05parts\x18\x04 \x01(\r\x12\x13\n\x0b\x61mount_msat\x18\x05 \x01(\x04\x12\x18\n\x10\x61mount_sent_msat\x18\x06 \x01(\x04\x12\x13\n\x0b\x64\x65stination\x18\x07 \x01(\x0c\"%\n\tPayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x02*:\n\x0eNetAddressType\x12\x08\n\x04Ipv4\x10\x00\x12\x08\n\x04Ipv6\x10\x01\x12\t\n\x05TorV2\x10\x02\x12\t\n\x05TorV3\x10\x03*-\n\x0e\x42tcAddressType\x12\n\n\x06\x42\x45\x43H32\x10\x00\x12\x0f\n\x0bP2SH_SEGWIT\x10\x01*.\n\x0cOutputStatus\x12\r\n\tCONFIRMED\x10\x00\x12\x0f\n\x0bUNCONFIRMED\x10\x01*1\n\rFeeratePreset\x12\n\n\x06NORMAL\x10\x00\x12\x08\n\x04SLOW\x10\x01\x12\n\n\x06URGENT\x10\x02*.\n\x10\x43loseChannelType\x12\n\n\x06MUTUAL\x10\x00\x12\x0e\n\nUNILATERAL\x10\x01*2\n\rInvoiceStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02*2\n\tPayStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x32\x89\x04\n\x04Node\x12S\n\x0eStreamIncoming\x12 .greenlight.StreamIncomingFilter\x1a\x1b.greenlight.IncomingPayment\"\x00\x30\x01\x12\x43\n\tStreamLog\x12\x1c.greenlight.StreamLogRequest\x1a\x14.greenlight.LogEntry\"\x00\x30\x01\x12P\n\x0fStreamCustommsg\x12\".greenlight.StreamCustommsgRequest\x1a\x15.greenlight.Custommsg\"\x00\x30\x01\x12\x42\n\x11StreamHsmRequests\x12\x11.greenlight.Empty\x1a\x16.greenlight.HsmRequest\"\x00\x30\x01\x12\x41\n\x11RespondHsmRequest\x12\x17.greenlight.HsmResponse\x1a\x11.greenlight.Empty\"\x00\x12\x36\n\tConfigure\x12\x14.greenlight.GlConfig\x1a\x11.greenlight.Empty\"\x00\x12V\n\rTrampolinePay\x12 .greenlight.TrampolinePayRequest\x1a!.greenlight.TrampolinePayResponse\"\x00\x32s\n\x03Hsm\x12<\n\x07Request\x12\x16.greenlight.HsmRequest\x1a\x17.greenlight.HsmResponse\"\x00\x12.\n\x04Ping\x12\x11.greenlight.Empty\x1a\x11.greenlight.Empty\"\x00\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x19glclient/greenlight.proto\x12\ngreenlight\"H\n\x11HsmRequestContext\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\x12\x0c\n\x04\x64\x62id\x18\x02 \x01(\x04\x12\x14\n\x0c\x63\x61pabilities\x18\x03 \x01(\x04\"q\n\x0bHsmResponse\x12\x12\n\nrequest_id\x18\x01 \x01(\r\x12\x0b\n\x03raw\x18\x02 \x01(\x0c\x12\x32\n\x0csigner_state\x18\x05 \x03(\x0b\x32\x1c.greenlight.SignerStateEntry\x12\r\n\x05\x65rror\x18\x06 \x01(\t\"\xbf\x01\n\nHsmRequest\x12\x12\n\nrequest_id\x18\x01 \x01(\r\x12.\n\x07\x63ontext\x18\x02 \x01(\x0b\x32\x1d.greenlight.HsmRequestContext\x12\x0b\n\x03raw\x18\x03 \x01(\x0c\x12\x32\n\x0csigner_state\x18\x04 \x03(\x0b\x32\x1c.greenlight.SignerStateEntry\x12,\n\x08requests\x18\x05 \x03(\x0b\x32\x1a.greenlight.PendingRequest\"\x07\n\x05\x45mpty\"l\n\x06\x41mount\x12\x16\n\x0cmillisatoshi\x18\x01 \x01(\x04H\x00\x12\x11\n\x07satoshi\x18\x02 \x01(\x04H\x00\x12\x11\n\x07\x62itcoin\x18\x03 \x01(\x04H\x00\x12\r\n\x03\x61ll\x18\x04 \x01(\x08H\x00\x12\r\n\x03\x61ny\x18\x05 \x01(\x08H\x00\x42\x06\n\x04unit\"\x16\n\x14StreamIncomingFilter\"\'\n\x08TlvField\x12\x0c\n\x04type\x18\x01 \x01(\x04\x12\r\n\x05value\x18\x02 \x01(\x0c\"\xa5\x01\n\x0fOffChainPayment\x12\r\n\x05label\x18\x01 \x01(\t\x12\x10\n\x08preimage\x18\x02 \x01(\x0c\x12\"\n\x06\x61mount\x18\x03 \x01(\x0b\x32\x12.greenlight.Amount\x12\'\n\textratlvs\x18\x04 \x03(\x0b\x32\x14.greenlight.TlvField\x12\x14\n\x0cpayment_hash\x18\x05 \x01(\x0c\x12\x0e\n\x06\x62olt11\x18\x06 \x01(\t\"M\n\x0fIncomingPayment\x12/\n\x08offchain\x18\x01 \x01(\x0b\x32\x1b.greenlight.OffChainPaymentH\x00\x42\t\n\x07\x64\x65tails\"\x12\n\x10StreamLogRequest\"\x18\n\x08LogEntry\x12\x0c\n\x04line\x18\x01 \x01(\t\"?\n\x10SignerStateEntry\x12\x0f\n\x07version\x18\x01 \x01(\x04\x12\x0b\n\x03key\x18\x02 \x01(\t\x12\r\n\x05value\x18\x03 \x01(\x0c\"r\n\x0ePendingRequest\x12\x0f\n\x07request\x18\x01 \x01(\x0c\x12\x0b\n\x03uri\x18\x02 \x01(\t\x12\x11\n\tsignature\x18\x03 \x01(\x0c\x12\x0e\n\x06pubkey\x18\x04 \x01(\x0c\x12\x11\n\ttimestamp\x18\x05 \x01(\x04\x12\x0c\n\x04rune\x18\x06 \x01(\x0c\"=\n\nNodeConfig\x12/\n\x0bstartupmsgs\x18\x01 \x03(\x0b\x32\x1a.greenlight.StartupMessage\"!\n\x08GlConfig\x12\x15\n\rclose_to_addr\x18\x01 \x01(\t\"3\n\x0eStartupMessage\x12\x0f\n\x07request\x18\x01 \x01(\x0c\x12\x10\n\x08response\x18\x02 \x01(\x0c\"\x18\n\x16StreamCustommsgRequest\"-\n\tCustommsg\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"\xa4\x01\n\x14TrampolinePayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x1a\n\x12trampoline_node_id\x18\x02 \x01(\x0c\x12\x13\n\x0b\x61mount_msat\x18\x03 \x01(\x04\x12\r\n\x05label\x18\x04 \x01(\t\x12\x15\n\rmaxfeepercent\x18\x05 \x01(\x02\x12\x10\n\x08maxdelay\x18\x06 \x01(\r\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\"\xd5\x01\n\x15TrampolinePayResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x12\n\ncreated_at\x18\x03 \x01(\x01\x12\r\n\x05parts\x18\x04 \x01(\r\x12\x13\n\x0b\x61mount_msat\x18\x05 \x01(\x04\x12\x18\n\x10\x61mount_sent_msat\x18\x06 \x01(\x04\x12\x13\n\x0b\x64\x65stination\x18\x07 \x01(\x0c\"%\n\tPayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x02\"k\n\x11LspInvoiceRequest\x12\x0e\n\x06lsp_id\x18\x01 \x01(\t\x12\r\n\x05token\x18\x02 \x01(\t\x12\x13\n\x0b\x61mount_msat\x18\x03 \x01(\x04\x12\x13\n\x0b\x64\x65scription\x18\x04 \x01(\t\x12\r\n\x05label\x18\x05 \x01(\t\"}\n\x12LspInvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x15\n\rcreated_index\x18\x02 \x01(\r\x12\x12\n\nexpires_at\x18\x03 \x01(\r\x12\x14\n\x0cpayment_hash\x18\x04 \x01(\x0c\x12\x16\n\x0epayment_secret\x18\x05 \x01(\x0c\x32\xd8\x04\n\x04Node\x12M\n\nLspInvoice\x12\x1d.greenlight.LspInvoiceRequest\x1a\x1e.greenlight.LspInvoiceResponse\"\x00\x12S\n\x0eStreamIncoming\x12 .greenlight.StreamIncomingFilter\x1a\x1b.greenlight.IncomingPayment\"\x00\x30\x01\x12\x43\n\tStreamLog\x12\x1c.greenlight.StreamLogRequest\x1a\x14.greenlight.LogEntry\"\x00\x30\x01\x12P\n\x0fStreamCustommsg\x12\".greenlight.StreamCustommsgRequest\x1a\x15.greenlight.Custommsg\"\x00\x30\x01\x12\x42\n\x11StreamHsmRequests\x12\x11.greenlight.Empty\x1a\x16.greenlight.HsmRequest\"\x00\x30\x01\x12\x41\n\x11RespondHsmRequest\x12\x17.greenlight.HsmResponse\x1a\x11.greenlight.Empty\"\x00\x12\x36\n\tConfigure\x12\x14.greenlight.GlConfig\x1a\x11.greenlight.Empty\"\x00\x12V\n\rTrampolinePay\x12 .greenlight.TrampolinePayRequest\x1a!.greenlight.TrampolinePayResponse\"\x00\x32s\n\x03Hsm\x12<\n\x07Request\x12\x16.greenlight.HsmRequest\x1a\x17.greenlight.HsmResponse\"\x00\x12.\n\x04Ping\x12\x11.greenlight.Empty\x1a\x11.greenlight.Empty\"\x00\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'glclient.greenlight_pb2', _globals) if not _descriptor._USE_C_DESCRIPTORS: DESCRIPTOR._loaded_options = None - _globals['_NETADDRESSTYPE']._serialized_start=6264 - _globals['_NETADDRESSTYPE']._serialized_end=6322 - _globals['_BTCADDRESSTYPE']._serialized_start=6324 - _globals['_BTCADDRESSTYPE']._serialized_end=6369 - _globals['_OUTPUTSTATUS']._serialized_start=6371 - _globals['_OUTPUTSTATUS']._serialized_end=6417 - _globals['_FEERATEPRESET']._serialized_start=6419 - _globals['_FEERATEPRESET']._serialized_end=6468 - _globals['_CLOSECHANNELTYPE']._serialized_start=6470 - _globals['_CLOSECHANNELTYPE']._serialized_end=6516 - _globals['_INVOICESTATUS']._serialized_start=6518 - _globals['_INVOICESTATUS']._serialized_end=6568 - _globals['_PAYSTATUS']._serialized_start=6570 - _globals['_PAYSTATUS']._serialized_end=6620 _globals['_HSMREQUESTCONTEXT']._serialized_start=41 _globals['_HSMREQUESTCONTEXT']._serialized_end=113 _globals['_HSMRESPONSE']._serialized_start=115 @@ -43,132 +39,46 @@ _globals['_HSMREQUEST']._serialized_end=422 _globals['_EMPTY']._serialized_start=424 _globals['_EMPTY']._serialized_end=431 - _globals['_ADDRESS']._serialized_start=433 - _globals['_ADDRESS']._serialized_end=512 - _globals['_GETINFOREQUEST']._serialized_start=514 - _globals['_GETINFOREQUEST']._serialized_end=530 - _globals['_GETINFORESPONSE']._serialized_start=533 - _globals['_GETINFORESPONSE']._serialized_end=711 - _globals['_STOPREQUEST']._serialized_start=713 - _globals['_STOPREQUEST']._serialized_end=726 - _globals['_STOPRESPONSE']._serialized_start=728 - _globals['_STOPRESPONSE']._serialized_end=742 - _globals['_CONNECTREQUEST']._serialized_start=744 - _globals['_CONNECTREQUEST']._serialized_end=791 - _globals['_CONNECTRESPONSE']._serialized_start=793 - _globals['_CONNECTRESPONSE']._serialized_end=845 - _globals['_LISTPEERSREQUEST']._serialized_start=847 - _globals['_LISTPEERSREQUEST']._serialized_end=882 - _globals['_HTLC']._serialized_start=885 - _globals['_HTLC']._serialized_end=1014 - _globals['_ALIASES']._serialized_start=1016 - _globals['_ALIASES']._serialized_end=1056 - _globals['_CHANNEL']._serialized_start=1059 - _globals['_CHANNEL']._serialized_end=1458 - _globals['_PEER']._serialized_start=1461 - _globals['_PEER']._serialized_end=1595 - _globals['_LISTPEERSRESPONSE']._serialized_start=1597 - _globals['_LISTPEERSRESPONSE']._serialized_end=1649 - _globals['_DISCONNECTREQUEST']._serialized_start=1651 - _globals['_DISCONNECTREQUEST']._serialized_end=1702 - _globals['_DISCONNECTRESPONSE']._serialized_start=1704 - _globals['_DISCONNECTRESPONSE']._serialized_end=1724 - _globals['_NEWADDRREQUEST']._serialized_start=1726 - _globals['_NEWADDRREQUEST']._serialized_end=1792 - _globals['_NEWADDRRESPONSE']._serialized_start=1794 - _globals['_NEWADDRRESPONSE']._serialized_end=1878 - _globals['_LISTFUNDSREQUEST']._serialized_start=1880 - _globals['_LISTFUNDSREQUEST']._serialized_end=1941 - _globals['_LISTFUNDSOUTPUT']._serialized_start=1944 - _globals['_LISTFUNDSOUTPUT']._serialized_end=2139 - _globals['_LISTFUNDSCHANNEL']._serialized_start=2142 - _globals['_LISTFUNDSCHANNEL']._serialized_end=2314 - _globals['_LISTFUNDSRESPONSE']._serialized_start=2316 - _globals['_LISTFUNDSRESPONSE']._serialized_end=2429 - _globals['_FEERATE']._serialized_start=2431 - _globals['_FEERATE']._serialized_end=2528 - _globals['_CONFIRMATION']._serialized_start=2530 - _globals['_CONFIRMATION']._serialized_end=2560 - _globals['_WITHDRAWREQUEST']._serialized_start=2563 - _globals['_WITHDRAWREQUEST']._serialized_end=2755 - _globals['_WITHDRAWRESPONSE']._serialized_start=2757 - _globals['_WITHDRAWRESPONSE']._serialized_end=2801 - _globals['_FUNDCHANNELREQUEST']._serialized_start=2804 - _globals['_FUNDCHANNELREQUEST']._serialized_end=2994 - _globals['_OUTPOINT']._serialized_start=2996 - _globals['_OUTPOINT']._serialized_end=3036 - _globals['_FUNDCHANNELRESPONSE']._serialized_start=3038 - _globals['_FUNDCHANNELRESPONSE']._serialized_end=3149 - _globals['_TIMEOUT']._serialized_start=3151 - _globals['_TIMEOUT']._serialized_end=3177 - _globals['_BITCOINADDRESS']._serialized_start=3179 - _globals['_BITCOINADDRESS']._serialized_end=3212 - _globals['_CLOSECHANNELREQUEST']._serialized_start=3215 - _globals['_CLOSECHANNELREQUEST']._serialized_end=3350 - _globals['_CLOSECHANNELRESPONSE']._serialized_start=3352 - _globals['_CLOSECHANNELRESPONSE']._serialized_end=3450 - _globals['_AMOUNT']._serialized_start=3452 - _globals['_AMOUNT']._serialized_end=3560 - _globals['_INVOICEREQUEST']._serialized_start=3562 - _globals['_INVOICEREQUEST']._serialized_end=3668 - _globals['_INVOICE']._serialized_start=3671 - _globals['_INVOICE']._serialized_end=3940 - _globals['_PAYREQUEST']._serialized_start=3943 - _globals['_PAYREQUEST']._serialized_end=4083 - _globals['_PAYMENT']._serialized_start=4086 - _globals['_PAYMENT']._serialized_end=4338 - _globals['_PAYMENTIDENTIFIER']._serialized_start=4340 - _globals['_PAYMENTIDENTIFIER']._serialized_end=4407 - _globals['_LISTPAYMENTSREQUEST']._serialized_start=4409 - _globals['_LISTPAYMENTSREQUEST']._serialized_end=4481 - _globals['_LISTPAYMENTSRESPONSE']._serialized_start=4483 - _globals['_LISTPAYMENTSRESPONSE']._serialized_end=4544 - _globals['_INVOICEIDENTIFIER']._serialized_start=4546 - _globals['_INVOICEIDENTIFIER']._serialized_end=4633 - _globals['_LISTINVOICESREQUEST']._serialized_start=4635 - _globals['_LISTINVOICESREQUEST']._serialized_end=4707 - _globals['_STREAMINCOMINGFILTER']._serialized_start=4709 - _globals['_STREAMINCOMINGFILTER']._serialized_end=4731 - _globals['_LISTINVOICESRESPONSE']._serialized_start=4733 - _globals['_LISTINVOICESRESPONSE']._serialized_end=4794 - _globals['_TLVFIELD']._serialized_start=4796 - _globals['_TLVFIELD']._serialized_end=4835 - _globals['_OFFCHAINPAYMENT']._serialized_start=4838 - _globals['_OFFCHAINPAYMENT']._serialized_end=5003 - _globals['_INCOMINGPAYMENT']._serialized_start=5005 - _globals['_INCOMINGPAYMENT']._serialized_end=5082 - _globals['_ROUTEHINTHOP']._serialized_start=5084 - _globals['_ROUTEHINTHOP']._serialized_end=5204 - _globals['_ROUTEHINT']._serialized_start=5206 - _globals['_ROUTEHINT']._serialized_end=5257 - _globals['_KEYSENDREQUEST']._serialized_start=5260 - _globals['_KEYSENDREQUEST']._serialized_end=5428 - _globals['_STREAMLOGREQUEST']._serialized_start=5430 - _globals['_STREAMLOGREQUEST']._serialized_end=5448 - _globals['_LOGENTRY']._serialized_start=5450 - _globals['_LOGENTRY']._serialized_end=5474 - _globals['_SIGNERSTATEENTRY']._serialized_start=5476 - _globals['_SIGNERSTATEENTRY']._serialized_end=5539 - _globals['_PENDINGREQUEST']._serialized_start=5541 - _globals['_PENDINGREQUEST']._serialized_end=5655 - _globals['_NODECONFIG']._serialized_start=5657 - _globals['_NODECONFIG']._serialized_end=5718 - _globals['_GLCONFIG']._serialized_start=5720 - _globals['_GLCONFIG']._serialized_end=5753 - _globals['_STARTUPMESSAGE']._serialized_start=5755 - _globals['_STARTUPMESSAGE']._serialized_end=5806 - _globals['_STREAMCUSTOMMSGREQUEST']._serialized_start=5808 - _globals['_STREAMCUSTOMMSGREQUEST']._serialized_end=5832 - _globals['_CUSTOMMSG']._serialized_start=5834 - _globals['_CUSTOMMSG']._serialized_end=5879 - _globals['_TRAMPOLINEPAYREQUEST']._serialized_start=5882 - _globals['_TRAMPOLINEPAYREQUEST']._serialized_end=6046 - _globals['_TRAMPOLINEPAYRESPONSE']._serialized_start=6049 - _globals['_TRAMPOLINEPAYRESPONSE']._serialized_end=6262 - _globals['_TRAMPOLINEPAYRESPONSE_PAYSTATUS']._serialized_start=6225 - _globals['_TRAMPOLINEPAYRESPONSE_PAYSTATUS']._serialized_end=6262 - _globals['_NODE']._serialized_start=6623 - _globals['_NODE']._serialized_end=7144 - _globals['_HSM']._serialized_start=7146 - _globals['_HSM']._serialized_end=7261 + _globals['_AMOUNT']._serialized_start=433 + _globals['_AMOUNT']._serialized_end=541 + _globals['_STREAMINCOMINGFILTER']._serialized_start=543 + _globals['_STREAMINCOMINGFILTER']._serialized_end=565 + _globals['_TLVFIELD']._serialized_start=567 + _globals['_TLVFIELD']._serialized_end=606 + _globals['_OFFCHAINPAYMENT']._serialized_start=609 + _globals['_OFFCHAINPAYMENT']._serialized_end=774 + _globals['_INCOMINGPAYMENT']._serialized_start=776 + _globals['_INCOMINGPAYMENT']._serialized_end=853 + _globals['_STREAMLOGREQUEST']._serialized_start=855 + _globals['_STREAMLOGREQUEST']._serialized_end=873 + _globals['_LOGENTRY']._serialized_start=875 + _globals['_LOGENTRY']._serialized_end=899 + _globals['_SIGNERSTATEENTRY']._serialized_start=901 + _globals['_SIGNERSTATEENTRY']._serialized_end=964 + _globals['_PENDINGREQUEST']._serialized_start=966 + _globals['_PENDINGREQUEST']._serialized_end=1080 + _globals['_NODECONFIG']._serialized_start=1082 + _globals['_NODECONFIG']._serialized_end=1143 + _globals['_GLCONFIG']._serialized_start=1145 + _globals['_GLCONFIG']._serialized_end=1178 + _globals['_STARTUPMESSAGE']._serialized_start=1180 + _globals['_STARTUPMESSAGE']._serialized_end=1231 + _globals['_STREAMCUSTOMMSGREQUEST']._serialized_start=1233 + _globals['_STREAMCUSTOMMSGREQUEST']._serialized_end=1257 + _globals['_CUSTOMMSG']._serialized_start=1259 + _globals['_CUSTOMMSG']._serialized_end=1304 + _globals['_TRAMPOLINEPAYREQUEST']._serialized_start=1307 + _globals['_TRAMPOLINEPAYREQUEST']._serialized_end=1471 + _globals['_TRAMPOLINEPAYRESPONSE']._serialized_start=1474 + _globals['_TRAMPOLINEPAYRESPONSE']._serialized_end=1687 + _globals['_TRAMPOLINEPAYRESPONSE_PAYSTATUS']._serialized_start=1650 + _globals['_TRAMPOLINEPAYRESPONSE_PAYSTATUS']._serialized_end=1687 + _globals['_LSPINVOICEREQUEST']._serialized_start=1689 + _globals['_LSPINVOICEREQUEST']._serialized_end=1796 + _globals['_LSPINVOICERESPONSE']._serialized_start=1798 + _globals['_LSPINVOICERESPONSE']._serialized_end=1923 + _globals['_NODE']._serialized_start=1926 + _globals['_NODE']._serialized_end=2526 + _globals['_HSM']._serialized_start=2528 + _globals['_HSM']._serialized_end=2643 # @@protoc_insertion_point(module_scope) diff --git a/libs/gl-client-py/glclient/greenlight_pb2.pyi b/libs/gl-client-py/glclient/greenlight_pb2.pyi index df1f57114..78426c3f3 100644 --- a/libs/gl-client-py/glclient/greenlight_pb2.pyi +++ b/libs/gl-client-py/glclient/greenlight_pb2.pyi @@ -19,1233 +19,138 @@ else: DESCRIPTOR: google.protobuf.descriptor.FileDescriptor -class _NetAddressType: - ValueType = typing.NewType("ValueType", builtins.int) - V: typing_extensions.TypeAlias = ValueType - -class _NetAddressTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_NetAddressType.ValueType], builtins.type): - DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor - Ipv4: _NetAddressType.ValueType # 0 - Ipv6: _NetAddressType.ValueType # 1 - TorV2: _NetAddressType.ValueType # 2 - TorV3: _NetAddressType.ValueType # 3 - -class NetAddressType(_NetAddressType, metaclass=_NetAddressTypeEnumTypeWrapper): ... - -Ipv4: NetAddressType.ValueType # 0 -Ipv6: NetAddressType.ValueType # 1 -TorV2: NetAddressType.ValueType # 2 -TorV3: NetAddressType.ValueType # 3 -global___NetAddressType = NetAddressType - -class _BtcAddressType: - ValueType = typing.NewType("ValueType", builtins.int) - V: typing_extensions.TypeAlias = ValueType - -class _BtcAddressTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_BtcAddressType.ValueType], builtins.type): - DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor - BECH32: _BtcAddressType.ValueType # 0 - """Default""" - P2SH_SEGWIT: _BtcAddressType.ValueType # 1 - -class BtcAddressType(_BtcAddressType, metaclass=_BtcAddressTypeEnumTypeWrapper): ... - -BECH32: BtcAddressType.ValueType # 0 -"""Default""" -P2SH_SEGWIT: BtcAddressType.ValueType # 1 -global___BtcAddressType = BtcAddressType - -class _OutputStatus: - ValueType = typing.NewType("ValueType", builtins.int) - V: typing_extensions.TypeAlias = ValueType - -class _OutputStatusEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_OutputStatus.ValueType], builtins.type): - DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor - CONFIRMED: _OutputStatus.ValueType # 0 - UNCONFIRMED: _OutputStatus.ValueType # 1 - -class OutputStatus(_OutputStatus, metaclass=_OutputStatusEnumTypeWrapper): ... - -CONFIRMED: OutputStatus.ValueType # 0 -UNCONFIRMED: OutputStatus.ValueType # 1 -global___OutputStatus = OutputStatus - -class _FeeratePreset: - ValueType = typing.NewType("ValueType", builtins.int) - V: typing_extensions.TypeAlias = ValueType - -class _FeeratePresetEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_FeeratePreset.ValueType], builtins.type): - DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor - NORMAL: _FeeratePreset.ValueType # 0 - SLOW: _FeeratePreset.ValueType # 1 - URGENT: _FeeratePreset.ValueType # 2 - -class FeeratePreset(_FeeratePreset, metaclass=_FeeratePresetEnumTypeWrapper): - """Let the node decide what feerate to apply based on its internal - view of the network's current feerate. - """ - -NORMAL: FeeratePreset.ValueType # 0 -SLOW: FeeratePreset.ValueType # 1 -URGENT: FeeratePreset.ValueType # 2 -global___FeeratePreset = FeeratePreset - -class _CloseChannelType: - ValueType = typing.NewType("ValueType", builtins.int) - V: typing_extensions.TypeAlias = ValueType - -class _CloseChannelTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_CloseChannelType.ValueType], builtins.type): - DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor - MUTUAL: _CloseChannelType.ValueType # 0 - UNILATERAL: _CloseChannelType.ValueType # 1 - -class CloseChannelType(_CloseChannelType, metaclass=_CloseChannelTypeEnumTypeWrapper): ... - -MUTUAL: CloseChannelType.ValueType # 0 -UNILATERAL: CloseChannelType.ValueType # 1 -global___CloseChannelType = CloseChannelType - -class _InvoiceStatus: - ValueType = typing.NewType("ValueType", builtins.int) - V: typing_extensions.TypeAlias = ValueType - -class _InvoiceStatusEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_InvoiceStatus.ValueType], builtins.type): - DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor - UNPAID: _InvoiceStatus.ValueType # 0 - PAID: _InvoiceStatus.ValueType # 1 - EXPIRED: _InvoiceStatus.ValueType # 2 - -class InvoiceStatus(_InvoiceStatus, metaclass=_InvoiceStatusEnumTypeWrapper): ... - -UNPAID: InvoiceStatus.ValueType # 0 -PAID: InvoiceStatus.ValueType # 1 -EXPIRED: InvoiceStatus.ValueType # 2 -global___InvoiceStatus = InvoiceStatus - -class _PayStatus: - ValueType = typing.NewType("ValueType", builtins.int) - V: typing_extensions.TypeAlias = ValueType - -class _PayStatusEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_PayStatus.ValueType], builtins.type): - DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor - PENDING: _PayStatus.ValueType # 0 - COMPLETE: _PayStatus.ValueType # 1 - FAILED: _PayStatus.ValueType # 2 - -class PayStatus(_PayStatus, metaclass=_PayStatusEnumTypeWrapper): ... - -PENDING: PayStatus.ValueType # 0 -COMPLETE: PayStatus.ValueType # 1 -FAILED: PayStatus.ValueType # 2 -global___PayStatus = PayStatus - -@typing.final -class HsmRequestContext(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - NODE_ID_FIELD_NUMBER: builtins.int - DBID_FIELD_NUMBER: builtins.int - CAPABILITIES_FIELD_NUMBER: builtins.int - node_id: builtins.bytes - dbid: builtins.int - capabilities: builtins.int - def __init__( - self, - *, - node_id: builtins.bytes = ..., - dbid: builtins.int = ..., - capabilities: builtins.int = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["capabilities", b"capabilities", "dbid", b"dbid", "node_id", b"node_id"]) -> None: ... - -global___HsmRequestContext = HsmRequestContext - -@typing.final -class HsmResponse(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - REQUEST_ID_FIELD_NUMBER: builtins.int - RAW_FIELD_NUMBER: builtins.int - SIGNER_STATE_FIELD_NUMBER: builtins.int - ERROR_FIELD_NUMBER: builtins.int - request_id: builtins.int - raw: builtins.bytes - error: builtins.str - """If the signer reported an error, and did therefore not include - `raw`, this is the stringified error, so we can print it in the - logs. This should help us collate policy errors with the changes - proposed by CLN - """ - @property - def signer_state(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___SignerStateEntry]: - """A list of updated key-value-version tuples that is to be - merged into the state tracked by the plugin. - """ - - def __init__( - self, - *, - request_id: builtins.int = ..., - raw: builtins.bytes = ..., - signer_state: collections.abc.Iterable[global___SignerStateEntry] | None = ..., - error: builtins.str = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["error", b"error", "raw", b"raw", "request_id", b"request_id", "signer_state", b"signer_state"]) -> None: ... - -global___HsmResponse = HsmResponse - -@typing.final -class HsmRequest(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - REQUEST_ID_FIELD_NUMBER: builtins.int - CONTEXT_FIELD_NUMBER: builtins.int - RAW_FIELD_NUMBER: builtins.int - SIGNER_STATE_FIELD_NUMBER: builtins.int - REQUESTS_FIELD_NUMBER: builtins.int - request_id: builtins.int - raw: builtins.bytes - @property - def context(self) -> global___HsmRequestContext: ... - @property - def signer_state(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___SignerStateEntry]: - """A list of key-value-version tuples that the signer should - use to update its internal state. - """ - - @property - def requests(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___PendingRequest]: - """Currently active requests that are used to justify changes - in state. - """ - - def __init__( - self, - *, - request_id: builtins.int = ..., - context: global___HsmRequestContext | None = ..., - raw: builtins.bytes = ..., - signer_state: collections.abc.Iterable[global___SignerStateEntry] | None = ..., - requests: collections.abc.Iterable[global___PendingRequest] | None = ..., - ) -> None: ... - def HasField(self, field_name: typing.Literal["context", b"context"]) -> builtins.bool: ... - def ClearField(self, field_name: typing.Literal["context", b"context", "raw", b"raw", "request_id", b"request_id", "requests", b"requests", "signer_state", b"signer_state"]) -> None: ... - -global___HsmRequest = HsmRequest - -@typing.final -class Empty(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - def __init__( - self, - ) -> None: ... - -global___Empty = Empty - -@typing.final -class Address(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - TYPE_FIELD_NUMBER: builtins.int - ADDR_FIELD_NUMBER: builtins.int - PORT_FIELD_NUMBER: builtins.int - type: global___NetAddressType.ValueType - addr: builtins.str - port: builtins.int - def __init__( - self, - *, - type: global___NetAddressType.ValueType = ..., - addr: builtins.str = ..., - port: builtins.int = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["addr", b"addr", "port", b"port", "type", b"type"]) -> None: ... - -global___Address = Address - -@typing.final -class GetInfoRequest(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - def __init__( - self, - ) -> None: ... - -global___GetInfoRequest = GetInfoRequest - -@typing.final -class GetInfoResponse(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - NODE_ID_FIELD_NUMBER: builtins.int - ALIAS_FIELD_NUMBER: builtins.int - COLOR_FIELD_NUMBER: builtins.int - NUM_PEERS_FIELD_NUMBER: builtins.int - ADDRESSES_FIELD_NUMBER: builtins.int - VERSION_FIELD_NUMBER: builtins.int - BLOCKHEIGHT_FIELD_NUMBER: builtins.int - NETWORK_FIELD_NUMBER: builtins.int - node_id: builtins.bytes - alias: builtins.str - color: builtins.bytes - num_peers: builtins.int - version: builtins.str - blockheight: builtins.int - network: builtins.str - @property - def addresses(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Address]: ... - def __init__( - self, - *, - node_id: builtins.bytes = ..., - alias: builtins.str = ..., - color: builtins.bytes = ..., - num_peers: builtins.int = ..., - addresses: collections.abc.Iterable[global___Address] | None = ..., - version: builtins.str = ..., - blockheight: builtins.int = ..., - network: builtins.str = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["addresses", b"addresses", "alias", b"alias", "blockheight", b"blockheight", "color", b"color", "network", b"network", "node_id", b"node_id", "num_peers", b"num_peers", "version", b"version"]) -> None: ... - -global___GetInfoResponse = GetInfoResponse - -@typing.final -class StopRequest(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - def __init__( - self, - ) -> None: ... - -global___StopRequest = StopRequest - -@typing.final -class StopResponse(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - def __init__( - self, - ) -> None: ... - -global___StopResponse = StopResponse - -@typing.final -class ConnectRequest(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - NODE_ID_FIELD_NUMBER: builtins.int - ADDR_FIELD_NUMBER: builtins.int - node_id: builtins.str - addr: builtins.str - def __init__( - self, - *, - node_id: builtins.str = ..., - addr: builtins.str = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["addr", b"addr", "node_id", b"node_id"]) -> None: ... - -global___ConnectRequest = ConnectRequest - -@typing.final -class ConnectResponse(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - NODE_ID_FIELD_NUMBER: builtins.int - FEATURES_FIELD_NUMBER: builtins.int - node_id: builtins.str - features: builtins.str - def __init__( - self, - *, - node_id: builtins.str = ..., - features: builtins.str = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["features", b"features", "node_id", b"node_id"]) -> None: ... - -global___ConnectResponse = ConnectResponse - -@typing.final -class ListPeersRequest(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - NODE_ID_FIELD_NUMBER: builtins.int - node_id: builtins.str - def __init__( - self, - *, - node_id: builtins.str = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["node_id", b"node_id"]) -> None: ... - -global___ListPeersRequest = ListPeersRequest - -@typing.final -class Htlc(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - DIRECTION_FIELD_NUMBER: builtins.int - ID_FIELD_NUMBER: builtins.int - AMOUNT_FIELD_NUMBER: builtins.int - EXPIRY_FIELD_NUMBER: builtins.int - PAYMENT_HASH_FIELD_NUMBER: builtins.int - STATE_FIELD_NUMBER: builtins.int - LOCAL_TRIMMED_FIELD_NUMBER: builtins.int - direction: builtins.str - id: builtins.int - amount: builtins.str - expiry: builtins.int - payment_hash: builtins.str - state: builtins.str - local_trimmed: builtins.bool - def __init__( - self, - *, - direction: builtins.str = ..., - id: builtins.int = ..., - amount: builtins.str = ..., - expiry: builtins.int = ..., - payment_hash: builtins.str = ..., - state: builtins.str = ..., - local_trimmed: builtins.bool = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["amount", b"amount", "direction", b"direction", "expiry", b"expiry", "id", b"id", "local_trimmed", b"local_trimmed", "payment_hash", b"payment_hash", "state", b"state"]) -> None: ... - -global___Htlc = Htlc - -@typing.final -class Aliases(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - LOCAL_FIELD_NUMBER: builtins.int - REMOTE_FIELD_NUMBER: builtins.int - local: builtins.str - remote: builtins.str - def __init__( - self, - *, - local: builtins.str = ..., - remote: builtins.str = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["local", b"local", "remote", b"remote"]) -> None: ... - -global___Aliases = Aliases - -@typing.final -class Channel(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - STATE_FIELD_NUMBER: builtins.int - OWNER_FIELD_NUMBER: builtins.int - ALIAS_FIELD_NUMBER: builtins.int - SHORT_CHANNEL_ID_FIELD_NUMBER: builtins.int - DIRECTION_FIELD_NUMBER: builtins.int - CHANNEL_ID_FIELD_NUMBER: builtins.int - FUNDING_TXID_FIELD_NUMBER: builtins.int - CLOSE_TO_ADDR_FIELD_NUMBER: builtins.int - CLOSE_TO_FIELD_NUMBER: builtins.int - PRIVATE_FIELD_NUMBER: builtins.int - TOTAL_FIELD_NUMBER: builtins.int - DUST_LIMIT_FIELD_NUMBER: builtins.int - SPENDABLE_FIELD_NUMBER: builtins.int - RECEIVABLE_FIELD_NUMBER: builtins.int - THEIR_TO_SELF_DELAY_FIELD_NUMBER: builtins.int - OUR_TO_SELF_DELAY_FIELD_NUMBER: builtins.int - STATUS_FIELD_NUMBER: builtins.int - HTLCS_FIELD_NUMBER: builtins.int - state: builtins.str - owner: builtins.str - short_channel_id: builtins.str - direction: builtins.int - channel_id: builtins.str - funding_txid: builtins.str - close_to_addr: builtins.str - close_to: builtins.str - private: builtins.bool - total: builtins.str - dust_limit: builtins.str - spendable: builtins.str - receivable: builtins.str - their_to_self_delay: builtins.int - our_to_self_delay: builtins.int - @property - def alias(self) -> global___Aliases: ... - @property - def status(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... - @property - def htlcs(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Htlc]: ... - def __init__( - self, - *, - state: builtins.str = ..., - owner: builtins.str = ..., - alias: global___Aliases | None = ..., - short_channel_id: builtins.str = ..., - direction: builtins.int = ..., - channel_id: builtins.str = ..., - funding_txid: builtins.str = ..., - close_to_addr: builtins.str = ..., - close_to: builtins.str = ..., - private: builtins.bool = ..., - total: builtins.str = ..., - dust_limit: builtins.str = ..., - spendable: builtins.str = ..., - receivable: builtins.str = ..., - their_to_self_delay: builtins.int = ..., - our_to_self_delay: builtins.int = ..., - status: collections.abc.Iterable[builtins.str] | None = ..., - htlcs: collections.abc.Iterable[global___Htlc] | None = ..., - ) -> None: ... - def HasField(self, field_name: typing.Literal["alias", b"alias"]) -> builtins.bool: ... - def ClearField(self, field_name: typing.Literal["alias", b"alias", "channel_id", b"channel_id", "close_to", b"close_to", "close_to_addr", b"close_to_addr", "direction", b"direction", "dust_limit", b"dust_limit", "funding_txid", b"funding_txid", "htlcs", b"htlcs", "our_to_self_delay", b"our_to_self_delay", "owner", b"owner", "private", b"private", "receivable", b"receivable", "short_channel_id", b"short_channel_id", "spendable", b"spendable", "state", b"state", "status", b"status", "their_to_self_delay", b"their_to_self_delay", "total", b"total"]) -> None: ... - -global___Channel = Channel - -@typing.final -class Peer(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - ID_FIELD_NUMBER: builtins.int - CONNECTED_FIELD_NUMBER: builtins.int - ADDRESSES_FIELD_NUMBER: builtins.int - FEATURES_FIELD_NUMBER: builtins.int - CHANNELS_FIELD_NUMBER: builtins.int - id: builtins.bytes - connected: builtins.bool - features: builtins.str - @property - def addresses(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Address]: ... - @property - def channels(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Channel]: ... - def __init__( - self, - *, - id: builtins.bytes = ..., - connected: builtins.bool = ..., - addresses: collections.abc.Iterable[global___Address] | None = ..., - features: builtins.str = ..., - channels: collections.abc.Iterable[global___Channel] | None = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["addresses", b"addresses", "channels", b"channels", "connected", b"connected", "features", b"features", "id", b"id"]) -> None: ... - -global___Peer = Peer - -@typing.final -class ListPeersResponse(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - PEERS_FIELD_NUMBER: builtins.int - @property - def peers(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Peer]: ... - def __init__( - self, - *, - peers: collections.abc.Iterable[global___Peer] | None = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["peers", b"peers"]) -> None: ... - -global___ListPeersResponse = ListPeersResponse - -@typing.final -class DisconnectRequest(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - NODE_ID_FIELD_NUMBER: builtins.int - FORCE_FIELD_NUMBER: builtins.int - node_id: builtins.str - force: builtins.bool - def __init__( - self, - *, - node_id: builtins.str = ..., - force: builtins.bool = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["force", b"force", "node_id", b"node_id"]) -> None: ... - -global___DisconnectRequest = DisconnectRequest - -@typing.final -class DisconnectResponse(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - def __init__( - self, - ) -> None: ... - -global___DisconnectResponse = DisconnectResponse - -@typing.final -class NewAddrRequest(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - ADDRESS_TYPE_FIELD_NUMBER: builtins.int - address_type: global___BtcAddressType.ValueType - def __init__( - self, - *, - address_type: global___BtcAddressType.ValueType = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["address_type", b"address_type"]) -> None: ... - -global___NewAddrRequest = NewAddrRequest - -@typing.final -class NewAddrResponse(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - ADDRESS_TYPE_FIELD_NUMBER: builtins.int - ADDRESS_FIELD_NUMBER: builtins.int - address_type: global___BtcAddressType.ValueType - address: builtins.str - def __init__( - self, - *, - address_type: global___BtcAddressType.ValueType = ..., - address: builtins.str = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["address", b"address", "address_type", b"address_type"]) -> None: ... - -global___NewAddrResponse = NewAddrResponse - -@typing.final -class ListFundsRequest(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - MINCONF_FIELD_NUMBER: builtins.int - @property - def minconf(self) -> global___Confirmation: ... - def __init__( - self, - *, - minconf: global___Confirmation | None = ..., - ) -> None: ... - def HasField(self, field_name: typing.Literal["minconf", b"minconf"]) -> builtins.bool: ... - def ClearField(self, field_name: typing.Literal["minconf", b"minconf"]) -> None: ... - -global___ListFundsRequest = ListFundsRequest - -@typing.final -class ListFundsOutput(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - OUTPUT_FIELD_NUMBER: builtins.int - AMOUNT_FIELD_NUMBER: builtins.int - ADDRESS_FIELD_NUMBER: builtins.int - STATUS_FIELD_NUMBER: builtins.int - RESERVED_FIELD_NUMBER: builtins.int - RESERVED_TO_BLOCK_FIELD_NUMBER: builtins.int - address: builtins.str - status: global___OutputStatus.ValueType - reserved: builtins.bool - reserved_to_block: builtins.int - @property - def output(self) -> global___Outpoint: ... - @property - def amount(self) -> global___Amount: ... - def __init__( - self, - *, - output: global___Outpoint | None = ..., - amount: global___Amount | None = ..., - address: builtins.str = ..., - status: global___OutputStatus.ValueType = ..., - reserved: builtins.bool = ..., - reserved_to_block: builtins.int = ..., - ) -> None: ... - def HasField(self, field_name: typing.Literal["amount", b"amount", "output", b"output"]) -> builtins.bool: ... - def ClearField(self, field_name: typing.Literal["address", b"address", "amount", b"amount", "output", b"output", "reserved", b"reserved", "reserved_to_block", b"reserved_to_block", "status", b"status"]) -> None: ... - -global___ListFundsOutput = ListFundsOutput - -@typing.final -class ListFundsChannel(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - PEER_ID_FIELD_NUMBER: builtins.int - CONNECTED_FIELD_NUMBER: builtins.int - SHORT_CHANNEL_ID_FIELD_NUMBER: builtins.int - OUR_AMOUNT_MSAT_FIELD_NUMBER: builtins.int - AMOUNT_MSAT_FIELD_NUMBER: builtins.int - FUNDING_TXID_FIELD_NUMBER: builtins.int - FUNDING_OUTPUT_FIELD_NUMBER: builtins.int - peer_id: builtins.bytes - connected: builtins.bool - short_channel_id: builtins.int - our_amount_msat: builtins.int - amount_msat: builtins.int - funding_txid: builtins.bytes - funding_output: builtins.int - def __init__( - self, - *, - peer_id: builtins.bytes = ..., - connected: builtins.bool = ..., - short_channel_id: builtins.int = ..., - our_amount_msat: builtins.int = ..., - amount_msat: builtins.int = ..., - funding_txid: builtins.bytes = ..., - funding_output: builtins.int = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["amount_msat", b"amount_msat", "connected", b"connected", "funding_output", b"funding_output", "funding_txid", b"funding_txid", "our_amount_msat", b"our_amount_msat", "peer_id", b"peer_id", "short_channel_id", b"short_channel_id"]) -> None: ... - -global___ListFundsChannel = ListFundsChannel - -@typing.final -class ListFundsResponse(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - OUTPUTS_FIELD_NUMBER: builtins.int - CHANNELS_FIELD_NUMBER: builtins.int - @property - def outputs(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___ListFundsOutput]: ... - @property - def channels(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___ListFundsChannel]: ... - def __init__( - self, - *, - outputs: collections.abc.Iterable[global___ListFundsOutput] | None = ..., - channels: collections.abc.Iterable[global___ListFundsChannel] | None = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["channels", b"channels", "outputs", b"outputs"]) -> None: ... - -global___ListFundsResponse = ListFundsResponse - -@typing.final -class Feerate(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - PRESET_FIELD_NUMBER: builtins.int - PERKW_FIELD_NUMBER: builtins.int - PERKB_FIELD_NUMBER: builtins.int - preset: global___FeeratePreset.ValueType - perkw: builtins.int - perkb: builtins.int - def __init__( - self, - *, - preset: global___FeeratePreset.ValueType = ..., - perkw: builtins.int = ..., - perkb: builtins.int = ..., - ) -> None: ... - def HasField(self, field_name: typing.Literal["perkb", b"perkb", "perkw", b"perkw", "preset", b"preset", "value", b"value"]) -> builtins.bool: ... - def ClearField(self, field_name: typing.Literal["perkb", b"perkb", "perkw", b"perkw", "preset", b"preset", "value", b"value"]) -> None: ... - def WhichOneof(self, oneof_group: typing.Literal["value", b"value"]) -> typing.Literal["preset", "perkw", "perkb"] | None: ... - -global___Feerate = Feerate - -@typing.final -class Confirmation(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - BLOCKS_FIELD_NUMBER: builtins.int - blocks: builtins.int - def __init__( - self, - *, - blocks: builtins.int = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["blocks", b"blocks"]) -> None: ... - -global___Confirmation = Confirmation - -@typing.final -class WithdrawRequest(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - DESTINATION_FIELD_NUMBER: builtins.int - AMOUNT_FIELD_NUMBER: builtins.int - FEERATE_FIELD_NUMBER: builtins.int - MINCONF_FIELD_NUMBER: builtins.int - UTXOS_FIELD_NUMBER: builtins.int - destination: builtins.str - @property - def amount(self) -> global___Amount: ... - @property - def feerate(self) -> global___Feerate: ... - @property - def minconf(self) -> global___Confirmation: ... - @property - def utxos(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Outpoint]: ... - def __init__( - self, - *, - destination: builtins.str = ..., - amount: global___Amount | None = ..., - feerate: global___Feerate | None = ..., - minconf: global___Confirmation | None = ..., - utxos: collections.abc.Iterable[global___Outpoint] | None = ..., - ) -> None: ... - def HasField(self, field_name: typing.Literal["amount", b"amount", "feerate", b"feerate", "minconf", b"minconf"]) -> builtins.bool: ... - def ClearField(self, field_name: typing.Literal["amount", b"amount", "destination", b"destination", "feerate", b"feerate", "minconf", b"minconf", "utxos", b"utxos"]) -> None: ... - -global___WithdrawRequest = WithdrawRequest - -@typing.final -class WithdrawResponse(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - TX_FIELD_NUMBER: builtins.int - TXID_FIELD_NUMBER: builtins.int - tx: builtins.bytes - txid: builtins.bytes - def __init__( - self, - *, - tx: builtins.bytes = ..., - txid: builtins.bytes = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["tx", b"tx", "txid", b"txid"]) -> None: ... - -global___WithdrawResponse = WithdrawResponse - -@typing.final -class FundChannelRequest(google.protobuf.message.Message): - """TODO: Extract AmountOrAll into its own message - TODO: Extract Feerate into its own message - """ - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - NODE_ID_FIELD_NUMBER: builtins.int - AMOUNT_FIELD_NUMBER: builtins.int - FEERATE_FIELD_NUMBER: builtins.int - ANNOUNCE_FIELD_NUMBER: builtins.int - MINCONF_FIELD_NUMBER: builtins.int - CLOSE_TO_FIELD_NUMBER: builtins.int - node_id: builtins.bytes - announce: builtins.bool - close_to: builtins.str - """TODO Maybe add UTXOS""" - @property - def amount(self) -> global___Amount: ... - @property - def feerate(self) -> global___Feerate: ... - @property - def minconf(self) -> global___Confirmation: ... - def __init__( - self, - *, - node_id: builtins.bytes = ..., - amount: global___Amount | None = ..., - feerate: global___Feerate | None = ..., - announce: builtins.bool = ..., - minconf: global___Confirmation | None = ..., - close_to: builtins.str = ..., - ) -> None: ... - def HasField(self, field_name: typing.Literal["amount", b"amount", "feerate", b"feerate", "minconf", b"minconf"]) -> builtins.bool: ... - def ClearField(self, field_name: typing.Literal["amount", b"amount", "announce", b"announce", "close_to", b"close_to", "feerate", b"feerate", "minconf", b"minconf", "node_id", b"node_id"]) -> None: ... - -global___FundChannelRequest = FundChannelRequest - -@typing.final -class Outpoint(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - TXID_FIELD_NUMBER: builtins.int - OUTNUM_FIELD_NUMBER: builtins.int - txid: builtins.bytes - outnum: builtins.int - def __init__( - self, - *, - txid: builtins.bytes = ..., - outnum: builtins.int = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["outnum", b"outnum", "txid", b"txid"]) -> None: ... - -global___Outpoint = Outpoint - -@typing.final -class FundChannelResponse(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - TX_FIELD_NUMBER: builtins.int - OUTPOINT_FIELD_NUMBER: builtins.int - CHANNEL_ID_FIELD_NUMBER: builtins.int - CLOSE_TO_FIELD_NUMBER: builtins.int - tx: builtins.bytes - channel_id: builtins.bytes - close_to: builtins.str - @property - def outpoint(self) -> global___Outpoint: ... - def __init__( - self, - *, - tx: builtins.bytes = ..., - outpoint: global___Outpoint | None = ..., - channel_id: builtins.bytes = ..., - close_to: builtins.str = ..., - ) -> None: ... - def HasField(self, field_name: typing.Literal["outpoint", b"outpoint"]) -> builtins.bool: ... - def ClearField(self, field_name: typing.Literal["channel_id", b"channel_id", "close_to", b"close_to", "outpoint", b"outpoint", "tx", b"tx"]) -> None: ... - -global___FundChannelResponse = FundChannelResponse - -@typing.final -class Timeout(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - SECONDS_FIELD_NUMBER: builtins.int - seconds: builtins.int - def __init__( - self, - *, - seconds: builtins.int = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["seconds", b"seconds"]) -> None: ... - -global___Timeout = Timeout - -@typing.final -class BitcoinAddress(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - ADDRESS_FIELD_NUMBER: builtins.int - address: builtins.str - def __init__( - self, - *, - address: builtins.str = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["address", b"address"]) -> None: ... - -global___BitcoinAddress = BitcoinAddress - -@typing.final -class CloseChannelRequest(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - NODE_ID_FIELD_NUMBER: builtins.int - UNILATERALTIMEOUT_FIELD_NUMBER: builtins.int - DESTINATION_FIELD_NUMBER: builtins.int - node_id: builtins.bytes - @property - def unilateraltimeout(self) -> global___Timeout: ... - @property - def destination(self) -> global___BitcoinAddress: ... - def __init__( - self, - *, - node_id: builtins.bytes = ..., - unilateraltimeout: global___Timeout | None = ..., - destination: global___BitcoinAddress | None = ..., - ) -> None: ... - def HasField(self, field_name: typing.Literal["destination", b"destination", "unilateraltimeout", b"unilateraltimeout"]) -> builtins.bool: ... - def ClearField(self, field_name: typing.Literal["destination", b"destination", "node_id", b"node_id", "unilateraltimeout", b"unilateraltimeout"]) -> None: ... - -global___CloseChannelRequest = CloseChannelRequest - -@typing.final -class CloseChannelResponse(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - CLOSE_TYPE_FIELD_NUMBER: builtins.int - TX_FIELD_NUMBER: builtins.int - TXID_FIELD_NUMBER: builtins.int - close_type: global___CloseChannelType.ValueType - tx: builtins.bytes - txid: builtins.bytes - def __init__( - self, - *, - close_type: global___CloseChannelType.ValueType = ..., - tx: builtins.bytes = ..., - txid: builtins.bytes = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["close_type", b"close_type", "tx", b"tx", "txid", b"txid"]) -> None: ... - -global___CloseChannelResponse = CloseChannelResponse - -@typing.final -class Amount(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - MILLISATOSHI_FIELD_NUMBER: builtins.int - SATOSHI_FIELD_NUMBER: builtins.int - BITCOIN_FIELD_NUMBER: builtins.int - ALL_FIELD_NUMBER: builtins.int - ANY_FIELD_NUMBER: builtins.int - millisatoshi: builtins.int - satoshi: builtins.int - bitcoin: builtins.int - all: builtins.bool - any: builtins.bool - def __init__( - self, - *, - millisatoshi: builtins.int = ..., - satoshi: builtins.int = ..., - bitcoin: builtins.int = ..., - all: builtins.bool = ..., - any: builtins.bool = ..., - ) -> None: ... - def HasField(self, field_name: typing.Literal["all", b"all", "any", b"any", "bitcoin", b"bitcoin", "millisatoshi", b"millisatoshi", "satoshi", b"satoshi", "unit", b"unit"]) -> builtins.bool: ... - def ClearField(self, field_name: typing.Literal["all", b"all", "any", b"any", "bitcoin", b"bitcoin", "millisatoshi", b"millisatoshi", "satoshi", b"satoshi", "unit", b"unit"]) -> None: ... - def WhichOneof(self, oneof_group: typing.Literal["unit", b"unit"]) -> typing.Literal["millisatoshi", "satoshi", "bitcoin", "all", "any"] | None: ... - -global___Amount = Amount - -@typing.final -class InvoiceRequest(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - AMOUNT_FIELD_NUMBER: builtins.int - LABEL_FIELD_NUMBER: builtins.int - DESCRIPTION_FIELD_NUMBER: builtins.int - PREIMAGE_FIELD_NUMBER: builtins.int - label: builtins.str - description: builtins.str - preimage: builtins.bytes - @property - def amount(self) -> global___Amount: ... - def __init__( - self, - *, - amount: global___Amount | None = ..., - label: builtins.str = ..., - description: builtins.str = ..., - preimage: builtins.bytes = ..., - ) -> None: ... - def HasField(self, field_name: typing.Literal["amount", b"amount"]) -> builtins.bool: ... - def ClearField(self, field_name: typing.Literal["amount", b"amount", "description", b"description", "label", b"label", "preimage", b"preimage"]) -> None: ... - -global___InvoiceRequest = InvoiceRequest - -@typing.final -class Invoice(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - LABEL_FIELD_NUMBER: builtins.int - DESCRIPTION_FIELD_NUMBER: builtins.int - AMOUNT_FIELD_NUMBER: builtins.int - RECEIVED_FIELD_NUMBER: builtins.int - STATUS_FIELD_NUMBER: builtins.int - PAYMENT_TIME_FIELD_NUMBER: builtins.int - EXPIRY_TIME_FIELD_NUMBER: builtins.int - BOLT11_FIELD_NUMBER: builtins.int - PAYMENT_HASH_FIELD_NUMBER: builtins.int - PAYMENT_PREIMAGE_FIELD_NUMBER: builtins.int - label: builtins.str - description: builtins.str - status: global___InvoiceStatus.ValueType - payment_time: builtins.int - expiry_time: builtins.int - bolt11: builtins.str - payment_hash: builtins.bytes - payment_preimage: builtins.bytes - @property - def amount(self) -> global___Amount: ... - @property - def received(self) -> global___Amount: ... - def __init__( - self, - *, - label: builtins.str = ..., - description: builtins.str = ..., - amount: global___Amount | None = ..., - received: global___Amount | None = ..., - status: global___InvoiceStatus.ValueType = ..., - payment_time: builtins.int = ..., - expiry_time: builtins.int = ..., - bolt11: builtins.str = ..., - payment_hash: builtins.bytes = ..., - payment_preimage: builtins.bytes = ..., - ) -> None: ... - def HasField(self, field_name: typing.Literal["amount", b"amount", "received", b"received"]) -> builtins.bool: ... - def ClearField(self, field_name: typing.Literal["amount", b"amount", "bolt11", b"bolt11", "description", b"description", "expiry_time", b"expiry_time", "label", b"label", "payment_hash", b"payment_hash", "payment_preimage", b"payment_preimage", "payment_time", b"payment_time", "received", b"received", "status", b"status"]) -> None: ... - -global___Invoice = Invoice - @typing.final -class PayRequest(google.protobuf.message.Message): +class HsmRequestContext(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - BOLT11_FIELD_NUMBER: builtins.int - AMOUNT_FIELD_NUMBER: builtins.int - TIMEOUT_FIELD_NUMBER: builtins.int - MAXFEEPERCENT_FIELD_NUMBER: builtins.int - MAXFEE_FIELD_NUMBER: builtins.int - bolt11: builtins.str - timeout: builtins.int - """Non-zero number of seconds before we should stop retrying - the payment and return an error. - """ - maxfeepercent: builtins.float - @property - def amount(self) -> global___Amount: - """Only needed when the invoice does not specify an amount.""" - - @property - def maxfee(self) -> global___Amount: ... + NODE_ID_FIELD_NUMBER: builtins.int + DBID_FIELD_NUMBER: builtins.int + CAPABILITIES_FIELD_NUMBER: builtins.int + node_id: builtins.bytes + dbid: builtins.int + capabilities: builtins.int def __init__( self, *, - bolt11: builtins.str = ..., - amount: global___Amount | None = ..., - timeout: builtins.int = ..., - maxfeepercent: builtins.float = ..., - maxfee: global___Amount | None = ..., + node_id: builtins.bytes = ..., + dbid: builtins.int = ..., + capabilities: builtins.int = ..., ) -> None: ... - def HasField(self, field_name: typing.Literal["amount", b"amount", "maxfee", b"maxfee"]) -> builtins.bool: ... - def ClearField(self, field_name: typing.Literal["amount", b"amount", "bolt11", b"bolt11", "maxfee", b"maxfee", "maxfeepercent", b"maxfeepercent", "timeout", b"timeout"]) -> None: ... + def ClearField(self, field_name: typing.Literal["capabilities", b"capabilities", "dbid", b"dbid", "node_id", b"node_id"]) -> None: ... -global___PayRequest = PayRequest +global___HsmRequestContext = HsmRequestContext @typing.final -class Payment(google.protobuf.message.Message): +class HsmResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - DESTINATION_FIELD_NUMBER: builtins.int - PAYMENT_HASH_FIELD_NUMBER: builtins.int - PAYMENT_PREIMAGE_FIELD_NUMBER: builtins.int - STATUS_FIELD_NUMBER: builtins.int - AMOUNT_FIELD_NUMBER: builtins.int - AMOUNT_SENT_FIELD_NUMBER: builtins.int - BOLT11_FIELD_NUMBER: builtins.int - CREATED_AT_FIELD_NUMBER: builtins.int - COMPLETED_AT_FIELD_NUMBER: builtins.int - destination: builtins.bytes - payment_hash: builtins.bytes - payment_preimage: builtins.bytes - status: global___PayStatus.ValueType - bolt11: builtins.str - created_at: builtins.float - """UTC Unix timestamp of the time the invoice was created.""" - completed_at: builtins.int - """UTC Unix timestamp of the time the payment was completed - (successfully or failed). 0 if not completed yet. + REQUEST_ID_FIELD_NUMBER: builtins.int + RAW_FIELD_NUMBER: builtins.int + SIGNER_STATE_FIELD_NUMBER: builtins.int + ERROR_FIELD_NUMBER: builtins.int + request_id: builtins.int + raw: builtins.bytes + error: builtins.str + """If the signer reported an error, and did therefore not include + `raw`, this is the stringified error, so we can print it in the + logs. This should help us collate policy errors with the changes + proposed by CLN """ @property - def amount(self) -> global___Amount: ... - @property - def amount_sent(self) -> global___Amount: ... - def __init__( - self, - *, - destination: builtins.bytes = ..., - payment_hash: builtins.bytes = ..., - payment_preimage: builtins.bytes = ..., - status: global___PayStatus.ValueType = ..., - amount: global___Amount | None = ..., - amount_sent: global___Amount | None = ..., - bolt11: builtins.str = ..., - created_at: builtins.float = ..., - completed_at: builtins.int = ..., - ) -> None: ... - def HasField(self, field_name: typing.Literal["amount", b"amount", "amount_sent", b"amount_sent"]) -> builtins.bool: ... - def ClearField(self, field_name: typing.Literal["amount", b"amount", "amount_sent", b"amount_sent", "bolt11", b"bolt11", "completed_at", b"completed_at", "created_at", b"created_at", "destination", b"destination", "payment_hash", b"payment_hash", "payment_preimage", b"payment_preimage", "status", b"status"]) -> None: ... - -global___Payment = Payment - -@typing.final -class PaymentIdentifier(google.protobuf.message.Message): - """A payment identifier is a way to reference a unique payment, either - by its bolt11 string or just the payment hash. Only one of the two - may be provided at once, since having multiple ones may conflict - with each other. - """ - - DESCRIPTOR: google.protobuf.descriptor.Descriptor + def signer_state(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___SignerStateEntry]: + """A list of updated key-value-version tuples that is to be + merged into the state tracked by the plugin. + """ - BOLT11_FIELD_NUMBER: builtins.int - PAYMENT_HASH_FIELD_NUMBER: builtins.int - bolt11: builtins.str - payment_hash: builtins.bytes def __init__( self, *, - bolt11: builtins.str = ..., - payment_hash: builtins.bytes = ..., + request_id: builtins.int = ..., + raw: builtins.bytes = ..., + signer_state: collections.abc.Iterable[global___SignerStateEntry] | None = ..., + error: builtins.str = ..., ) -> None: ... - def HasField(self, field_name: typing.Literal["bolt11", b"bolt11", "id", b"id", "payment_hash", b"payment_hash"]) -> builtins.bool: ... - def ClearField(self, field_name: typing.Literal["bolt11", b"bolt11", "id", b"id", "payment_hash", b"payment_hash"]) -> None: ... - def WhichOneof(self, oneof_group: typing.Literal["id", b"id"]) -> typing.Literal["bolt11", "payment_hash"] | None: ... + def ClearField(self, field_name: typing.Literal["error", b"error", "raw", b"raw", "request_id", b"request_id", "signer_state", b"signer_state"]) -> None: ... -global___PaymentIdentifier = PaymentIdentifier +global___HsmResponse = HsmResponse @typing.final -class ListPaymentsRequest(google.protobuf.message.Message): - """Request a list of payments that this node has performed. Optionally - the query can be narrowed to a single payment by providing an - identifier. - """ - +class HsmRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - IDENTIFIER_FIELD_NUMBER: builtins.int + REQUEST_ID_FIELD_NUMBER: builtins.int + CONTEXT_FIELD_NUMBER: builtins.int + RAW_FIELD_NUMBER: builtins.int + SIGNER_STATE_FIELD_NUMBER: builtins.int + REQUESTS_FIELD_NUMBER: builtins.int + request_id: builtins.int + raw: builtins.bytes @property - def identifier(self) -> global___PaymentIdentifier: ... - def __init__( - self, - *, - identifier: global___PaymentIdentifier | None = ..., - ) -> None: ... - def HasField(self, field_name: typing.Literal["identifier", b"identifier"]) -> builtins.bool: ... - def ClearField(self, field_name: typing.Literal["identifier", b"identifier"]) -> None: ... - -global___ListPaymentsRequest = ListPaymentsRequest - -@typing.final -class ListPaymentsResponse(google.protobuf.message.Message): - """The response matching `ListPaymentRequest`. It returns a list of - PayResponses, i.e., the same format as `Pay` returned its result. - """ - - DESCRIPTOR: google.protobuf.descriptor.Descriptor + def context(self) -> global___HsmRequestContext: ... + @property + def signer_state(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___SignerStateEntry]: + """A list of key-value-version tuples that the signer should + use to update its internal state. + """ - PAYMENTS_FIELD_NUMBER: builtins.int @property - def payments(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Payment]: ... + def requests(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___PendingRequest]: + """Currently active requests that are used to justify changes + in state. + """ + def __init__( self, *, - payments: collections.abc.Iterable[global___Payment] | None = ..., + request_id: builtins.int = ..., + context: global___HsmRequestContext | None = ..., + raw: builtins.bytes = ..., + signer_state: collections.abc.Iterable[global___SignerStateEntry] | None = ..., + requests: collections.abc.Iterable[global___PendingRequest] | None = ..., ) -> None: ... - def ClearField(self, field_name: typing.Literal["payments", b"payments"]) -> None: ... + def HasField(self, field_name: typing.Literal["context", b"context"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["context", b"context", "raw", b"raw", "request_id", b"request_id", "requests", b"requests", "signer_state", b"signer_state"]) -> None: ... -global___ListPaymentsResponse = ListPaymentsResponse +global___HsmRequest = HsmRequest @typing.final -class InvoiceIdentifier(google.protobuf.message.Message): +class Empty(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - LABEL_FIELD_NUMBER: builtins.int - INVSTRING_FIELD_NUMBER: builtins.int - PAYMENT_HASH_FIELD_NUMBER: builtins.int - label: builtins.str - invstring: builtins.str - payment_hash: builtins.bytes def __init__( self, - *, - label: builtins.str = ..., - invstring: builtins.str = ..., - payment_hash: builtins.bytes = ..., ) -> None: ... - def HasField(self, field_name: typing.Literal["id", b"id", "invstring", b"invstring", "label", b"label", "payment_hash", b"payment_hash"]) -> builtins.bool: ... - def ClearField(self, field_name: typing.Literal["id", b"id", "invstring", b"invstring", "label", b"label", "payment_hash", b"payment_hash"]) -> None: ... - def WhichOneof(self, oneof_group: typing.Literal["id", b"id"]) -> typing.Literal["label", "invstring", "payment_hash"] | None: ... -global___InvoiceIdentifier = InvoiceIdentifier +global___Empty = Empty @typing.final -class ListInvoicesRequest(google.protobuf.message.Message): +class Amount(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - IDENTIFIER_FIELD_NUMBER: builtins.int - @property - def identifier(self) -> global___InvoiceIdentifier: ... + MILLISATOSHI_FIELD_NUMBER: builtins.int + SATOSHI_FIELD_NUMBER: builtins.int + BITCOIN_FIELD_NUMBER: builtins.int + ALL_FIELD_NUMBER: builtins.int + ANY_FIELD_NUMBER: builtins.int + millisatoshi: builtins.int + satoshi: builtins.int + bitcoin: builtins.int + all: builtins.bool + any: builtins.bool def __init__( self, *, - identifier: global___InvoiceIdentifier | None = ..., + millisatoshi: builtins.int = ..., + satoshi: builtins.int = ..., + bitcoin: builtins.int = ..., + all: builtins.bool = ..., + any: builtins.bool = ..., ) -> None: ... - def HasField(self, field_name: typing.Literal["identifier", b"identifier"]) -> builtins.bool: ... - def ClearField(self, field_name: typing.Literal["identifier", b"identifier"]) -> None: ... + def HasField(self, field_name: typing.Literal["all", b"all", "any", b"any", "bitcoin", b"bitcoin", "millisatoshi", b"millisatoshi", "satoshi", b"satoshi", "unit", b"unit"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["all", b"all", "any", b"any", "bitcoin", b"bitcoin", "millisatoshi", b"millisatoshi", "satoshi", b"satoshi", "unit", b"unit"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["unit", b"unit"]) -> typing.Literal["millisatoshi", "satoshi", "bitcoin", "all", "any"] | None: ... -global___ListInvoicesRequest = ListInvoicesRequest +global___Amount = Amount @typing.final class StreamIncomingFilter(google.protobuf.message.Message): @@ -1259,22 +164,6 @@ class StreamIncomingFilter(google.protobuf.message.Message): global___StreamIncomingFilter = StreamIncomingFilter -@typing.final -class ListInvoicesResponse(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - INVOICES_FIELD_NUMBER: builtins.int - @property - def invoices(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Invoice]: ... - def __init__( - self, - *, - invoices: collections.abc.Iterable[global___Invoice] | None = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["invoices", b"invoices"]) -> None: ... - -global___ListInvoicesResponse = ListInvoicesResponse - @typing.final class TlvField(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1347,82 +236,6 @@ class IncomingPayment(google.protobuf.message.Message): global___IncomingPayment = IncomingPayment -@typing.final -class RoutehintHop(google.protobuf.message.Message): - """A single hop in a Routehint""" - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - NODE_ID_FIELD_NUMBER: builtins.int - SHORT_CHANNEL_ID_FIELD_NUMBER: builtins.int - FEE_BASE_FIELD_NUMBER: builtins.int - FEE_PROP_FIELD_NUMBER: builtins.int - CLTV_EXPIRY_DELTA_FIELD_NUMBER: builtins.int - node_id: builtins.bytes - short_channel_id: builtins.str - fee_base: builtins.int - fee_prop: builtins.int - cltv_expiry_delta: builtins.int - def __init__( - self, - *, - node_id: builtins.bytes = ..., - short_channel_id: builtins.str = ..., - fee_base: builtins.int = ..., - fee_prop: builtins.int = ..., - cltv_expiry_delta: builtins.int = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["cltv_expiry_delta", b"cltv_expiry_delta", "fee_base", b"fee_base", "fee_prop", b"fee_prop", "node_id", b"node_id", "short_channel_id", b"short_channel_id"]) -> None: ... - -global___RoutehintHop = RoutehintHop - -@typing.final -class Routehint(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - HOPS_FIELD_NUMBER: builtins.int - @property - def hops(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___RoutehintHop]: ... - def __init__( - self, - *, - hops: collections.abc.Iterable[global___RoutehintHop] | None = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["hops", b"hops"]) -> None: ... - -global___Routehint = Routehint - -@typing.final -class KeysendRequest(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - NODE_ID_FIELD_NUMBER: builtins.int - AMOUNT_FIELD_NUMBER: builtins.int - LABEL_FIELD_NUMBER: builtins.int - ROUTEHINTS_FIELD_NUMBER: builtins.int - EXTRATLVS_FIELD_NUMBER: builtins.int - node_id: builtins.bytes - label: builtins.str - @property - def amount(self) -> global___Amount: ... - @property - def routehints(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Routehint]: ... - @property - def extratlvs(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___TlvField]: ... - def __init__( - self, - *, - node_id: builtins.bytes = ..., - amount: global___Amount | None = ..., - label: builtins.str = ..., - routehints: collections.abc.Iterable[global___Routehint] | None = ..., - extratlvs: collections.abc.Iterable[global___TlvField] | None = ..., - ) -> None: ... - def HasField(self, field_name: typing.Literal["amount", b"amount"]) -> builtins.bool: ... - def ClearField(self, field_name: typing.Literal["amount", b"amount", "extratlvs", b"extratlvs", "label", b"label", "node_id", b"node_id", "routehints", b"routehints"]) -> None: ... - -global___KeysendRequest = KeysendRequest - @typing.final class StreamLogRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1686,3 +499,64 @@ class TrampolinePayResponse(google.protobuf.message.Message): def ClearField(self, field_name: typing.Literal["amount_msat", b"amount_msat", "amount_sent_msat", b"amount_sent_msat", "created_at", b"created_at", "destination", b"destination", "parts", b"parts", "payment_hash", b"payment_hash", "payment_preimage", b"payment_preimage"]) -> None: ... global___TrampolinePayResponse = TrampolinePayResponse + +@typing.final +class LspInvoiceRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + LSP_ID_FIELD_NUMBER: builtins.int + TOKEN_FIELD_NUMBER: builtins.int + AMOUNT_MSAT_FIELD_NUMBER: builtins.int + DESCRIPTION_FIELD_NUMBER: builtins.int + LABEL_FIELD_NUMBER: builtins.int + lsp_id: builtins.str + """len=0 => None, let the server decide.""" + token: builtins.str + """Optional: for discounts/API keys + len=0 => None + """ + amount_msat: builtins.int + """Pass-through of cln invoice rpc params + 0 => Any + """ + description: builtins.str + label: builtins.str + def __init__( + self, + *, + lsp_id: builtins.str = ..., + token: builtins.str = ..., + amount_msat: builtins.int = ..., + description: builtins.str = ..., + label: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["amount_msat", b"amount_msat", "description", b"description", "label", b"label", "lsp_id", b"lsp_id", "token", b"token"]) -> None: ... + +global___LspInvoiceRequest = LspInvoiceRequest + +@typing.final +class LspInvoiceResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + BOLT11_FIELD_NUMBER: builtins.int + CREATED_INDEX_FIELD_NUMBER: builtins.int + EXPIRES_AT_FIELD_NUMBER: builtins.int + PAYMENT_HASH_FIELD_NUMBER: builtins.int + PAYMENT_SECRET_FIELD_NUMBER: builtins.int + bolt11: builtins.str + created_index: builtins.int + expires_at: builtins.int + payment_hash: builtins.bytes + payment_secret: builtins.bytes + def __init__( + self, + *, + bolt11: builtins.str = ..., + created_index: builtins.int = ..., + expires_at: builtins.int = ..., + payment_hash: builtins.bytes = ..., + payment_secret: builtins.bytes = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["bolt11", b"bolt11", "created_index", b"created_index", "expires_at", b"expires_at", "payment_hash", b"payment_hash", "payment_secret", b"payment_secret"]) -> None: ... + +global___LspInvoiceResponse = LspInvoiceResponse diff --git a/libs/gl-client-py/glclient/greenlight_pb2_grpc.py b/libs/gl-client-py/glclient/greenlight_pb2_grpc.py index 320a2da4a..7800c6861 100644 --- a/libs/gl-client-py/glclient/greenlight_pb2_grpc.py +++ b/libs/gl-client-py/glclient/greenlight_pb2_grpc.py @@ -5,10 +5,8 @@ from glclient import greenlight_pb2 as glclient_dot_greenlight__pb2 -GRPC_GENERATED_VERSION = '1.64.1' +GRPC_GENERATED_VERSION = '1.76.0' GRPC_VERSION = grpc.__version__ -EXPECTED_ERROR_RELEASE = '1.65.0' -SCHEDULED_RELEASE_DATE = 'June 25, 2024' _version_not_supported = False try: @@ -18,15 +16,12 @@ _version_not_supported = True if _version_not_supported: - warnings.warn( + raise RuntimeError( f'The grpc package installed is at version {GRPC_VERSION},' - + f' but the generated code in glclient/greenlight_pb2_grpc.py depends on' + + ' but the generated code in glclient/greenlight_pb2_grpc.py depends on' + f' grpcio>={GRPC_GENERATED_VERSION}.' + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' - + f' This warning will become an error in {EXPECTED_ERROR_RELEASE},' - + f' scheduled for release on {SCHEDULED_RELEASE_DATE}.', - RuntimeWarning ) @@ -44,7 +39,8 @@ class NodeStub(object): Deprecated methods are being replaced by the standardized and automatically managed cln-grpc protocol you can find in - `node.proto` + `node.proto`. This interface consists mostly of + Greenlight-specific, and backported functionality. """ def __init__(self, channel): @@ -53,6 +49,11 @@ def __init__(self, channel): Args: channel: A grpc.Channel. """ + self.LspInvoice = channel.unary_unary( + '/greenlight.Node/LspInvoice', + request_serializer=glclient_dot_greenlight__pb2.LspInvoiceRequest.SerializeToString, + response_deserializer=glclient_dot_greenlight__pb2.LspInvoiceResponse.FromString, + _registered_method=True) self.StreamIncoming = channel.unary_stream( '/greenlight.Node/StreamIncoming', request_serializer=glclient_dot_greenlight__pb2.StreamIncomingFilter.SerializeToString, @@ -104,9 +105,18 @@ class NodeServicer(object): Deprecated methods are being replaced by the standardized and automatically managed cln-grpc protocol you can find in - `node.proto` + `node.proto`. This interface consists mostly of + Greenlight-specific, and backported functionality. """ + def LspInvoice(self, request, context): + """Create an invoice to request an incoming payment. Includes LSP + negotiation to open a channel on-demand when needed. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + def StreamIncoming(self, request, context): """Stream incoming payments @@ -178,6 +188,11 @@ def TrampolinePay(self, request, context): def add_NodeServicer_to_server(servicer, server): rpc_method_handlers = { + 'LspInvoice': grpc.unary_unary_rpc_method_handler( + servicer.LspInvoice, + request_deserializer=glclient_dot_greenlight__pb2.LspInvoiceRequest.FromString, + response_serializer=glclient_dot_greenlight__pb2.LspInvoiceResponse.SerializeToString, + ), 'StreamIncoming': grpc.unary_stream_rpc_method_handler( servicer.StreamIncoming, request_deserializer=glclient_dot_greenlight__pb2.StreamIncomingFilter.FromString, @@ -235,9 +250,37 @@ class Node(object): Deprecated methods are being replaced by the standardized and automatically managed cln-grpc protocol you can find in - `node.proto` + `node.proto`. This interface consists mostly of + Greenlight-specific, and backported functionality. """ + @staticmethod + def LspInvoice(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/greenlight.Node/LspInvoice', + glclient_dot_greenlight__pb2.LspInvoiceRequest.SerializeToString, + glclient_dot_greenlight__pb2.LspInvoiceResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + @staticmethod def StreamIncoming(request, target, diff --git a/libs/gl-client-py/glclient/greenlight_pb2_grpc.pyi b/libs/gl-client-py/glclient/greenlight_pb2_grpc.pyi index e9601dd64..7b9dcab23 100644 --- a/libs/gl-client-py/glclient/greenlight_pb2_grpc.pyi +++ b/libs/gl-client-py/glclient/greenlight_pb2_grpc.pyi @@ -31,10 +31,19 @@ class NodeStub: Deprecated methods are being replaced by the standardized and automatically managed cln-grpc protocol you can find in - `node.proto` + `node.proto`. This interface consists mostly of + Greenlight-specific, and backported functionality. """ def __init__(self, channel: typing.Union[grpc.Channel, grpc.aio.Channel]) -> None: ... + LspInvoice: grpc.UnaryUnaryMultiCallable[ + glclient.greenlight_pb2.LspInvoiceRequest, + glclient.greenlight_pb2.LspInvoiceResponse, + ] + """Create an invoice to request an incoming payment. Includes LSP + negotiation to open a channel on-demand when needed. + """ + StreamIncoming: grpc.UnaryStreamMultiCallable[ glclient.greenlight_pb2.StreamIncomingFilter, glclient.greenlight_pb2.IncomingPayment, @@ -114,7 +123,16 @@ class NodeAsyncStub: Deprecated methods are being replaced by the standardized and automatically managed cln-grpc protocol you can find in - `node.proto` + `node.proto`. This interface consists mostly of + Greenlight-specific, and backported functionality. + """ + + LspInvoice: grpc.aio.UnaryUnaryMultiCallable[ + glclient.greenlight_pb2.LspInvoiceRequest, + glclient.greenlight_pb2.LspInvoiceResponse, + ] + """Create an invoice to request an incoming payment. Includes LSP + negotiation to open a channel on-demand when needed. """ StreamIncoming: grpc.aio.UnaryStreamMultiCallable[ @@ -196,9 +214,20 @@ class NodeServicer(metaclass=abc.ABCMeta): Deprecated methods are being replaced by the standardized and automatically managed cln-grpc protocol you can find in - `node.proto` + `node.proto`. This interface consists mostly of + Greenlight-specific, and backported functionality. """ + @abc.abstractmethod + def LspInvoice( + self, + request: glclient.greenlight_pb2.LspInvoiceRequest, + context: _ServicerContext, + ) -> typing.Union[glclient.greenlight_pb2.LspInvoiceResponse, collections.abc.Awaitable[glclient.greenlight_pb2.LspInvoiceResponse]]: + """Create an invoice to request an incoming payment. Includes LSP + negotiation to open a channel on-demand when needed. + """ + @abc.abstractmethod def StreamIncoming( self, diff --git a/libs/gl-client-py/glclient/scheduler_pb2.py b/libs/gl-client-py/glclient/scheduler_pb2.py index 1b05456f6..0acdb1461 100644 --- a/libs/gl-client-py/glclient/scheduler_pb2.py +++ b/libs/gl-client-py/glclient/scheduler_pb2.py @@ -1,12 +1,22 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: glclient/scheduler.proto -# Protobuf Python Version: 5.26.1 +# Protobuf Python Version: 6.31.1 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'glclient/scheduler.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() diff --git a/libs/gl-client-py/glclient/scheduler_pb2_grpc.py b/libs/gl-client-py/glclient/scheduler_pb2_grpc.py index 7f57a4540..7f7d122f8 100644 --- a/libs/gl-client-py/glclient/scheduler_pb2_grpc.py +++ b/libs/gl-client-py/glclient/scheduler_pb2_grpc.py @@ -6,10 +6,8 @@ from glclient import greenlight_pb2 as glclient_dot_greenlight__pb2 from glclient import scheduler_pb2 as glclient_dot_scheduler__pb2 -GRPC_GENERATED_VERSION = '1.64.1' +GRPC_GENERATED_VERSION = '1.76.0' GRPC_VERSION = grpc.__version__ -EXPECTED_ERROR_RELEASE = '1.65.0' -SCHEDULED_RELEASE_DATE = 'June 25, 2024' _version_not_supported = False try: @@ -19,15 +17,12 @@ _version_not_supported = True if _version_not_supported: - warnings.warn( + raise RuntimeError( f'The grpc package installed is at version {GRPC_VERSION},' - + f' but the generated code in glclient/scheduler_pb2_grpc.py depends on' + + ' but the generated code in glclient/scheduler_pb2_grpc.py depends on' + f' grpcio>={GRPC_GENERATED_VERSION}.' + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' - + f' This warning will become an error in {EXPECTED_ERROR_RELEASE},' - + f' scheduled for release on {SCHEDULED_RELEASE_DATE}.', - RuntimeWarning ) diff --git a/libs/gl-client-py/src/node.rs b/libs/gl-client-py/src/node.rs index ade59d805..992a3a491 100644 --- a/libs/gl-client-py/src/node.rs +++ b/libs/gl-client-py/src/node.rs @@ -94,9 +94,9 @@ impl Node { fn lsps_invoice( &self, - amount_msat: Option, - description: String, label: String, + description: String, + amount_msat: Option, token: Option, ) -> PyResult> { let req = pb::LspInvoiceRequest { diff --git a/libs/gl-plugin/.resources/proto/glclient/greenlight.proto b/libs/gl-plugin/.resources/proto/glclient/greenlight.proto index 1c306e30f..63dd182c0 100644 --- a/libs/gl-plugin/.resources/proto/glclient/greenlight.proto +++ b/libs/gl-plugin/.resources/proto/glclient/greenlight.proto @@ -19,7 +19,7 @@ package greenlight; service Node { // Create an invoice to request an incoming payment. Includes LSP // negotiation to open a channel on-demand when needed. - rpc Invoice(InvoiceRequest) returns (InvoiceResponse) {} + rpc LspInvoice(LspInvoiceRequest) returns (LspInvoiceResponse) {} // Stream incoming payments // @@ -227,8 +227,8 @@ message TrampolinePayResponse { bytes destination = 7; } -message InvoiceRequest { - string lsp_id = 1; // len=0 => None +message LspInvoiceRequest { + string lsp_id = 1; // len=0 => None, let the server decide. // Optional: for discounts/API keys string token = 2; // len=0 => None // Pass-through of cln invoice rpc params @@ -236,7 +236,7 @@ message InvoiceRequest { string description = 4; string label = 5; } -message InvoiceResponse { +message LspInvoiceResponse { string bolt11 = 1; uint32 created_index = 2; uint32 expires_at = 3; diff --git a/libs/gl-plugin/src/node/mod.rs b/libs/gl-plugin/src/node/mod.rs index ea513a4cb..6e2c07ea3 100644 --- a/libs/gl-plugin/src/node/mod.rs +++ b/libs/gl-plugin/src/node/mod.rs @@ -181,11 +181,11 @@ impl Node for PluginNodeServer { type StreamHsmRequestsStream = ReceiverStream>; type StreamLogStream = ReceiverStream>; - async fn invoice( + async fn lsp_invoice( &self, - req: Request, - ) -> Result, Status> { - let req: pb::InvoiceRequest = req.into_inner(); + req: Request, + ) -> Result, Status> { + let req: pb::LspInvoiceRequest = req.into_inner(); let invreq: crate::requests::InvoiceRequest = req.into(); let rpc_arc = get_rpc(&self.rpc_path).await; diff --git a/libs/gl-plugin/src/node/wrapper.rs b/libs/gl-plugin/src/node/wrapper.rs index 84ef4cfa5..e13a542a4 100644 --- a/libs/gl-plugin/src/node/wrapper.rs +++ b/libs/gl-plugin/src/node/wrapper.rs @@ -1192,7 +1192,7 @@ impl WrappedNodeServer { use crate::pb::{ node_server::Node as GlNode, Custommsg, Empty, HsmRequest, HsmResponse, IncomingPayment, - InvoiceRequest, InvoiceResponse, LogEntry, StreamCustommsgRequest, StreamIncomingFilter, + LogEntry, LspInvoiceRequest, LspInvoiceResponse, StreamCustommsgRequest, StreamIncomingFilter, StreamLogRequest, }; @@ -1203,11 +1203,11 @@ impl GlNode for WrappedNodeServer { type StreamLogStream = ReceiverStream>; type StreamIncomingStream = ReceiverStream>; - async fn invoice( + async fn lsp_invoice( &self, - req: Request, - ) -> Result, Status> { - self.node_server.invoice(req).await + req: Request, + ) -> Result, Status> { + self.node_server.lsp_invoice(req).await } async fn stream_incoming( diff --git a/libs/gl-plugin/src/requests.rs b/libs/gl-plugin/src/requests.rs index 3d3f54d44..d2d6f69e3 100644 --- a/libs/gl-plugin/src/requests.rs +++ b/libs/gl-plugin/src/requests.rs @@ -220,8 +220,8 @@ pub struct InvoiceRequest { use cln_rpc::model::TypedRequest; -impl From for InvoiceRequest { - fn from(o: crate::pb::InvoiceRequest) -> InvoiceRequest { +impl From for InvoiceRequest { + fn from(o: crate::pb::LspInvoiceRequest) -> InvoiceRequest { InvoiceRequest { lsp_id: o.lsp_id, token: match o.token.as_ref() { @@ -254,14 +254,14 @@ mod test { #[test] fn test_invoice_response() { let tests = vec![ - (crate::pb::InvoiceRequest { + (crate::pb::LspInvoiceRequest { lsp_id: "lsp_id".to_owned(), token: "".to_owned(), amount_msat: 0, description: "description".to_owned(), label: "label".to_owned(), }), - crate::pb::InvoiceRequest { + crate::pb::LspInvoiceRequest { lsp_id: "lsp_id".to_owned(), token: "token".to_owned(), amount_msat: 1337, diff --git a/libs/gl-plugin/src/responses.rs b/libs/gl-plugin/src/responses.rs index edde5054b..491bee261 100644 --- a/libs/gl-plugin/src/responses.rs +++ b/libs/gl-plugin/src/responses.rs @@ -343,9 +343,9 @@ pub struct InvoiceResponse { pub payment_secret: String, } -impl From for crate::pb::InvoiceResponse { - fn from(o: InvoiceResponse) -> crate::pb::InvoiceResponse { - crate::pb::InvoiceResponse { +impl From for crate::pb::LspInvoiceResponse { + fn from(o: InvoiceResponse) -> crate::pb::LspInvoiceResponse { + crate::pb::LspInvoiceResponse { bolt11: o.bolt11, created_index: o.created_index, expires_at: o.expires_at, @@ -399,7 +399,7 @@ mod test { }]; for t in tests { - let _actual: crate::pb::InvoiceResponse = t.into(); + let _actual: crate::pb::LspInvoiceResponse = t.into(); } } } From a7ec2a973749ea15abf0f4b1c5dcc53d96f63793 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 14 Nov 2025 12:24:18 +0100 Subject: [PATCH 14/17] test(py): Make the test use the grpc interface exposed by py --- libs/gl-client-py/tests/test_plugin.py | 30 +++++++++++--------------- libs/gl-testing/gltesting/node.py | 3 ++- 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/libs/gl-client-py/tests/test_plugin.py b/libs/gl-client-py/tests/test_plugin.py index e423b2d25..85341013c 100644 --- a/libs/gl-client-py/tests/test_plugin.py +++ b/libs/gl-client-py/tests/test_plugin.py @@ -205,9 +205,7 @@ def test_trampoline_multi_htlc(bitcoind, clients, node_factory): assert res.parts == 2 -def test_lsps_plugin_calls( - clients, bitcoind, node_factory, lsps_server -): +def test_lsps_plugin_calls(clients, bitcoind, node_factory, lsps_server): """Test that we can call the `lsps-jitchannel` method from a variety of places. @@ -223,22 +221,18 @@ def test_lsps_plugin_calls( # Get the GL node c = clients.new() c.register() - s = c1.node() - s.connect(lsps_server) - - res = s.localrpc.lsps_lsps2_invoice( - lsp_id=lsp_id, - token=None, - amount_msat="1337msat", - description="description", - label="lbl1", - ) + s1 = c.signer() + s1.run_in_thread() + s = c.node() + s.connect_peer(lsps_server.info["id"], f"localhost:{lsps_server.port}") - inv = s.localrpc.decodepay(res['bolt11']) + # Try the pyo3 bindings from gl-client-py + res = s.lsp_invoice(label="lbl2", description="description", amount_msat=42) + inv = s.decodepay(res["bolt11"]) pprint(inv) # Only one routehint, with only one hop, the LSP to the destination - assert len(inv['routes']) == 1 and len(inv['routes'][0]) == 1 - assert inv['description'] == 'description' - rh = inv['routes'][0][0] - assert rh['pubkey'] == lsp_id + assert len(inv["routes"]) == 1 and len(inv["routes"][0]) == 1 + assert inv["description"] == "description" + rh = inv["routes"][0][0] + assert rh["pubkey"] == lsp_id diff --git a/libs/gl-testing/gltesting/node.py b/libs/gl-testing/gltesting/node.py index c4963d9f8..3dcca6ece 100644 --- a/libs/gl-testing/gltesting/node.py +++ b/libs/gl-testing/gltesting/node.py @@ -98,6 +98,7 @@ def __init__( '--allow-deprecated-apis=true', #'--experimental-anchors', '--developer', # TODO Make this multi-version capable + '--experimental-lsps-client', ] def write_node_config(self, network: str): @@ -129,7 +130,7 @@ def start(self): self.grpc_uri = f"https://localhost:{self.grpc_port}" self.env.update({ "GL_CERT_PATH": self.directory / "certs", - "PATH": f"{self.version.lightningd}:{libexec_path}:{path}", + "PATH": f"{self.version.lightningd.parent}:{libexec_path}:{path}", "CLN_VERSION": self.version.name, "GL_NODE_NETWORK": self.network, "GL_NODE_ID": self.node_id.hex(), From 0532066db3ed498cb0eb726e676bb44e99948e36 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 14 Nov 2025 14:36:03 +0100 Subject: [PATCH 15/17] chore(py): Updated dependency constraints --- libs/gl-client-py/.kacl.yml | 52 ++++++++++++++++++++++++++++++++ libs/gl-client-py/CHANGELOG.md | 7 +++++ libs/gl-client-py/pyproject.toml | 5 +-- 3 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 libs/gl-client-py/.kacl.yml create mode 100644 libs/gl-client-py/CHANGELOG.md diff --git a/libs/gl-client-py/.kacl.yml b/libs/gl-client-py/.kacl.yml new file mode 100644 index 000000000..6ce4f7a36 --- /dev/null +++ b/libs/gl-client-py/.kacl.yml @@ -0,0 +1,52 @@ +kacl: + file: CHANGELOG.md + allowed_header_titles: + - Changelog + - Change Log + allowed_version_sections: + - Added + - Changed + - Deprecated + - Removed + - Fixed + - Security + default_content: + - All notable changes to this project will be documented in this file. + - The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + release: + add_unreleased: True + git: + commit: False + commit_message: "[skip ci] Releasing Changelog version {new_version}" + commit_additional_files: [] + tag: False + tag_name: "v{new_version}" + tag_description: "Version v{new_version} released" + links: + auto_generate: False + compare_versions_template: '{host}/compare/{previous_version}...{version}' + unreleased_changes_template: '{host}/compare/{latest_version}...master' + initial_version_template: '{host}/tree/{version}' + extension: + post_release_version_prefix: null + issue_tracker: + jira: + host: null + username: null + password: null + issue_patterns: ["[A-Z]+-[0-9]+"] + comment_template: | + # 🚀 New version [v{new_version}]({link}) + + A new release has been created referencing this issue. Please check it out. + + ## 🚧 Changes in this version + + {changes} + + ## 🧭 Reference + + Code: [Source Code Management System]({link}) + stash: + directory: .kacl_stash + always: False diff --git a/libs/gl-client-py/CHANGELOG.md b/libs/gl-client-py/CHANGELOG.md new file mode 100644 index 000000000..245034002 --- /dev/null +++ b/libs/gl-client-py/CHANGELOG.md @@ -0,0 +1,7 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## Unreleased diff --git a/libs/gl-client-py/pyproject.toml b/libs/gl-client-py/pyproject.toml index 3addceb22..38211de61 100644 --- a/libs/gl-client-py/pyproject.toml +++ b/libs/gl-client-py/pyproject.toml @@ -5,7 +5,7 @@ description = "" readme = "README.md" requires-python = ">=3.8,<4" dependencies = [ - "protobuf>=4", + "protobuf>=6", "pyln-grpc-proto>=0.1", ] authors = [ @@ -27,8 +27,9 @@ dev = [ "mypy>=1.14.1", "mypy-protobuf>=3.6.0", "patchelf>=0.17.2.2", - "protobuf>=4", + "protobuf>=6", "pyln-grpc-proto>=0.1.2", + "python-kacl>=0.6.7", "types-protobuf>=5.29.1.20241207", ] From 46aa6dcee066af5f9fb3627471795f4dd25af862 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 17 Nov 2025 14:18:44 +0100 Subject: [PATCH 16/17] plugin: Implement LSP selection for `lsp_invoice` --- libs/gl-client-py/tests/test_plugin.py | 13 +- libs/gl-client/src/signer/model/greenlight.rs | 4 + libs/gl-client/src/signer/model/mod.rs | 1 + libs/gl-plugin/src/node/mod.rs | 122 ++++++++++++++++-- libs/gl-plugin/src/requests.rs | 27 +++- libs/gl-plugin/src/responses.rs | 18 +++ 6 files changed, 163 insertions(+), 22 deletions(-) diff --git a/libs/gl-client-py/tests/test_plugin.py b/libs/gl-client-py/tests/test_plugin.py index 85341013c..e699c5e48 100644 --- a/libs/gl-client-py/tests/test_plugin.py +++ b/libs/gl-client-py/tests/test_plugin.py @@ -227,12 +227,11 @@ def test_lsps_plugin_calls(clients, bitcoind, node_factory, lsps_server): s.connect_peer(lsps_server.info["id"], f"localhost:{lsps_server.port}") # Try the pyo3 bindings from gl-client-py - res = s.lsp_invoice(label="lbl2", description="description", amount_msat=42) - inv = s.decodepay(res["bolt11"]) + res = s.lsp_invoice(label="lbl2", description="description", amount_msat=31337) + from pyln.proto import Invoice + inv = Invoice.decode(res.bolt11) pprint(inv) - # Only one routehint, with only one hop, the LSP to the destination - assert len(inv["routes"]) == 1 and len(inv["routes"][0]) == 1 - assert inv["description"] == "description" - rh = inv["routes"][0][0] - assert rh["pubkey"] == lsp_id + assert len(inv.route_hints.route_hints) == 1 + rh = inv.route_hints.route_hints[0] + assert rh["pubkey"] == bytes.fromhex(lsp_id) diff --git a/libs/gl-client/src/signer/model/greenlight.rs b/libs/gl-client/src/signer/model/greenlight.rs index d4f39c073..8e27ef256 100644 --- a/libs/gl-client/src/signer/model/greenlight.rs +++ b/libs/gl-client/src/signer/model/greenlight.rs @@ -12,6 +12,10 @@ pub fn decode_request(uri: &str, p: &[u8]) -> anyhow::Result { "/greenlight.Node/TrampolinePay" => { Request::TrampolinePay(crate::pb::TrampolinePayRequest::decode(p)?) } + "/greenlight.Node/LspInvoice" => { + Request::LspInvoice(crate::pb::LspInvoiceRequest::decode(p)?) + } + uri => return Err(anyhow!("Unknown URI {}, can't decode payload", uri)), }) } diff --git a/libs/gl-client/src/signer/model/mod.rs b/libs/gl-client/src/signer/model/mod.rs index 9dcffb62e..8a8b47b41 100644 --- a/libs/gl-client/src/signer/model/mod.rs +++ b/libs/gl-client/src/signer/model/mod.rs @@ -9,6 +9,7 @@ pub mod greenlight; #[derive(Clone, Debug)] pub enum Request { GlConfig(greenlight::GlConfig), + LspInvoice(greenlight::LspInvoiceRequest), Getinfo(cln::GetinfoRequest), ListPeers(cln::ListpeersRequest), ListFunds(cln::ListfundsRequest), diff --git a/libs/gl-plugin/src/node/mod.rs b/libs/gl-plugin/src/node/mod.rs index 6e2c07ea3..9547f38a6 100644 --- a/libs/gl-plugin/src/node/mod.rs +++ b/libs/gl-plugin/src/node/mod.rs @@ -34,6 +34,8 @@ static LIMITER: OnceCell> = static RPC_CLIENT: OnceCell>> = OnceCell::const_new(); static RPC_POLL_INTERVAL: Duration = Duration::from_millis(500); +const OPT_SUPPORTS_LSPS: usize = 729; + pub async fn get_rpc>(path: P) -> Arc> { RPC_CLIENT .get_or_init(|| async { @@ -186,15 +188,31 @@ impl Node for PluginNodeServer { req: Request, ) -> Result, Status> { let req: pb::LspInvoiceRequest = req.into_inner(); - let invreq: crate::requests::InvoiceRequest = req.into(); + let mut invreq: crate::requests::LspInvoiceRequest = req.into(); let rpc_arc = get_rpc(&self.rpc_path).await; let mut rpc = rpc_arc.lock().await; + + // In case the client did not specify an LSP to work with, + // let's enumerate them, and select the best option ourselves. + let lsps = self.get_lsps_offers(&mut rpc).await.map_err(|_e| { + Status::not_found("Could not retrieve LSPS peers for invoice negotiation.") + })?; + + if lsps.len() < 1 { + return Err(Status::not_found( + "Could not find an LSP peer to negotiate the LSPS2 channel for this invoice.", + )); + } + + let lsp = &lsps[0]; + log::info!("Selecting {:?} for invoice negotiation", lsp); + invreq.lsp_id = lsp.node_id.to_owned(); + let res = rpc .call_typed(&invreq) .await .map_err(|e| Status::new(Code::Internal, e.to_string()))?; - Ok(Response::new(res.into())) } @@ -595,6 +613,12 @@ impl Node for PluginNodeServer { use cln_grpc::pb::node_server::NodeServer; +#[derive(Clone, Debug)] +struct Lsps2Offer { + node_id: String, + params: Vec, +} + impl PluginNodeServer { pub async fn run(self) -> Result<()> { let addr = self.grpc_binding.parse().unwrap(); @@ -655,17 +679,99 @@ impl PluginNodeServer { return Ok(()); } + async fn list_peers( + &self, + rpc: &mut cln_rpc::ClnRpc, + ) -> Result { + rpc.call_typed(&cln_rpc::model::requests::ListpeersRequest { + id: None, + level: None, + }) + .await + .map_err(|e| e.into()) + } + + async fn get_lsps_offers(&self, rpc: &mut cln_rpc::ClnRpc) -> Result, Error> { + // Collect peers offering LSP functionality + let lpeers = self.list_peers(rpc).await?; + + // Filter out the ones that do not announce the LSPs features. + // TODO: Re-enable the filtering once the cln-lsps-service plugin announces the features. + let _lsps: Vec = lpeers + .peers + .into_iter() + //.filter(|p| has_feature( + // hex::decode(p.features.clone().unwrap_or_default()).expect("featurebits are hex"), + // OPT_SUPPORTS_LSPS + //)) + .collect(); + + // Query all peers for their LSPS offers, but with a brief + // timeout so the invoice creation isn't help up too long. + let futs: Vec< + tokio::task::JoinHandle<( + String, + Result< + Result, + tokio::time::error::Elapsed, + >, + )>, + > = _lsps + .into_iter() + .map(|peer| { + let rpc_path = self.rpc_path.clone(); + tokio::spawn(async move { + let peer_id = format!("{:x}", peer.id); + let mut rpc = cln_rpc::ClnRpc::new(rpc_path.clone()).await.unwrap(); + let req = crate::requests::LspGetinfoRequest { + lsp_id: peer_id.clone(), + token: None, + }; + + ( + peer_id, + tokio::time::timeout( + tokio::time::Duration::from_secs(2), + rpc.call_typed(&req), + ) + .await, + ) + }) + }) + .collect(); + + let mut res = vec![]; + for f in futs { + match f.await { + //TODO We need to drag the node_id along. + Ok((node_id, Ok(Ok(r)))) => res.push(Lsps2Offer { + node_id: node_id, + params: r.opening_fee_params_menu, + }), + Ok((node_id, Err(e))) => warn!( + "Error fetching LSPS menu items from peer_id={}: {:?}", + node_id, e + ), + Ok((node_id, Ok(Err(e)))) => warn!( + "Error fetching LSPS menu items from peer_id={}: {:?}", + node_id, e + ), + Err(_) => warn!("Timeout fetching LSPS menu items"), + } + } + + log::info!("Gathered {} LSP menus", res.len()); + log::trace!("LSP menus: {:?}", &res); + + Ok(res) + } + async fn get_reconnect_peers( &self, ) -> Result, Error> { let rpc_arc = get_rpc(&self.rpc_path).await; let mut rpc = rpc_arc.lock().await; - let peers = rpc - .call_typed(&cln_rpc::model::requests::ListpeersRequest { - id: None, - level: None, - }) - .await?; + let peers = self.list_peers(&mut rpc).await?; let mut requests: Vec = peers .peers diff --git a/libs/gl-plugin/src/requests.rs b/libs/gl-plugin/src/requests.rs index d2d6f69e3..caed0e6e6 100644 --- a/libs/gl-plugin/src/requests.rs +++ b/libs/gl-plugin/src/requests.rs @@ -209,7 +209,7 @@ pub struct Keysend { pub struct ListIncoming {} #[derive(Debug, Clone, Serialize)] -pub struct InvoiceRequest { +pub struct LspInvoiceRequest { pub lsp_id: String, #[serde(skip_serializing_if = "Option::is_none")] pub token: Option, @@ -218,11 +218,18 @@ pub struct InvoiceRequest { pub label: String, } +#[derive(Debug, Clone, Serialize)] +pub struct LspGetinfoRequest { + pub lsp_id: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub token: Option, +} + use cln_rpc::model::TypedRequest; -impl From for InvoiceRequest { - fn from(o: crate::pb::LspInvoiceRequest) -> InvoiceRequest { - InvoiceRequest { +impl From for LspInvoiceRequest { + fn from(o: crate::pb::LspInvoiceRequest) -> LspInvoiceRequest { + LspInvoiceRequest { lsp_id: o.lsp_id, token: match o.token.as_ref() { "" => None, @@ -240,14 +247,20 @@ impl From for InvoiceRequest { } } -impl TypedRequest for InvoiceRequest { +impl TypedRequest for LspInvoiceRequest { type Response = super::responses::InvoiceResponse; - fn method(&self) -> &str { "lsps-lsps2-invoice" } } +impl TypedRequest for LspGetinfoRequest { + type Response = super::responses::LspGetinfoResponse; + fn method(&self) -> &str { + "lsps-lsps2-getinfo" + } +} + #[cfg(test)] mod test { use super::*; @@ -271,7 +284,7 @@ mod test { ]; for t in tests { - let _actual: super::InvoiceRequest = t.into(); + let _actual: super::LspInvoiceRequest = t.into(); } } } diff --git a/libs/gl-plugin/src/responses.rs b/libs/gl-plugin/src/responses.rs index 491bee261..65cc63f5f 100644 --- a/libs/gl-plugin/src/responses.rs +++ b/libs/gl-plugin/src/responses.rs @@ -343,6 +343,24 @@ pub struct InvoiceResponse { pub payment_secret: String, } +#[derive(Debug, Clone, Deserialize)] +pub struct LspGetinfoResponse { +pub opening_fee_params_menu: Vec, + +} +#[derive(Debug, Clone, Deserialize)] +#[serde(deny_unknown_fields)] // LSPS2 requires the client to fail if a field is unrecognized. +pub struct OpeningFeeParams { + pub min_fee_msat: String, + pub proportional: u64, + pub valid_until: String, + pub min_lifetime: u32, + pub max_client_to_self_delay: u32, + pub min_payment_size_msat: String , + pub max_payment_size_msat: String , + pub promise: String, // Max 512 bytes +} + impl From for crate::pb::LspInvoiceResponse { fn from(o: InvoiceResponse) -> crate::pb::LspInvoiceResponse { crate::pb::LspInvoiceResponse { From 65cb0bcc187ca5a521812469fa83dbfdf7396ee5 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 17 Nov 2025 14:33:09 +0100 Subject: [PATCH 17/17] fixup! plugin: Implement LSP selection for `lsp_invoice` --- libs/gl-client-py/tests/test_plugin.py | 2 +- libs/gl-client/src/signer/resolve.rs | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/libs/gl-client-py/tests/test_plugin.py b/libs/gl-client-py/tests/test_plugin.py index e699c5e48..b99cb7fa6 100644 --- a/libs/gl-client-py/tests/test_plugin.py +++ b/libs/gl-client-py/tests/test_plugin.py @@ -234,4 +234,4 @@ def test_lsps_plugin_calls(clients, bitcoind, node_factory, lsps_server): # Only one routehint, with only one hop, the LSP to the destination assert len(inv.route_hints.route_hints) == 1 rh = inv.route_hints.route_hints[0] - assert rh["pubkey"] == bytes.fromhex(lsp_id) + assert rh.pubkey == bytes.fromhex(lsp_id) diff --git a/libs/gl-client/src/signer/resolve.rs b/libs/gl-client/src/signer/resolve.rs index 97f8ca980..6b94d29e0 100644 --- a/libs/gl-client/src/signer/resolve.rs +++ b/libs/gl-client/src/signer/resolve.rs @@ -81,6 +81,11 @@ impl Resolver { // TODO: Add `close_to` to allowlist for the close // later on } + (Message::SignInvoice(_l), Request::LspInvoice(_r)) => { + // TODO: This could also need some + // strengthening. See below. + true + } (Message::SignInvoice(_l), Request::Invoice(_r)) => { // TODO: This could be strengthened by parsing the // invoice from `l.u5bytes` and verify the