From b1ef1fc7348776045e3aa16a70ba004f72d0ae47 Mon Sep 17 00:00:00 2001 From: benthecarman Date: Mon, 23 Mar 2026 19:21:39 -0500 Subject: [PATCH 1/4] Replace protobuf with JSON everywhere MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Protobuf added complexity without much benefit for our use case — the binary encoding is opaque, hard to debug with standard HTTP tools, and requires proto toolchain maintenance. JSON is human-readable, widely supported, and sufficient for our throughput needs. This removes prost and all .proto files entirely, renaming the ldk-server-protos crate to ldk-server-json-models. Types are rewritten as hand-written Rust structs and enums with serde derives rather than prost-generated code. Fixed-size byte fields (hashes, channel IDs, public keys) use [u8; 32] and [u8; 33] with hex serde instead of String, giving type safety at the model layer. Several proto-era patterns are cleaned up: wrapper structs that only existed because protobuf wraps oneof in a message are removed, fields that were Option only because proto message fields are nullable are made required where the server always provides them, and the EventEnvelope wrapper is dropped in favor of using Event directly. Storage namespaces are changed from ("payments", "") to ("ldk-server", "payments") so existing protobuf-encoded data is silently ignored rather than failing to deserialize, avoiding the need for migration code or manual database wipes. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/check-protos.yml | 32 - CLAUDE.md | 2 +- CONTRIBUTING.md | 13 +- Cargo.lock | 18 +- Cargo.toml | 2 +- README.md | 4 +- e2e-tests/Cargo.lock | 16 +- e2e-tests/Cargo.toml | 3 +- e2e-tests/build.rs | 4 +- e2e-tests/src/lib.rs | 27 +- e2e-tests/tests/e2e.rs | 187 ++- ldk-server-cli/Cargo.toml | 2 +- ldk-server-cli/src/main.rs | 129 +- ldk-server-cli/src/types.rs | 2 +- ldk-server-client/Cargo.toml | 9 +- ldk-server-client/src/client.rs | 53 +- ldk-server-client/src/error.rs | 12 +- ldk-server-client/src/lib.rs | 2 +- ldk-server-json-models/Cargo.toml | 7 + .../src/api.rs | 751 ++++------- .../src/endpoints.rs | 0 ldk-server-json-models/src/error.rs | 42 + ldk-server-json-models/src/events.rs | 59 + .../src/lib.rs | 1 - ldk-server-json-models/src/serde_utils.rs | 143 +++ ldk-server-json-models/src/types.rs | 756 +++++++++++ ldk-server-protos/Cargo.toml | 25 - ldk-server-protos/build.rs | 95 -- ldk-server-protos/src/error.rs | 79 -- ldk-server-protos/src/events.rs | 87 -- ldk-server-protos/src/proto/api.proto | 824 ------------ ldk-server-protos/src/proto/error.proto | 45 - ldk-server-protos/src/proto/events.proto | 44 - ldk-server-protos/src/proto/types.proto | 820 ------------ ldk-server-protos/src/serde_utils.rs | 58 - ldk-server-protos/src/types.rs | 1143 ----------------- ldk-server/Cargo.toml | 5 +- ldk-server/src/api/bolt11_claim_for_hash.rs | 20 +- ldk-server/src/api/bolt11_fail_for_hash.rs | 12 +- ldk-server/src/api/bolt11_receive.rs | 11 +- ldk-server/src/api/bolt11_receive_for_hash.rs | 16 +- .../src/api/bolt11_receive_via_jit_channel.rs | 8 +- ldk-server/src/api/bolt11_send.rs | 8 +- ldk-server/src/api/bolt12_receive.rs | 5 +- ldk-server/src/api/bolt12_send.rs | 8 +- ldk-server/src/api/close_channel.rs | 8 +- ldk-server/src/api/connect_peer.rs | 4 +- ldk-server/src/api/disconnect_peer.rs | 6 +- ldk-server/src/api/error.rs | 8 +- .../src/api/export_pathfinding_scores.rs | 6 +- ldk-server/src/api/get_balances.rs | 8 +- ldk-server/src/api/get_node_info.rs | 13 +- ldk-server/src/api/get_payment_details.rs | 18 +- ldk-server/src/api/graph_get_channel.rs | 6 +- ldk-server/src/api/graph_get_node.rs | 15 +- ldk-server/src/api/graph_list_channels.rs | 2 +- ldk-server/src/api/graph_list_nodes.rs | 2 +- ldk-server/src/api/list_channels.rs | 6 +- ldk-server/src/api/list_forwarded_payments.rs | 10 +- ldk-server/src/api/list_payments.rs | 8 +- ldk-server/src/api/list_peers.rs | 6 +- ldk-server/src/api/mod.rs | 25 +- ldk-server/src/api/onchain_receive.rs | 2 +- ldk-server/src/api/onchain_send.rs | 5 +- ldk-server/src/api/open_channel.rs | 8 +- ldk-server/src/api/sign_message.rs | 2 +- ldk-server/src/api/splice_channel.rs | 6 +- ldk-server/src/api/spontaneous_send.rs | 12 +- ldk-server/src/api/unified_send.rs | 15 +- ldk-server/src/api/update_channel_config.rs | 21 +- ldk-server/src/api/verify_signature.rs | 6 +- ldk-server/src/io/events/event_publisher.rs | 19 +- ldk-server/src/io/events/mod.rs | 14 +- ldk-server/src/io/events/rabbitmq/mod.rs | 31 +- ldk-server/src/io/persist/mod.rs | 8 +- ldk-server/src/main.rs | 51 +- ldk-server/src/service.rs | 32 +- .../src/util/{proto_adapter.rs => adapter.rs} | 433 +++---- ldk-server/src/util/mod.rs | 2 +- 79 files changed, 1935 insertions(+), 4472 deletions(-) delete mode 100644 .github/workflows/check-protos.yml create mode 100644 ldk-server-json-models/Cargo.toml rename {ldk-server-protos => ldk-server-json-models}/src/api.rs (52%) rename {ldk-server-protos => ldk-server-json-models}/src/endpoints.rs (100%) create mode 100644 ldk-server-json-models/src/error.rs create mode 100644 ldk-server-json-models/src/events.rs rename {ldk-server-protos => ldk-server-json-models}/src/lib.rs (95%) create mode 100644 ldk-server-json-models/src/serde_utils.rs create mode 100644 ldk-server-json-models/src/types.rs delete mode 100644 ldk-server-protos/Cargo.toml delete mode 100644 ldk-server-protos/build.rs delete mode 100644 ldk-server-protos/src/error.rs delete mode 100644 ldk-server-protos/src/events.rs delete mode 100644 ldk-server-protos/src/proto/api.proto delete mode 100644 ldk-server-protos/src/proto/error.proto delete mode 100644 ldk-server-protos/src/proto/events.proto delete mode 100644 ldk-server-protos/src/proto/types.proto delete mode 100644 ldk-server-protos/src/serde_utils.rs delete mode 100644 ldk-server-protos/src/types.rs rename ldk-server/src/util/{proto_adapter.rs => adapter.rs} (50%) diff --git a/.github/workflows/check-protos.yml b/.github/workflows/check-protos.yml deleted file mode 100644 index 629bc0bb..00000000 --- a/.github/workflows/check-protos.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Check Generated Protos - -on: - push: - paths: - - 'ldk-server-protos/**' - pull_request: - paths: - - 'ldk-server-protos/**' - workflow_dispatch: - -jobs: - check-protos: - runs-on: ubuntu-latest - steps: - - name: Checkout source code - uses: actions/checkout@v3 - - name: Install Rust stable toolchain - run: | - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile=minimal --default-toolchain stable - - name: Install protoc - run: sudo apt-get install -y protobuf-compiler - - name: Generate protos - run: RUSTFLAGS="--cfg genproto" cargo build -p ldk-server-protos - - name: Format generated code - run: rustup component add rustfmt && cargo fmt --all - - name: Check for differences - run: | - if ! git diff --exit-code; then - echo "error: Generated protobuf files are out of date. Run: RUSTFLAGS=\"--cfg genproto\" cargo build -p ldk-server-protos && cargo fmt --all" - exit 1 - fi diff --git a/CLAUDE.md b/CLAUDE.md index aa1da645..f4bd6434 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -9,7 +9,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for build commands, testing, code style, - **ldk-server** - Main daemon server (entry point: `src/main.rs`) - **ldk-server-cli** - CLI client using clap - **ldk-server-client** - Reqwest-based client library -- **ldk-server-protos** - Protocol buffer definitions and generated Rust code +- **ldk-server-json-models** - Shared JSON request/response types with serde ## Development Rules diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9a783dfb..76fae4fd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -37,18 +37,11 @@ cargo clippy --all-features -- -D warnings -A clippy::drop_non_drop # Lint (CI - Hard tabs, max width 100 chars - Imports grouped: std, external crates, local crates -## Protocol Buffer Generation - -```bash -RUSTFLAGS="--cfg genproto" cargo build -p ldk-server-protos -cargo fmt --all -``` - ## Adding a New API Endpoint -1. Define request/response messages in `ldk-server-protos/src/proto/api.proto` -2. Regenerate protos (see above) -3. Create handler in `ldk-server/src/api/` (follow existing patterns) +1. Define request/response types in `ldk-server-json-models/src/api.rs` +2. Create handler in `ldk-server/src/api/` (follow existing patterns) +3. Add endpoint constant in `ldk-server-json-models/src/endpoints.rs` 4. Add route in `ldk-server/src/service.rs` 5. Add CLI command in `ldk-server-cli/src/main.rs` diff --git a/Cargo.lock b/Cargo.lock index 99a74a7d..b3fe9283 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -573,9 +573,6 @@ name = "bytes" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" -dependencies = [ - "serde", -] [[package]] name = "cbc" @@ -1753,7 +1750,6 @@ version = "0.1.0" dependencies = [ "async-trait", "base64 0.21.7", - "bytes", "chrono", "clap", "futures-util", @@ -1764,12 +1760,12 @@ dependencies = [ "hyper-util", "lapin", "ldk-node", - "ldk-server-protos", + "ldk-server-json-models", "log", - "prost", "ring", "rusqlite", "serde", + "serde_json", "tokio", "tokio-rustls 0.26.4", "toml", @@ -1794,18 +1790,16 @@ name = "ldk-server-client" version = "0.1.0" dependencies = [ "bitcoin_hashes 0.14.0", - "ldk-server-protos", - "prost", + "ldk-server-json-models", "reqwest 0.11.27", + "serde", + "serde_json", ] [[package]] -name = "ldk-server-protos" +name = "ldk-server-json-models" version = "0.1.0" dependencies = [ - "bytes", - "prost", - "prost-build", "serde", ] diff --git a/Cargo.toml b/Cargo.toml index 2a8c8b9b..aaa6637f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] resolver = "2" -members = ["ldk-server-cli", "ldk-server-client", "ldk-server-protos", "ldk-server"] +members = ["ldk-server-cli", "ldk-server-client", "ldk-server-json-models", "ldk-server"] exclude = ["e2e-tests"] [profile.release] diff --git a/README.md b/README.md index f14d2881..04ddfc34 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ The primary goal of LDK Server is to provide an efficient, stable, and API-first solution for deploying and managing a Lightning Network node. With its streamlined setup, LDK Server enables users to easily set up, configure, and run -a Lightning node while exposing a robust, language-agnostic API via [Protocol Buffers (Protobuf)](https://protobuf.dev/). +a Lightning node while exposing a robust, language-agnostic REST API with JSON. ### Features @@ -15,7 +15,7 @@ a Lightning node while exposing a robust, language-agnostic API via [Protocol Bu - Deploy a Lightning Network node with minimal configuration, no coding required. - **API-First Design**: - - Exposes a well-defined API using Protobuf, allowing seamless integration with HTTP-clients or applications. + - Exposes a well-defined JSON REST API, allowing seamless integration with HTTP-clients or applications. - **Powered by LDK**: - Built on top of LDK-Node, leveraging the modular, reliable, and high-performance architecture of LDK. diff --git a/e2e-tests/Cargo.lock b/e2e-tests/Cargo.lock index 13326fd6..11b6c65e 100644 --- a/e2e-tests/Cargo.lock +++ b/e2e-tests/Cargo.lock @@ -548,9 +548,6 @@ name = "bytes" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" -dependencies = [ - "serde", -] [[package]] name = "bzip2" @@ -874,8 +871,7 @@ dependencies = [ "lapin", "ldk-node", "ldk-server-client", - "ldk-server-protos", - "prost", + "ldk-server-json-models", "serde_json", "tempfile", "tokio", @@ -1834,18 +1830,16 @@ name = "ldk-server-client" version = "0.1.0" dependencies = [ "bitcoin_hashes", - "ldk-server-protos", - "prost", + "ldk-server-json-models", "reqwest 0.11.27", + "serde", + "serde_json", ] [[package]] -name = "ldk-server-protos" +name = "ldk-server-json-models" version = "0.1.0" dependencies = [ - "bytes", - "prost", - "prost-build", "serde", ] diff --git a/e2e-tests/Cargo.toml b/e2e-tests/Cargo.toml index 5576b7d2..e0f42f61 100644 --- a/e2e-tests/Cargo.toml +++ b/e2e-tests/Cargo.toml @@ -8,10 +8,9 @@ corepc-node = { version = "0.10", features = ["download", "29_0"] } tempfile = "3" tokio = { version = "1.38.0", features = ["rt-multi-thread", "macros", "time"] } ldk-server-client = { path = "../ldk-server-client" } -ldk-server-protos = { path = "../ldk-server-protos", features = ["serde"] } +ldk-server-json-models = { path = "../ldk-server-json-models" } serde_json = "1.0" hex-conservative = { version = "0.2", features = ["std"] } lapin = { version = "2.4.0", features = ["rustls"], default-features = false } -prost = { version = "0.11.6", default-features = false, features = ["std"] } futures-util = "0.3" ldk-node = { git = "https://github.com/lightningdevkit/ldk-node", rev = "d1bbf978c8b7abe87ae2e40793556c1fe4e7ea49" } diff --git a/e2e-tests/build.rs b/e2e-tests/build.rs index 6701b3d0..0741cbe8 100644 --- a/e2e-tests/build.rs +++ b/e2e-tests/build.rs @@ -45,6 +45,6 @@ fn main() { println!("cargo:rerun-if-changed=../ldk-server/Cargo.toml"); println!("cargo:rerun-if-changed=../ldk-server-cli/src"); println!("cargo:rerun-if-changed=../ldk-server-cli/Cargo.toml"); - println!("cargo:rerun-if-changed=../ldk-server-protos/src"); - println!("cargo:rerun-if-changed=../ldk-server-protos/Cargo.toml"); + println!("cargo:rerun-if-changed=../ldk-server-json-models/src"); + println!("cargo:rerun-if-changed=../ldk-server-json-models/Cargo.toml"); } diff --git a/e2e-tests/src/lib.rs b/e2e-tests/src/lib.rs index 083c08e7..41f4c49f 100644 --- a/e2e-tests/src/lib.rs +++ b/e2e-tests/src/lib.rs @@ -16,9 +16,9 @@ use std::time::Duration; use corepc_node::Node; use hex_conservative::DisplayHex; use ldk_server_client::client::LdkServerClient; -use ldk_server_client::ldk_server_protos::api::{GetNodeInfoRequest, GetNodeInfoResponse}; -use ldk_server_protos::api::{ - GetBalancesRequest, ListChannelsRequest, OnchainReceiveRequest, OpenChannelRequest, +use ldk_server_json_models::api::{ + GetBalancesRequest, GetNodeInfoRequest, GetNodeInfoResponse, ListChannelsRequest, + OnchainReceiveRequest, OpenChannelRequest, }; /// Wrapper around a managed bitcoind process for regtest. @@ -92,7 +92,7 @@ pub struct LdkServerHandle { pub storage_dir: PathBuf, pub api_key: String, pub tls_cert_path: PathBuf, - pub node_id: String, + pub node_id: [u8; 33], pub exchange_name: String, client: LdkServerClient, } @@ -201,7 +201,7 @@ client_trusts_lsp = true storage_dir, api_key, tls_cert_path, - node_id: String::new(), + node_id: [0u8; 33], exchange_name, client, }; @@ -217,7 +217,7 @@ client_trusts_lsp = true &self.client } - pub fn node_id(&self) -> &str { + pub fn node_id(&self) -> &[u8; 33] { &self.node_id } @@ -325,16 +325,14 @@ pub async fn mine_and_sync( let start = std::time::Instant::now(); loop { if let Ok(info) = client.get_node_info(GetNodeInfoRequest {}).await { - if info.current_best_block.as_ref().map(|b| b.height).unwrap_or(0) - >= expected_height as u32 - { + if info.current_best_block.height >= expected_height as u32 { break; } } if start.elapsed() > timeout { panic!( "Timed out waiting for server {} to sync to height {}", - server.node_id(), + server.node_id().to_lower_hex_string(), expected_height ); } @@ -409,7 +407,7 @@ pub async fn setup_funded_channel( let open_resp = server_a .client() .open_channel(OpenChannelRequest { - node_pubkey: server_b.node_id().to_string(), + node_pubkey: *server_b.node_id(), address: format!("127.0.0.1:{}", server_b.p2p_port), channel_amount_sats, push_to_counterparty_msat: None, @@ -502,17 +500,16 @@ impl RabbitMqEventConsumer { /// Consume up to `count` events, waiting up to `timeout` for each. pub async fn consume_events( &mut self, count: usize, timeout: Duration, - ) -> Vec { + ) -> Vec { use futures_util::StreamExt; use lapin::options::BasicAckOptions; - use prost::Message; let mut events = Vec::new(); for _ in 0..count { match tokio::time::timeout(timeout, self.consumer.next()).await { Ok(Some(Ok(delivery))) => { - let event = ldk_server_protos::events::EventEnvelope::decode(&*delivery.data) - .expect("Failed to decode event"); + let event: ldk_server_json_models::events::Event = + serde_json::from_slice(&delivery.data).expect("Failed to decode event"); self.channel .basic_ack(delivery.delivery_tag, BasicAckOptions::default()) .await diff --git a/e2e-tests/tests/e2e.rs b/e2e-tests/tests/e2e.rs index 577b74c1..4d9a1270 100644 --- a/e2e-tests/tests/e2e.rs +++ b/e2e-tests/tests/e2e.rs @@ -16,16 +16,16 @@ use e2e_tests::{ }; use hex_conservative::{DisplayHex, FromHex}; use ldk_node::bitcoin::hashes::{sha256, Hash}; +use ldk_node::bitcoin::secp256k1::PublicKey; +use ldk_node::bitcoin::Network; use ldk_node::lightning::ln::msgs::SocketAddress; use ldk_node::lightning::offers::offer::Offer; use ldk_node::lightning_invoice::Bolt11Invoice; -use ldk_server_client::ldk_server_protos::api::{ +use ldk_server_json_models::api::{ Bolt11ReceiveRequest, Bolt12ReceiveRequest, OnchainReceiveRequest, }; -use ldk_server_client::ldk_server_protos::types::{ - bolt11_invoice_description, Bolt11InvoiceDescription, -}; -use ldk_server_protos::events::event_envelope::Event; +use ldk_server_json_models::events::Event; +use ldk_server_json_models::types::Bolt11InvoiceDescription; #[tokio::test] async fn test_cli_get_node_info() { @@ -33,8 +33,13 @@ async fn test_cli_get_node_info() { let server = LdkServerHandle::start(&bitcoind).await; let output = run_cli(&server, &["get-node-info"]); - assert!(output.get("node_id").is_some()); - assert_eq!(output["node_id"], server.node_id()); + assert_eq!(output["node_id"], server.node_id().to_lower_hex_string()); + + // Verify block hash is a parseable 32-byte value and height is nonzero + let block_hash_hex = output["current_best_block"]["block_hash"].as_str().unwrap(); + assert_eq!(block_hash_hex.len(), 64); + <[u8; 32]>::from_hex(block_hash_hex).expect("block_hash should be valid 32-byte hex"); + assert!(output["current_best_block"]["height"].as_u64().unwrap() > 0); } #[tokio::test] @@ -102,7 +107,8 @@ async fn test_cli_verify_signature() { let sign_output = run_cli(&server, &["sign-message", "hello"]); let signature = sign_output["signature"].as_str().unwrap(); - let output = run_cli(&server, &["verify-signature", "hello", signature, server.node_id()]); + let node_id_hex = server.node_id().to_lower_hex_string(); + let output = run_cli(&server, &["verify-signature", "hello", signature, &node_id_hex]); assert_eq!(output["valid"], true); } @@ -118,9 +124,7 @@ async fn test_cli_export_pathfinding_scores() { .client() .bolt11_receive(Bolt11ReceiveRequest { amount_msat: Some(10_000_000), - description: Some(Bolt11InvoiceDescription { - kind: Some(bolt11_invoice_description::Kind::Direct("test".to_string())), - }), + description: Some(Bolt11InvoiceDescription::Direct("test".to_string())), expiry_secs: 3600, }) .await @@ -142,10 +146,18 @@ async fn test_cli_bolt11_receive() { assert!(invoice_str.starts_with("lnbcrt"), "Expected lnbcrt prefix, got: {}", invoice_str); let invoice: Bolt11Invoice = invoice_str.parse().unwrap(); - let payment_hash = sha256::Hash::from_str(output["payment_hash"].as_str().unwrap()).unwrap(); - assert_eq!(*invoice.payment_hash(), payment_hash); - let payment_secret = <[u8; 32]>::from_hex(output["payment_secret"].as_str().unwrap()).unwrap(); - assert_eq!(invoice.payment_secret().0, payment_secret); + + // Cross-check payment_hash bytes: API response vs parsed invoice + let api_hash = <[u8; 32]>::from_hex(output["payment_hash"].as_str().unwrap()).unwrap(); + assert_eq!( + api_hash, + *invoice.payment_hash().as_byte_array(), + "payment_hash bytes should match invoice" + ); + + // Cross-check payment_secret bytes: API response vs parsed invoice + let api_secret = <[u8; 32]>::from_hex(output["payment_secret"].as_str().unwrap()).unwrap(); + assert_eq!(api_secret, invoice.payment_secret().0, "payment_secret bytes should match invoice"); } #[tokio::test] @@ -181,7 +193,9 @@ async fn test_cli_onchain_send() { let dest_addr = recv_output["address"].as_str().unwrap(); let output = run_cli(&server, &["onchain-send", dest_addr, "50000sat"]); - assert!(!output["txid"].as_str().unwrap().is_empty()); + let txid_hex = output["txid"].as_str().unwrap(); + assert_eq!(txid_hex.len(), 64); + <[u8; 32]>::from_hex(txid_hex).expect("txid should be valid 32-byte hex"); } #[tokio::test] @@ -191,7 +205,8 @@ async fn test_cli_connect_peer() { let server_b = LdkServerHandle::start(&bitcoind).await; let addr = format!("127.0.0.1:{}", server_b.p2p_port); - let output = run_cli(&server_a, &["connect-peer", server_b.node_id(), &addr]); + let node_id_hex = server_b.node_id().to_lower_hex_string(); + let output = run_cli(&server_a, &["connect-peer", &node_id_hex, &addr]); // ConnectPeerResponse is empty assert!(output.is_object()); } @@ -208,12 +223,13 @@ async fn test_cli_list_peers() { assert!(output["peers"].as_array().unwrap().is_empty()); let addr = format!("127.0.0.1:{}", server_b.p2p_port); - run_cli(&server_a, &["connect-peer", server_b.node_id(), &addr]); + let node_id_hex = server_b.node_id().to_lower_hex_string(); + run_cli(&server_a, &["connect-peer", &node_id_hex, &addr]); let output = run_cli(&server_a, &["list-peers"]); let peers = output["peers"].as_array().unwrap(); assert_eq!(peers.len(), 1); - assert_eq!(peers[0]["node_id"], server_b.node_id()); + assert_eq!(peers[0]["node_id"], node_id_hex); assert_eq!(peers[0]["address"], addr); assert_eq!(peers[0]["is_persisted"], false); assert_eq!(peers[0]["is_connected"], true); @@ -238,9 +254,10 @@ async fn test_cli_open_channel() { // Open channel via CLI let addr = format!("127.0.0.1:{}", server_b.p2p_port); + let node_id_hex = server_b.node_id().to_lower_hex_string(); let output = run_cli( &server_a, - &["open-channel", server_b.node_id(), &addr, "100000sat", "--announce-channel"], + &["open-channel", &node_id_hex, &addr, "100000sat", "--announce-channel"], ); assert!(!output["user_channel_id"].as_str().unwrap().is_empty()); } @@ -255,7 +272,32 @@ async fn test_cli_list_channels() { let output = run_cli(&server_a, &["list-channels"]); let channels = output["channels"].as_array().unwrap(); assert!(!channels.is_empty()); - assert_eq!(channels[0]["counterparty_node_id"], server_b.node_id()); + + let ch = &channels[0]; + // Verify counterparty_node_id is server_b's actual pubkey + let cp_id = <[u8; 33]>::from_hex(ch["counterparty_node_id"].as_str().unwrap()).unwrap(); + assert_eq!(cp_id, *server_b.node_id(), "counterparty should be server_b"); + + // Verify channel_id is a valid 32-byte value + let channel_id = <[u8; 32]>::from_hex(ch["channel_id"].as_str().unwrap()).unwrap(); + assert_ne!(channel_id, [0u8; 32], "channel_id should not be all zeros"); + + // Verify funding txo has a valid txid + let funding_txid_hex = ch["funding_txo"]["txid"].as_str().unwrap(); + assert_eq!(funding_txid_hex.len(), 64); + <[u8; 32]>::from_hex(funding_txid_hex).expect("funding txid should be valid 32-byte hex"); + + // Both sides should see the same channel — cross-check from server_b + let output_b = run_cli(&server_b, &["list-channels"]); + let channels_b = output_b["channels"].as_array().unwrap(); + assert!(!channels_b.is_empty()); + let ch_b = &channels_b[0]; + let cp_id_b = <[u8; 33]>::from_hex(ch_b["counterparty_node_id"].as_str().unwrap()).unwrap(); + assert_eq!(cp_id_b, *server_a.node_id(), "server_b's counterparty should be server_a"); + assert_eq!( + ch_b["funding_txo"]["txid"], ch["funding_txo"]["txid"], + "both sides should report same funding txid" + ); } #[tokio::test] @@ -265,12 +307,13 @@ async fn test_cli_update_channel_config() { let server_b = LdkServerHandle::start(&bitcoind).await; let user_channel_id = setup_funded_channel(&bitcoind, &server_a, &server_b, 100_000).await; + let node_id_hex = server_b.node_id().to_lower_hex_string(); let output = run_cli( &server_a, &[ "update-channel-config", &user_channel_id, - server_b.node_id(), + &node_id_hex, "--forwarding-fee-base-msat", "100", ], @@ -295,9 +338,7 @@ async fn test_cli_bolt11_send() { .client() .bolt11_receive(Bolt11ReceiveRequest { amount_msat: Some(10_000_000), - description: Some(Bolt11InvoiceDescription { - kind: Some(bolt11_invoice_description::Kind::Direct("test".to_string())), - }), + description: Some(Bolt11InvoiceDescription::Direct("test".to_string())), expiry_secs: 3600, }) .await @@ -312,13 +353,13 @@ async fn test_cli_bolt11_send() { let events_a = consumer_a.consume_events(5, Duration::from_secs(10)).await; assert!( - events_a.iter().any(|e| matches!(&e.event, Some(Event::PaymentSuccessful(_)))), + events_a.iter().any(|e| matches!(e, Event::PaymentSuccessful(_))), "Expected PaymentSuccessful on sender" ); let events_b = consumer_b.consume_events(5, Duration::from_secs(10)).await; assert!( - events_b.iter().any(|e| matches!(&e.event, Some(Event::PaymentReceived(_)))), + events_b.iter().any(|e| matches!(e, Event::PaymentReceived(_))), "Expected PaymentReceived on receiver" ); } @@ -335,9 +376,7 @@ async fn test_cli_pay() { .client() .bolt11_receive(Bolt11ReceiveRequest { amount_msat: Some(10_000_000), - description: Some(Bolt11InvoiceDescription { - kind: Some(bolt11_invoice_description::Kind::Direct("test".to_string())), - }), + description: Some(Bolt11InvoiceDescription::Direct("test".to_string())), expiry_secs: 3600, }) .await @@ -395,7 +434,8 @@ async fn test_cli_spontaneous_send() { setup_funded_channel(&bitcoind, &server_a, &server_b, 100_000).await; - let output = run_cli(&server_a, &["spontaneous-send", server_b.node_id(), "10000sat"]); + let node_id_hex = server_b.node_id().to_lower_hex_string(); + let output = run_cli(&server_a, &["spontaneous-send", &node_id_hex, "10000sat"]); assert!(!output["payment_id"].as_str().unwrap().is_empty()); // Verify events @@ -403,13 +443,13 @@ async fn test_cli_spontaneous_send() { let events_a = consumer_a.consume_events(5, Duration::from_secs(10)).await; assert!( - events_a.iter().any(|e| matches!(&e.event, Some(Event::PaymentSuccessful(_)))), + events_a.iter().any(|e| matches!(e, Event::PaymentSuccessful(_))), "Expected PaymentSuccessful on sender" ); let events_b = consumer_b.consume_events(5, Duration::from_secs(10)).await; assert!( - events_b.iter().any(|e| matches!(&e.event, Some(Event::PaymentReceived(_)))), + events_b.iter().any(|e| matches!(e, Event::PaymentReceived(_))), "Expected PaymentReceived on receiver" ); } @@ -426,14 +466,14 @@ async fn test_cli_get_payment_details() { .client() .bolt11_receive(Bolt11ReceiveRequest { amount_msat: Some(10_000_000), - description: Some(Bolt11InvoiceDescription { - kind: Some(bolt11_invoice_description::Kind::Direct("test".to_string())), - }), + description: Some(Bolt11InvoiceDescription::Direct("test".to_string())), expiry_secs: 3600, }) .await .unwrap(); + let invoice: Bolt11Invoice = invoice_resp.invoice.parse().unwrap(); + let send_output = run_cli(&server_a, &["bolt11-send", &invoice_resp.invoice]); let payment_id = send_output["payment_id"].as_str().unwrap(); @@ -441,8 +481,19 @@ async fn test_cli_get_payment_details() { tokio::time::sleep(Duration::from_secs(3)).await; let output = run_cli(&server_a, &["get-payment-details", payment_id]); - assert!(output.get("payment").is_some()); - assert_eq!(output["payment"]["id"], payment_id); + let payment = &output["payment"]; + assert_eq!(payment["id"], payment_id); + assert_eq!(payment["status"], "succeeded"); + assert_eq!(payment["direction"], "outbound"); + + // Verify the payment hash in the details matches the invoice + let details_hash_hex = payment["kind"]["bolt11"]["hash"].as_str().unwrap(); + let details_hash = <[u8; 32]>::from_hex(details_hash_hex).unwrap(); + assert_eq!( + details_hash, + *invoice.payment_hash().as_byte_array(), + "payment hash in details should match invoice" + ); } #[tokio::test] @@ -457,9 +508,7 @@ async fn test_cli_list_payments() { .client() .bolt11_receive(Bolt11ReceiveRequest { amount_msat: Some(10_000_000), - description: Some(Bolt11InvoiceDescription { - kind: Some(bolt11_invoice_description::Kind::Direct("test".to_string())), - }), + description: Some(Bolt11InvoiceDescription::Direct("test".to_string())), expiry_secs: 3600, }) .await @@ -479,7 +528,8 @@ async fn test_cli_close_channel() { let server_b = LdkServerHandle::start(&bitcoind).await; let user_channel_id = setup_funded_channel(&bitcoind, &server_a, &server_b, 100_000).await; - let output = run_cli(&server_a, &["close-channel", &user_channel_id, server_b.node_id()]); + let node_id_hex = server_b.node_id().to_lower_hex_string(); + let output = run_cli(&server_a, &["close-channel", &user_channel_id, &node_id_hex]); assert!(output.is_object()); mine_and_sync(&bitcoind, &[&server_a, &server_b], 6).await; @@ -496,7 +546,8 @@ async fn test_cli_force_close_channel() { let server_b = LdkServerHandle::start(&bitcoind).await; let user_channel_id = setup_funded_channel(&bitcoind, &server_a, &server_b, 100_000).await; - let output = run_cli(&server_a, &["force-close-channel", &user_channel_id, server_b.node_id()]); + let node_id_hex = server_b.node_id().to_lower_hex_string(); + let output = run_cli(&server_a, &["force-close-channel", &user_channel_id, &node_id_hex]); assert!(output.is_object()); mine_and_sync(&bitcoind, &[&server_a, &server_b], 6).await; @@ -513,8 +564,8 @@ async fn test_cli_splice_in() { let server_b = LdkServerHandle::start(&bitcoind).await; let user_channel_id = setup_funded_channel(&bitcoind, &server_a, &server_b, 100_000).await; - let output = - run_cli(&server_a, &["splice-in", &user_channel_id, server_b.node_id(), "50000sat"]); + let node_id_hex = server_b.node_id().to_lower_hex_string(); + let output = run_cli(&server_a, &["splice-in", &user_channel_id, &node_id_hex, "50000sat"]); assert!(output.is_object()); } @@ -525,8 +576,8 @@ async fn test_cli_splice_out() { let server_b = LdkServerHandle::start(&bitcoind).await; let user_channel_id = setup_funded_channel(&bitcoind, &server_a, &server_b, 100_000).await; - let output = - run_cli(&server_a, &["splice-out", &user_channel_id, server_b.node_id(), "10000sat"]); + let node_id_hex = server_b.node_id().to_lower_hex_string(); + let output = run_cli(&server_a, &["splice-out", &user_channel_id, &node_id_hex, "10000sat"]); let address = output["address"].as_str().unwrap(); assert!(address.starts_with("bcrt1"), "Expected regtest address, got: {}", address); } @@ -577,7 +628,9 @@ async fn test_cli_graph_with_channel() { let channel = &output["channel"]; let node_one = channel["node_one"].as_str().unwrap(); let node_two = channel["node_two"].as_str().unwrap(); - let nodes = [server_a.node_id(), server_b.node_id()]; + let node_a_hex = server_a.node_id().to_lower_hex_string(); + let node_b_hex = server_b.node_id().to_lower_hex_string(); + let nodes = [node_a_hex.as_str(), node_b_hex.as_str()]; assert!(nodes.contains(&node_one), "node_one {} not one of our nodes", node_one); assert!(nodes.contains(&node_two), "node_two {} not one of our nodes", node_two); @@ -585,11 +638,11 @@ async fn test_cli_graph_with_channel() { let output = run_cli(&server_a, &["graph-list-nodes"]); let node_ids: Vec<&str> = output["node_ids"].as_array().unwrap().iter().map(|n| n.as_str().unwrap()).collect(); - assert!(node_ids.contains(&server_a.node_id()), "Expected server_a in graph nodes"); - assert!(node_ids.contains(&server_b.node_id()), "Expected server_b in graph nodes"); + assert!(node_ids.contains(&node_a_hex.as_str()), "Expected server_a in graph nodes"); + assert!(node_ids.contains(&node_b_hex.as_str()), "Expected server_b in graph nodes"); // Test GraphGetNode: should return node info with at least one channel. - let output = run_cli(&server_a, &["graph-get-node", server_b.node_id()]); + let output = run_cli(&server_a, &["graph-get-node", &node_b_hex]); let node = &output["node"]; let channels = node["channels"].as_array().unwrap(); assert!(!channels.is_empty(), "Expected node to have at least one channel"); @@ -630,7 +683,7 @@ async fn test_forwarded_payment_event() { let storage_dir_c = tempfile::tempdir().unwrap().into_path(); let p2p_port_c = find_available_port(); let config_c = ldk_node::config::Config { - network: ldk_node::bitcoin::Network::Regtest, + network: Network::Regtest, storage_dir_path: storage_dir_c.to_str().unwrap().to_string(), listening_addresses: Some(vec![SocketAddress::from_str(&format!( "127.0.0.1:{p2p_port_c}" @@ -644,7 +697,8 @@ async fn test_forwarded_payment_event() { builder_c.set_chain_source_bitcoind_rpc(rpc_host, rpc_port, rpc_user, rpc_password); // Set B as LSPS2 LSP for C - let b_node_id = ldk_node::bitcoin::secp256k1::PublicKey::from_str(server_b.node_id()).unwrap(); + let b_node_id_hex = server_b.node_id().to_lower_hex_string(); + let b_node_id = PublicKey::from_str(&b_node_id_hex).unwrap(); let b_addr = SocketAddress::from_str(&format!("127.0.0.1:{}", server_b.p2p_port)).unwrap(); builder_c.set_liquidity_source_lsps2(b_node_id, b_addr, None); @@ -678,9 +732,9 @@ async fn test_forwarded_payment_event() { // Verify PaymentForwarded event on B let events_b = consumer_b.consume_events(10, Duration::from_secs(15)).await; assert!( - events_b.iter().any(|e| matches!(&e.event, Some(Event::PaymentForwarded(_)))), + events_b.iter().any(|e| matches!(e, Event::PaymentForwarded(_))), "Expected PaymentForwarded event on LSP node B, got events: {:?}", - events_b.iter().map(|e| &e.event).collect::>() + events_b.iter().map(|e| e).collect::>() ); node_c.stop().unwrap(); @@ -731,9 +785,9 @@ async fn test_hodl_invoice_claim() { // Verify PaymentClaimable event on B let events_b = consumer_b.consume_events(1, Duration::from_secs(10)).await; assert!( - events_b.iter().any(|e| matches!(&e.event, Some(Event::PaymentClaimable(_)))), + events_b.iter().any(|e| matches!(e, Event::PaymentClaimable(_))), "Expected PaymentClaimable on receiver, got events: {:?}", - events_b.iter().map(|e| &e.event).collect::>() + events_b.iter().map(|e| e).collect::>() ); // Claim the payment on B @@ -749,26 +803,23 @@ async fn test_hodl_invoice_claim() { // Verify PaymentReceived event on B let events_b = consumer_b.consume_events(1, Duration::from_secs(10)).await; assert!( - events_b.iter().any(|e| matches!(&e.event, Some(Event::PaymentReceived(_)))), + events_b.iter().any(|e| matches!(e, Event::PaymentReceived(_))), "Expected PaymentReceived on receiver after claim, got events: {:?}", - events_b.iter().map(|e| &e.event).collect::>() + events_b.iter().map(|e| e).collect::>() ); // Verify PaymentSuccessful on A let events_a = consumer_a.consume_events(1, Duration::from_secs(10)).await; assert!( - events_a.iter().any(|e| matches!(&e.event, Some(Event::PaymentSuccessful(_)))), + events_a.iter().any(|e| matches!(e, Event::PaymentSuccessful(_))), "Expected PaymentSuccessful on sender, got events: {:?}", - events_a.iter().map(|e| &e.event).collect::>() + events_a.iter().map(|e| e).collect::>() ); } } #[tokio::test] async fn test_hodl_invoice_fail() { - use hex_conservative::DisplayHex; - use ldk_node::bitcoin::hashes::{sha256, Hash}; - let bitcoind = TestBitcoind::new(); let server_a = LdkServerHandle::start(&bitcoind).await; let server_b = LdkServerHandle::start(&bitcoind).await; @@ -808,9 +859,9 @@ async fn test_hodl_invoice_fail() { // Verify PaymentClaimable event on B let events_b = consumer_b.consume_events(5, Duration::from_secs(10)).await; assert!( - events_b.iter().any(|e| matches!(&e.event, Some(Event::PaymentClaimable(_)))), + events_b.iter().any(|e| matches!(e, Event::PaymentClaimable(_))), "Expected PaymentClaimable on receiver, got events: {:?}", - events_b.iter().map(|e| &e.event).collect::>() + events_b.iter().map(|e| e).collect::>() ); // Fail the payment on B using CLI @@ -822,8 +873,8 @@ async fn test_hodl_invoice_fail() { // Verify PaymentFailed on A let events_a = consumer_a.consume_events(10, Duration::from_secs(10)).await; assert!( - events_a.iter().any(|e| matches!(&e.event, Some(Event::PaymentFailed(_)))), + events_a.iter().any(|e| matches!(e, Event::PaymentFailed(_))), "Expected PaymentFailed on sender after hodl rejection, got events: {:?}", - events_a.iter().map(|e| &e.event).collect::>() + events_a.iter().map(|e| e).collect::>() ); } diff --git a/ldk-server-cli/Cargo.toml b/ldk-server-cli/Cargo.toml index 5d006001..4b839146 100644 --- a/ldk-server-cli/Cargo.toml +++ b/ldk-server-cli/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -ldk-server-client = { path = "../ldk-server-client", features = ["serde"] } +ldk-server-client = { path = "../ldk-server-client" } clap = { version = "4.0.5", default-features = false, features = ["derive", "std", "error-context", "suggestions", "help"] } clap_complete = { version = "4.0", default-features = false } hex-conservative = { version = "0.2", default-features = false, features = ["std"] } diff --git a/ldk-server-cli/src/main.rs b/ldk-server-cli/src/main.rs index d960f087..b60e9d2d 100644 --- a/ldk-server-cli/src/main.rs +++ b/ldk-server-cli/src/main.rs @@ -15,13 +15,14 @@ use config::{ api_key_path_for_storage_dir, cert_path_for_storage_dir, get_default_api_key_path, get_default_cert_path, get_default_config_path, load_config, }; -use hex_conservative::DisplayHex; +use hex_conservative::{DisplayHex, FromHex}; use ldk_server_client::client::LdkServerClient; use ldk_server_client::error::LdkServerError; use ldk_server_client::error::LdkServerErrorCode::{ - AuthError, InternalError, InternalServerError, InvalidRequestError, LightningError, + AuthError, InternalError, InternalServerError, InvalidRequestError, JsonParseError, + LightningError, }; -use ldk_server_client::ldk_server_protos::api::{ +use ldk_server_client::ldk_server_json_models::api::{ Bolt11ClaimForHashRequest, Bolt11ClaimForHashResponse, Bolt11FailForHashRequest, Bolt11FailForHashResponse, Bolt11ReceiveForHashRequest, Bolt11ReceiveForHashResponse, Bolt11ReceiveRequest, Bolt11ReceiveResponse, Bolt11ReceiveVariableAmountViaJitChannelRequest, @@ -42,9 +43,8 @@ use ldk_server_client::ldk_server_protos::api::{ SpontaneousSendResponse, UnifiedSendRequest, UnifiedSendResponse, UpdateChannelConfigRequest, UpdateChannelConfigResponse, VerifySignatureRequest, VerifySignatureResponse, }; -use ldk_server_client::ldk_server_protos::types::{ - bolt11_invoice_description, Bolt11InvoiceDescription, ChannelConfig, PageToken, - RouteParametersConfig, +use ldk_server_client::ldk_server_json_models::types::{ + Bolt11InvoiceDescription, ChannelConfig, PageToken, RouteParametersConfig, }; use serde::Serialize; use serde_json::{json, Value}; @@ -55,9 +55,7 @@ use types::{ mod config; mod types; -// Having these default values as constants in the Proto file and -// importing/reusing them here might be better, but Proto3 removed -// the ability to set default values. +// Default values for route parameters configuration. const DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA: u32 = 1008; const DEFAULT_MAX_PATH_COUNT: u32 = 10; const DEFAULT_MAX_CHANNEL_SATURATION_POWER_OF_HALF: u32 = 2; @@ -654,12 +652,8 @@ async fn main() { } => { let amount_msat = amount.map(|a| a.to_msat()); let invoice_description = match (description, description_hash) { - (Some(desc), None) => Some(Bolt11InvoiceDescription { - kind: Some(bolt11_invoice_description::Kind::Direct(desc)), - }), - (None, Some(hash)) => Some(Bolt11InvoiceDescription { - kind: Some(bolt11_invoice_description::Kind::Hash(hash)), - }), + (Some(desc), None) => Some(Bolt11InvoiceDescription::Direct(desc)), + (None, Some(hash)) => Some(Bolt11InvoiceDescription::Hash(hash)), (Some(_), Some(_)) => { handle_error(LdkServerError::new( InternalError, @@ -674,7 +668,7 @@ async fn main() { description: invoice_description, expiry_secs, amount_msat, - payment_hash, + payment_hash: parse_hex_32(&payment_hash, "payment_hash"), }; handle_response_result::<_, Bolt11ReceiveForHashResponse>( @@ -685,16 +679,20 @@ async fn main() { handle_response_result::<_, Bolt11ClaimForHashResponse>( client .bolt11_claim_for_hash(Bolt11ClaimForHashRequest { - payment_hash, + payment_hash: payment_hash.map(|h| parse_hex_32(&h, "payment_hash")), claimable_amount_msat: claimable_amount.map(|a| a.to_msat()), - preimage, + preimage: parse_hex_32(&preimage, "preimage"), }) .await, ); }, Commands::Bolt11FailForHash { payment_hash } => { handle_response_result::<_, Bolt11FailForHashResponse>( - client.bolt11_fail_for_hash(Bolt11FailForHashRequest { payment_hash }).await, + client + .bolt11_fail_for_hash(Bolt11FailForHashRequest { + payment_hash: parse_hex_32(&payment_hash, "payment_hash"), + }) + .await, ); }, Commands::Bolt11ReceiveViaJitChannel { @@ -828,7 +826,7 @@ async fn main() { client .spontaneous_send(SpontaneousSendRequest { amount_msat, - node_id, + node_id: parse_hex_33(&node_id, "node_id"), route_parameters: Some(route_parameters), }) .await, @@ -865,7 +863,13 @@ async fn main() { Commands::CloseChannel { user_channel_id, counterparty_node_id } => { handle_response_result::<_, CloseChannelResponse>( client - .close_channel(CloseChannelRequest { user_channel_id, counterparty_node_id }) + .close_channel(CloseChannelRequest { + user_channel_id, + counterparty_node_id: parse_hex_33( + &counterparty_node_id, + "counterparty_node_id", + ), + }) .await, ); }, @@ -878,7 +882,10 @@ async fn main() { client .force_close_channel(ForceCloseChannelRequest { user_channel_id, - counterparty_node_id, + counterparty_node_id: parse_hex_33( + &counterparty_node_id, + "counterparty_node_id", + ), force_close_reason, }) .await, @@ -906,7 +913,7 @@ async fn main() { handle_response_result::<_, OpenChannelResponse>( client .open_channel(OpenChannelRequest { - node_pubkey, + node_pubkey: parse_hex_33(&node_pubkey, "node_pubkey"), address, channel_amount_sats, push_to_counterparty_msat, @@ -923,7 +930,10 @@ async fn main() { client .splice_in(SpliceInRequest { user_channel_id, - counterparty_node_id, + counterparty_node_id: parse_hex_33( + &counterparty_node_id, + "counterparty_node_id", + ), splice_amount_sats, }) .await, @@ -936,7 +946,10 @@ async fn main() { client .splice_out(SpliceOutRequest { user_channel_id, - counterparty_node_id, + counterparty_node_id: parse_hex_33( + &counterparty_node_id, + "counterparty_node_id", + ), address, splice_amount_sats, }) @@ -964,7 +977,11 @@ async fn main() { }, Commands::GetPaymentDetails { payment_id } => { handle_response_result::<_, GetPaymentDetailsResponse>( - client.get_payment_details(GetPaymentDetailsRequest { payment_id }).await, + client + .get_payment_details(GetPaymentDetailsRequest { + payment_id: parse_hex_32(&payment_id, "payment_id"), + }) + .await, ); }, Commands::ListForwardedPayments { number_of_payments, page_token } => { @@ -1005,7 +1022,10 @@ async fn main() { client .update_channel_config(UpdateChannelConfigRequest { user_channel_id, - counterparty_node_id, + counterparty_node_id: parse_hex_33( + &counterparty_node_id, + "counterparty_node_id", + ), channel_config: Some(channel_config), }) .await, @@ -1021,12 +1041,22 @@ async fn main() { std::process::exit(1); }; handle_response_result::<_, ConnectPeerResponse>( - client.connect_peer(ConnectPeerRequest { node_pubkey, address, persist }).await, + client + .connect_peer(ConnectPeerRequest { + node_pubkey: parse_hex_33(&node_pubkey, "node_pubkey"), + address, + persist, + }) + .await, ); }, Commands::DisconnectPeer { node_pubkey } => { handle_response_result::<_, DisconnectPeerResponse>( - client.disconnect_peer(DisconnectPeerRequest { node_pubkey }).await, + client + .disconnect_peer(DisconnectPeerRequest { + node_pubkey: parse_hex_33(&node_pubkey, "node_pubkey"), + }) + .await, ); }, Commands::ListPeers => { @@ -1036,18 +1066,16 @@ async fn main() { }, Commands::SignMessage { message } => { handle_response_result::<_, SignMessageResponse>( - client - .sign_message(SignMessageRequest { message: message.into_bytes().into() }) - .await, + client.sign_message(SignMessageRequest { message: message.into_bytes() }).await, ); }, Commands::VerifySignature { message, signature, public_key } => { handle_response_result::<_, VerifySignatureResponse>( client .verify_signature(VerifySignatureRequest { - message: message.into_bytes().into(), + message: message.into_bytes(), signature, - public_key, + public_key: parse_hex_33(&public_key, "public_key"), }) .await, ); @@ -1079,7 +1107,11 @@ async fn main() { }, Commands::GraphGetNode { node_id } => { handle_response_result::<_, GraphGetNodeResponse>( - client.graph_get_node(GraphGetNodeRequest { node_id }).await, + client + .graph_get_node(GraphGetNodeRequest { + node_id: parse_hex_33(&node_id, "node_id"), + }) + .await, ); }, Commands::Completions { .. } => unreachable!("Handled above"), @@ -1166,16 +1198,30 @@ where } } +fn parse_hex_32(hex: &str, field_name: &str) -> [u8; 32] { + <[u8; 32]>::from_hex(hex).unwrap_or_else(|_| { + handle_error(LdkServerError::new( + InvalidRequestError, + format!("Invalid {field_name}, must be a 32-byte hex string."), + )) + }) +} + +fn parse_hex_33(hex: &str, field_name: &str) -> [u8; 33] { + <[u8; 33]>::from_hex(hex).unwrap_or_else(|_| { + handle_error(LdkServerError::new( + InvalidRequestError, + format!("Invalid {field_name}, must be a 33-byte hex string."), + )) + }) +} + fn parse_bolt11_invoice_description( description: Option, description_hash: Option, ) -> Option { match (description, description_hash) { - (Some(desc), None) => Some(Bolt11InvoiceDescription { - kind: Some(bolt11_invoice_description::Kind::Direct(desc)), - }), - (None, Some(hash)) => Some(Bolt11InvoiceDescription { - kind: Some(bolt11_invoice_description::Kind::Hash(hash)), - }), + (Some(desc), None) => Some(Bolt11InvoiceDescription::Direct(desc)), + (None, Some(hash)) => Some(Bolt11InvoiceDescription::Hash(hash)), (Some(_), Some(_)) => { handle_error(LdkServerError::new( InternalError, @@ -1211,6 +1257,7 @@ fn handle_error(e: LdkServerError) -> ! { AuthError => "Authentication Error", LightningError => "Lightning Error", InternalServerError => "Internal Server Error", + JsonParseError => "JSON Parse Error", InternalError => "Internal Error", }; eprintln!("Error ({}): {}", error_type, e.message); diff --git a/ldk-server-cli/src/types.rs b/ldk-server-cli/src/types.rs index 92f778ad..ca42760f 100644 --- a/ldk-server-cli/src/types.rs +++ b/ldk-server-cli/src/types.rs @@ -16,7 +16,7 @@ use std::fmt; use std::str::FromStr; -use ldk_server_client::ldk_server_protos::types::{ForwardedPayment, PageToken, Payment}; +use ldk_server_client::ldk_server_json_models::types::{ForwardedPayment, PageToken, Payment}; use serde::Serialize; /// CLI-specific wrapper for paginated responses that formats the page token diff --git a/ldk-server-client/Cargo.toml b/ldk-server-client/Cargo.toml index 13916fa3..c1560fad 100644 --- a/ldk-server-client/Cargo.toml +++ b/ldk-server-client/Cargo.toml @@ -3,12 +3,9 @@ name = "ldk-server-client" version = "0.1.0" edition = "2021" -[features] -default = [] -serde = ["ldk-server-protos/serde"] - [dependencies] -ldk-server-protos = { path = "../ldk-server-protos" } +ldk-server-json-models = { path = "../ldk-server-json-models" } reqwest = { version = "0.11.13", default-features = false, features = ["rustls-tls"] } -prost = { version = "0.11.6", default-features = false, features = ["std", "prost-derive"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" bitcoin_hashes = "0.14" diff --git a/ldk-server-client/src/client.rs b/ldk-server-client/src/client.rs index 75459a45..a087950f 100644 --- a/ldk-server-client/src/client.rs +++ b/ldk-server-client/src/client.rs @@ -11,7 +11,7 @@ use std::time::{SystemTime, UNIX_EPOCH}; use bitcoin_hashes::hmac::{Hmac, HmacEngine}; use bitcoin_hashes::{sha256, Hash, HashEngine}; -use ldk_server_protos::api::{ +use ldk_server_json_models::api::{ Bolt11ClaimForHashRequest, Bolt11ClaimForHashResponse, Bolt11FailForHashRequest, Bolt11FailForHashResponse, Bolt11ReceiveForHashRequest, Bolt11ReceiveForHashResponse, Bolt11ReceiveRequest, Bolt11ReceiveResponse, Bolt11ReceiveVariableAmountViaJitChannelRequest, @@ -33,7 +33,7 @@ use ldk_server_protos::api::{ SpontaneousSendResponse, UnifiedSendRequest, UnifiedSendResponse, UpdateChannelConfigRequest, UpdateChannelConfigResponse, VerifySignatureRequest, VerifySignatureResponse, }; -use ldk_server_protos::endpoints::{ +use ldk_server_json_models::endpoints::{ BOLT11_CLAIM_FOR_HASH_PATH, BOLT11_FAIL_FOR_HASH_PATH, BOLT11_RECEIVE_FOR_HASH_PATH, BOLT11_RECEIVE_PATH, BOLT11_RECEIVE_VARIABLE_AMOUNT_VIA_JIT_CHANNEL_PATH, BOLT11_RECEIVE_VIA_JIT_CHANNEL_PATH, BOLT11_SEND_PATH, BOLT12_RECEIVE_PATH, BOLT12_SEND_PATH, @@ -45,17 +45,19 @@ use ldk_server_protos::endpoints::{ SPLICE_OUT_PATH, SPONTANEOUS_SEND_PATH, UNIFIED_SEND_PATH, UPDATE_CHANNEL_CONFIG_PATH, VERIFY_SIGNATURE_PATH, }; -use ldk_server_protos::error::{ErrorCode, ErrorResponse}; -use prost::Message; +use ldk_server_json_models::error::{ErrorCode, ErrorResponse}; use reqwest::header::CONTENT_TYPE; use reqwest::{Certificate, Client}; +use serde::de::DeserializeOwned; +use serde::Serialize; use crate::error::LdkServerError; use crate::error::LdkServerErrorCode::{ - AuthError, InternalError, InternalServerError, InvalidRequestError, LightningError, + AuthError, InternalError, InternalServerError, InvalidRequestError, JsonParseError, + LightningError, }; -const APPLICATION_OCTET_STREAM: &str = "application/octet-stream"; +const APPLICATION_JSON: &str = "application/json"; /// Client to access a hosted instance of LDK Server. /// @@ -427,15 +429,17 @@ impl LdkServerClient { self.post_request(&request, &url).await } - async fn post_request( + async fn post_request( &self, request: &Rq, url: &str, ) -> Result { - let request_body = request.encode_to_vec(); + let request_body = serde_json::to_vec(request).map_err(|e| { + LdkServerError::new(JsonParseError, format!("Failed to serialize request: {}", e)) + })?; let auth_header = self.compute_auth_header(&request_body); let response_raw = self .client .post(url) - .header(CONTENT_TYPE, APPLICATION_OCTET_STREAM) + .header(CONTENT_TYPE, APPLICATION_JSON) .header("X-Auth", auth_header) .body(request_body) .send() @@ -450,26 +454,27 @@ impl LdkServerClient { })?; if status.is_success() { - Ok(Rs::decode(&payload[..]).map_err(|e| { + Ok(serde_json::from_slice::(&payload).map_err(|e| { LdkServerError::new( - InternalError, + JsonParseError, format!("Failed to decode success response: {}", e), ) })?) } else { - let error_response = ErrorResponse::decode(&payload[..]).map_err(|e| { - LdkServerError::new( - InternalError, - format!("Failed to decode error response (status {}): {}", status, e), - ) - })?; - - let error_code = match ErrorCode::from_i32(error_response.error_code) { - Some(ErrorCode::InvalidRequestError) => InvalidRequestError, - Some(ErrorCode::AuthError) => AuthError, - Some(ErrorCode::LightningError) => LightningError, - Some(ErrorCode::InternalServerError) => InternalServerError, - Some(ErrorCode::UnknownError) | None => InternalError, + let error_response = + serde_json::from_slice::(&payload).map_err(|e| { + LdkServerError::new( + JsonParseError, + format!("Failed to decode error response (status {}): {}", status, e), + ) + })?; + + let error_code = match error_response.error_code { + ErrorCode::InvalidRequestError => InvalidRequestError, + ErrorCode::AuthError => AuthError, + ErrorCode::LightningError => LightningError, + ErrorCode::InternalServerError => InternalServerError, + ErrorCode::UnknownError => InternalError, }; Err(LdkServerError::new(error_code, error_response.message)) diff --git a/ldk-server-client/src/error.rs b/ldk-server-client/src/error.rs index 67cba37d..06bc867a 100644 --- a/ldk-server-client/src/error.rs +++ b/ldk-server-client/src/error.rs @@ -41,18 +41,21 @@ impl fmt::Display for LdkServerError { /// Defines error codes for categorizing LDK server errors. #[derive(Clone, Debug, PartialEq, Eq)] pub enum LdkServerErrorCode { - /// Please refer to [`ldk_server_protos::error::ErrorCode::InvalidRequestError`]. + /// Please refer to [`ldk_server_json_models::error::ErrorCode::InvalidRequestError`]. InvalidRequestError, - /// Please refer to [`ldk_server_protos::error::ErrorCode::AuthError`]. + /// Please refer to [`ldk_server_json_models::error::ErrorCode::AuthError`]. AuthError, - /// Please refer to [`ldk_server_protos::error::ErrorCode::LightningError`]. + /// Please refer to [`ldk_server_json_models::error::ErrorCode::LightningError`]. LightningError, - /// Please refer to [`ldk_server_protos::error::ErrorCode::InternalServerError`]. + /// Please refer to [`ldk_server_json_models::error::ErrorCode::InternalServerError`]. InternalServerError, + /// A JSON serialization or deserialization error occurred. + JsonParseError, + /// There is an unknown error, it could be a client-side bug, unrecognized error-code, network error /// or something else. InternalError, @@ -65,6 +68,7 @@ impl fmt::Display for LdkServerErrorCode { LdkServerErrorCode::AuthError => write!(f, "AuthError"), LdkServerErrorCode::LightningError => write!(f, "LightningError"), LdkServerErrorCode::InternalServerError => write!(f, "InternalServerError"), + LdkServerErrorCode::JsonParseError => write!(f, "JsonParseError"), LdkServerErrorCode::InternalError => write!(f, "InternalError"), } } diff --git a/ldk-server-client/src/lib.rs b/ldk-server-client/src/lib.rs index 098c7087..708f23a3 100644 --- a/ldk-server-client/src/lib.rs +++ b/ldk-server-client/src/lib.rs @@ -20,4 +20,4 @@ pub mod client; pub mod error; /// Request/Response structs required for interacting with the ldk-ldk-server-client. -pub use ldk_server_protos; +pub use ldk_server_json_models; diff --git a/ldk-server-json-models/Cargo.toml b/ldk-server-json-models/Cargo.toml new file mode 100644 index 00000000..928107a0 --- /dev/null +++ b/ldk-server-json-models/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "ldk-server-json-models" +version = "0.1.0" +edition = "2021" + +[dependencies] +serde = { version = "1.0", features = ["derive"] } diff --git a/ldk-server-protos/src/api.rs b/ldk-server-json-models/src/api.rs similarity index 52% rename from ldk-server-protos/src/api.rs rename to ldk-server-json-models/src/api.rs index f72357d3..1a97eec4 100644 --- a/ldk-server-protos/src/api.rs +++ b/ldk-server-json-models/src/api.rs @@ -7,113 +7,85 @@ // You may not use this file except in accordance with one or both of these // licenses. +use serde::{Deserialize, Serialize}; + /// Retrieve the latest node info like `node_id`, `current_best_block` etc. /// See more: /// - /// - -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct GetNodeInfoRequest {} /// The response `content` for the `GetNodeInfo` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct GetNodeInfoResponse { /// The hex-encoded `node-id` or public key for our own lightning node. - #[prost(string, tag = "1")] - pub node_id: ::prost::alloc::string::String, + #[serde(with = "crate::serde_utils::hex_33")] + pub node_id: [u8; 33], /// The best block to which our Lightning wallet is currently synced. - /// - /// Should be always set, will never be `None`. - #[prost(message, optional, tag = "3")] - pub current_best_block: ::core::option::Option, + pub current_best_block: super::types::BestBlock, /// The timestamp, in seconds since start of the UNIX epoch, when we last successfully synced our Lightning wallet to /// the chain tip. /// /// Will be `None` if the wallet hasn't been synced yet. - #[prost(uint64, optional, tag = "4")] - pub latest_lightning_wallet_sync_timestamp: ::core::option::Option, + pub latest_lightning_wallet_sync_timestamp: Option, /// The timestamp, in seconds since start of the UNIX epoch, when we last successfully synced our on-chain /// wallet to the chain tip. /// - /// Will be `None` if the wallet hasn’t been synced since the node was initialized. - #[prost(uint64, optional, tag = "5")] - pub latest_onchain_wallet_sync_timestamp: ::core::option::Option, + /// Will be `None` if the wallet hasn't been synced since the node was initialized. + pub latest_onchain_wallet_sync_timestamp: Option, /// The timestamp, in seconds since start of the UNIX epoch, when we last successfully update our fee rate cache. /// - /// Will be `None` if the cache hasn’t been updated since the node was initialized. - #[prost(uint64, optional, tag = "6")] - pub latest_fee_rate_cache_update_timestamp: ::core::option::Option, + /// Will be `None` if the cache hasn't been updated since the node was initialized. + pub latest_fee_rate_cache_update_timestamp: Option, /// The timestamp, in seconds since start of the UNIX epoch, when the last rapid gossip sync (RGS) snapshot we /// successfully applied was generated. /// - /// Will be `None` if RGS isn’t configured or the snapshot hasn’t been updated since the node was initialized. - #[prost(uint64, optional, tag = "7")] - pub latest_rgs_snapshot_timestamp: ::core::option::Option, + /// Will be `None` if RGS isn't configured or the snapshot hasn't been updated since the node was initialized. + pub latest_rgs_snapshot_timestamp: Option, /// The timestamp, in seconds since start of the UNIX epoch, when we last broadcasted a node announcement. /// - /// Will be `None` if we have no public channels or we haven’t broadcasted since the node was initialized. - #[prost(uint64, optional, tag = "8")] - pub latest_node_announcement_broadcast_timestamp: ::core::option::Option, + /// Will be `None` if we have no public channels or we haven't broadcasted since the node was initialized. + pub latest_node_announcement_broadcast_timestamp: Option, /// The addresses the node is currently listening on for incoming connections. /// /// Will be empty if the node is not listening on any addresses. - #[prost(string, repeated, tag = "9")] - pub listening_addresses: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + pub listening_addresses: Vec, /// The addresses the node announces to the network. /// /// Will be empty if no announcement addresses are configured. - #[prost(string, repeated, tag = "10")] - pub announcement_addresses: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + pub announcement_addresses: Vec, /// The node alias, if configured. /// /// Will be `None` if no alias is configured. - #[prost(string, optional, tag = "11")] - pub node_alias: ::core::option::Option<::prost::alloc::string::String>, + pub node_alias: Option, /// The node URIs that can be used to connect to this node, in the format `node_id@address`. /// /// These are constructed from the announcement addresses and the node's public key. /// Will be empty if no announcement addresses are configured. - #[prost(string, repeated, tag = "12")] - pub node_uris: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + pub node_uris: Vec, } /// Retrieve a new on-chain funding address. /// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct OnchainReceiveRequest {} /// The response `content` for the `OnchainReceive` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`.. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct OnchainReceiveResponse { /// A Bitcoin on-chain address. - #[prost(string, tag = "1")] - pub address: ::prost::alloc::string::String, + pub address: String, } /// Send an on-chain payment to the given address. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct OnchainSendRequest { /// The address to send coins to. - #[prost(string, tag = "1")] - pub address: ::prost::alloc::string::String, + pub address: String, /// The amount in satoshis to send. /// While sending the specified amount, we will respect any on-chain reserve we need to keep, /// i.e., won't allow to cut into `total_anchor_channels_reserve_sats`. /// See more: - #[prost(uint64, optional, tag = "2")] - pub amount_sats: ::core::option::Option, + pub amount_sats: Option, /// If set, the amount_sats field should be unset. /// It indicates that node will send full balance to the specified address. /// @@ -121,23 +93,18 @@ pub struct OnchainSendRequest { /// which might be potentially dangerous if you have open Anchor channels for which you can't trust /// the counterparty to spend the Anchor output after channel closure. /// See more: - #[prost(bool, optional, tag = "3")] - pub send_all: ::core::option::Option, + pub send_all: Option, /// If `fee_rate_sat_per_vb` is set it will be used on the resulting transaction. Otherwise we'll retrieve /// a reasonable estimate from BitcoinD. - #[prost(uint64, optional, tag = "4")] - pub fee_rate_sat_per_vb: ::core::option::Option, + pub fee_rate_sat_per_vb: Option, } /// The response `content` for the `OnchainSend` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct OnchainSendResponse { /// The transaction ID of the broadcasted transaction. - #[prost(string, tag = "1")] - pub txid: ::prost::alloc::string::String, + #[serde(with = "crate::serde_utils::hex_32")] + pub txid: [u8; 32], } /// Return a BOLT11 payable invoice that can be used to request and receive a payment /// for the given amount, if specified. @@ -145,40 +112,30 @@ pub struct OnchainSendResponse { /// See more: /// - /// - -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Bolt11ReceiveRequest { /// The amount in millisatoshi to send. If unset, a "zero-amount" or variable-amount invoice is returned. - #[prost(uint64, optional, tag = "1")] - pub amount_msat: ::core::option::Option, + pub amount_msat: Option, /// An optional description to attach along with the invoice. /// Will be set in the description field of the encoded payment request. - #[prost(message, optional, tag = "2")] - pub description: ::core::option::Option, + pub description: Option, /// Invoice expiry time in seconds. - #[prost(uint32, tag = "3")] pub expiry_secs: u32, } /// The response `content` for the `Bolt11Receive` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Bolt11ReceiveResponse { /// An invoice for a payment within the Lightning Network. /// With the details of the invoice, the sender has all the data necessary to send a payment /// to the recipient. - #[prost(string, tag = "1")] - pub invoice: ::prost::alloc::string::String, + pub invoice: String, /// The hex-encoded 32-byte payment hash. - #[prost(string, tag = "2")] - pub payment_hash: ::prost::alloc::string::String, + #[serde(with = "crate::serde_utils::hex_32")] + pub payment_hash: [u8; 32], /// The hex-encoded 32-byte payment secret. - #[prost(string, tag = "3")] - pub payment_secret: ::prost::alloc::string::String, + #[serde(with = "crate::serde_utils::hex_32")] + pub payment_secret: [u8; 32], } /// Return a BOLT11 payable invoice for a given payment hash. /// The inbound payment will NOT be automatically claimed upon arrival. @@ -187,495 +144,342 @@ pub struct Bolt11ReceiveResponse { /// See more: /// - /// - -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Bolt11ReceiveForHashRequest { /// The amount in millisatoshi to receive. If unset, a "zero-amount" or variable-amount invoice is returned. - #[prost(uint64, optional, tag = "1")] - pub amount_msat: ::core::option::Option, + pub amount_msat: Option, /// An optional description to attach along with the invoice. /// Will be set in the description field of the encoded payment request. - #[prost(message, optional, tag = "2")] - pub description: ::core::option::Option, + pub description: Option, /// Invoice expiry time in seconds. - #[prost(uint32, tag = "3")] pub expiry_secs: u32, /// The hex-encoded 32-byte payment hash to use for the invoice. - #[prost(string, tag = "4")] - pub payment_hash: ::prost::alloc::string::String, + #[serde(with = "crate::serde_utils::hex_32")] + pub payment_hash: [u8; 32], } /// The response `content` for the `Bolt11ReceiveForHash` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Bolt11ReceiveForHashResponse { /// An invoice for a payment within the Lightning Network. /// With the details of the invoice, the sender has all the data necessary to send a payment /// to the recipient. - #[prost(string, tag = "1")] - pub invoice: ::prost::alloc::string::String, + pub invoice: String, } /// Manually claim a payment for a given payment hash with the corresponding preimage. /// This should be used to claim payments created via `Bolt11ReceiveForHash`. /// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Bolt11ClaimForHashRequest { /// The hex-encoded 32-byte payment hash. /// If provided, it will be used to verify that the preimage matches. - #[prost(string, optional, tag = "1")] - pub payment_hash: ::core::option::Option<::prost::alloc::string::String>, + #[serde(default, with = "crate::serde_utils::opt_hex_32")] + pub payment_hash: Option<[u8; 32]>, /// The amount in millisatoshi that is claimable. /// If not provided, skips amount verification. - #[prost(uint64, optional, tag = "2")] - pub claimable_amount_msat: ::core::option::Option, + pub claimable_amount_msat: Option, /// The hex-encoded 32-byte payment preimage. - #[prost(string, tag = "3")] - pub preimage: ::prost::alloc::string::String, + #[serde(with = "crate::serde_utils::hex_32")] + pub preimage: [u8; 32], } /// The response `content` for the `Bolt11ClaimForHash` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Bolt11ClaimForHashResponse {} /// Manually fail a payment for a given payment hash. /// This should be used to reject payments created via `Bolt11ReceiveForHash`. /// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Bolt11FailForHashRequest { /// The hex-encoded 32-byte payment hash. - #[prost(string, tag = "1")] - pub payment_hash: ::prost::alloc::string::String, + #[serde(with = "crate::serde_utils::hex_32")] + pub payment_hash: [u8; 32], } /// The response `content` for the `Bolt11FailForHash` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Bolt11FailForHashResponse {} /// Return a BOLT11 payable invoice that can be used to request and receive a payment via an /// LSPS2 just-in-time channel. /// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Bolt11ReceiveViaJitChannelRequest { /// The amount in millisatoshi to request. - #[prost(uint64, tag = "1")] pub amount_msat: u64, /// An optional description to attach along with the invoice. /// Will be set in the description field of the encoded payment request. - #[prost(message, optional, tag = "2")] - pub description: ::core::option::Option, + pub description: Option, /// Invoice expiry time in seconds. - #[prost(uint32, tag = "3")] pub expiry_secs: u32, /// Optional upper bound for the total fee an LSP may deduct when opening the JIT channel. - #[prost(uint64, optional, tag = "4")] - pub max_total_lsp_fee_limit_msat: ::core::option::Option, + pub max_total_lsp_fee_limit_msat: Option, } /// The response `content` for the `Bolt11ReceiveViaJitChannel` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Bolt11ReceiveViaJitChannelResponse { /// An invoice for a payment within the Lightning Network. - #[prost(string, tag = "1")] - pub invoice: ::prost::alloc::string::String, + pub invoice: String, } /// Return a variable-amount BOLT11 invoice that can be used to receive a payment via an LSPS2 /// just-in-time channel. /// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Bolt11ReceiveVariableAmountViaJitChannelRequest { /// An optional description to attach along with the invoice. /// Will be set in the description field of the encoded payment request. - #[prost(message, optional, tag = "1")] - pub description: ::core::option::Option, + pub description: Option, /// Invoice expiry time in seconds. - #[prost(uint32, tag = "2")] pub expiry_secs: u32, /// Optional upper bound for the proportional fee, in parts-per-million millisatoshis, that an /// LSP may deduct when opening the JIT channel. - #[prost(uint64, optional, tag = "3")] - pub max_proportional_lsp_fee_limit_ppm_msat: ::core::option::Option, + pub max_proportional_lsp_fee_limit_ppm_msat: Option, } /// The response `content` for the `Bolt11ReceiveVariableAmountViaJitChannel` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Bolt11ReceiveVariableAmountViaJitChannelResponse { /// An invoice for a payment within the Lightning Network. - #[prost(string, tag = "1")] - pub invoice: ::prost::alloc::string::String, + pub invoice: String, } /// Send a payment for a BOLT11 invoice. /// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Bolt11SendRequest { /// An invoice for a payment within the Lightning Network. - #[prost(string, tag = "1")] - pub invoice: ::prost::alloc::string::String, + pub invoice: String, /// Set this field when paying a so-called "zero-amount" invoice, i.e., an invoice that leaves the /// amount paid to be determined by the user. /// This operation will fail if the amount specified is less than the value required by the given invoice. - #[prost(uint64, optional, tag = "2")] - pub amount_msat: ::core::option::Option, + pub amount_msat: Option, /// Configuration options for payment routing and pathfinding. - #[prost(message, optional, tag = "3")] - pub route_parameters: ::core::option::Option, + pub route_parameters: Option, } /// The response `content` for the `Bolt11Send` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Bolt11SendResponse { /// An identifier used to uniquely identify a payment in hex-encoded form. - #[prost(string, tag = "1")] - pub payment_id: ::prost::alloc::string::String, + #[serde(with = "crate::serde_utils::hex_32")] + pub payment_id: [u8; 32], } /// Returns a BOLT12 offer for the given amount, if specified. /// /// See more: /// - /// - -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Bolt12ReceiveRequest { /// An optional description to attach along with the offer. /// Will be set in the description field of the encoded offer. - #[prost(string, tag = "1")] - pub description: ::prost::alloc::string::String, + pub description: String, /// The amount in millisatoshi to send. If unset, a "zero-amount" or variable-amount offer is returned. - #[prost(uint64, optional, tag = "2")] - pub amount_msat: ::core::option::Option, + pub amount_msat: Option, /// Offer expiry time in seconds. - #[prost(uint32, optional, tag = "3")] - pub expiry_secs: ::core::option::Option, + pub expiry_secs: Option, /// If set, it represents the number of items requested, can only be set for fixed-amount offers. - #[prost(uint64, optional, tag = "4")] - pub quantity: ::core::option::Option, + pub quantity: Option, } /// The response `content` for the `Bolt12Receive` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Bolt12ReceiveResponse { /// An offer for a payment within the Lightning Network. /// With the details of the offer, the sender has all the data necessary to send a payment /// to the recipient. - #[prost(string, tag = "1")] - pub offer: ::prost::alloc::string::String, + pub offer: String, /// The hex-encoded offer id. - #[prost(string, tag = "2")] - pub offer_id: ::prost::alloc::string::String, + #[serde(with = "crate::serde_utils::hex_32")] + pub offer_id: [u8; 32], } /// Send a payment for a BOLT12 offer. /// See more: /// - /// - -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Bolt12SendRequest { /// An offer for a payment within the Lightning Network. - #[prost(string, tag = "1")] - pub offer: ::prost::alloc::string::String, + pub offer: String, /// Set this field when paying a so-called "zero-amount" offer, i.e., an offer that leaves the /// amount paid to be determined by the user. /// This operation will fail if the amount specified is less than the value required by the given offer. - #[prost(uint64, optional, tag = "2")] - pub amount_msat: ::core::option::Option, + pub amount_msat: Option, /// If set, it represents the number of items requested. - #[prost(uint64, optional, tag = "3")] - pub quantity: ::core::option::Option, + pub quantity: Option, /// If set, it will be seen by the recipient and reflected back in the invoice. - #[prost(string, optional, tag = "4")] - pub payer_note: ::core::option::Option<::prost::alloc::string::String>, + pub payer_note: Option, /// Configuration options for payment routing and pathfinding. - #[prost(message, optional, tag = "5")] - pub route_parameters: ::core::option::Option, + pub route_parameters: Option, } /// The response `content` for the `Bolt12Send` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Bolt12SendResponse { /// An identifier used to uniquely identify a payment in hex-encoded form. - #[prost(string, tag = "1")] - pub payment_id: ::prost::alloc::string::String, + #[serde(with = "crate::serde_utils::hex_32")] + pub payment_id: [u8; 32], } /// Send a spontaneous payment, also known as "keysend", to a node. /// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct SpontaneousSendRequest { /// The amount in millisatoshis to send. - #[prost(uint64, tag = "1")] pub amount_msat: u64, /// The hex-encoded public key of the node to send the payment to. - #[prost(string, tag = "2")] - pub node_id: ::prost::alloc::string::String, + #[serde(with = "crate::serde_utils::hex_33")] + pub node_id: [u8; 33], /// Configuration options for payment routing and pathfinding. - #[prost(message, optional, tag = "3")] - pub route_parameters: ::core::option::Option, + pub route_parameters: Option, } /// The response `content` for the `SpontaneousSend` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct SpontaneousSendResponse { /// An identifier used to uniquely identify a payment in hex-encoded form. - #[prost(string, tag = "1")] - pub payment_id: ::prost::alloc::string::String, + #[serde(with = "crate::serde_utils::hex_32")] + pub payment_id: [u8; 32], } /// Creates a new outbound channel to the given remote node. /// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct OpenChannelRequest { /// The hex-encoded public key of the node to open a channel with. - #[prost(string, tag = "1")] - pub node_pubkey: ::prost::alloc::string::String, + #[serde(with = "crate::serde_utils::hex_33")] + pub node_pubkey: [u8; 33], /// An address which can be used to connect to a remote peer. /// It can be of type IPv4:port, IPv6:port, OnionV3:port or hostname:port - #[prost(string, tag = "2")] - pub address: ::prost::alloc::string::String, + pub address: String, /// The amount of satoshis the caller is willing to commit to the channel. - #[prost(uint64, tag = "3")] pub channel_amount_sats: u64, /// The amount of satoshis to push to the remote side as part of the initial commitment state. - #[prost(uint64, optional, tag = "4")] - pub push_to_counterparty_msat: ::core::option::Option, + pub push_to_counterparty_msat: Option, /// The channel configuration to be used for opening this channel. If unset, default ChannelConfig is used. - #[prost(message, optional, tag = "5")] - pub channel_config: ::core::option::Option, + pub channel_config: Option, /// Whether the channel should be public. - #[prost(bool, tag = "6")] pub announce_channel: bool, } /// The response `content` for the `OpenChannel` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct OpenChannelResponse { /// The local channel id of the created channel that user can use to refer to channel. - #[prost(string, tag = "1")] - pub user_channel_id: ::prost::alloc::string::String, + pub user_channel_id: String, } /// Increases the channel balance by the given amount. /// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct SpliceInRequest { /// The local `user_channel_id` of the channel. - #[prost(string, tag = "1")] - pub user_channel_id: ::prost::alloc::string::String, + pub user_channel_id: String, /// The hex-encoded public key of the channel's counterparty node. - #[prost(string, tag = "2")] - pub counterparty_node_id: ::prost::alloc::string::String, + #[serde(with = "crate::serde_utils::hex_33")] + pub counterparty_node_id: [u8; 33], /// The amount of sats to splice into the channel. - #[prost(uint64, tag = "3")] pub splice_amount_sats: u64, } /// The response `content` for the `SpliceIn` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct SpliceInResponse {} /// Decreases the channel balance by the given amount. /// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct SpliceOutRequest { /// The local `user_channel_id` of this channel. - #[prost(string, tag = "1")] - pub user_channel_id: ::prost::alloc::string::String, + pub user_channel_id: String, /// The hex-encoded public key of the channel's counterparty node. - #[prost(string, tag = "2")] - pub counterparty_node_id: ::prost::alloc::string::String, + #[serde(with = "crate::serde_utils::hex_33")] + pub counterparty_node_id: [u8; 33], /// A Bitcoin on-chain address to send the spliced-out funds. /// /// If not set, an address from the node's on-chain wallet will be used. - #[prost(string, optional, tag = "3")] - pub address: ::core::option::Option<::prost::alloc::string::String>, + pub address: Option, /// The amount of sats to splice out of the channel. - #[prost(uint64, tag = "4")] pub splice_amount_sats: u64, } /// The response `content` for the `SpliceOut` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct SpliceOutResponse { /// The Bitcoin on-chain address where the funds will be sent. - #[prost(string, tag = "1")] - pub address: ::prost::alloc::string::String, + pub address: String, } /// Update the config for a previously opened channel. /// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct UpdateChannelConfigRequest { /// The local `user_channel_id` of this channel. - #[prost(string, tag = "1")] - pub user_channel_id: ::prost::alloc::string::String, + pub user_channel_id: String, /// The hex-encoded public key of the counterparty node to update channel config with. - #[prost(string, tag = "2")] - pub counterparty_node_id: ::prost::alloc::string::String, + #[serde(with = "crate::serde_utils::hex_33")] + pub counterparty_node_id: [u8; 33], /// The updated channel configuration settings for a channel. - #[prost(message, optional, tag = "3")] - pub channel_config: ::core::option::Option, + pub channel_config: Option, } /// The response `content` for the `UpdateChannelConfig` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct UpdateChannelConfigResponse {} /// Closes the channel specified by given request. /// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct CloseChannelRequest { /// The local `user_channel_id` of this channel. - #[prost(string, tag = "1")] - pub user_channel_id: ::prost::alloc::string::String, + pub user_channel_id: String, /// The hex-encoded public key of the node to close a channel with. - #[prost(string, tag = "2")] - pub counterparty_node_id: ::prost::alloc::string::String, + #[serde(with = "crate::serde_utils::hex_33")] + pub counterparty_node_id: [u8; 33], } /// The response `content` for the `CloseChannel` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct CloseChannelResponse {} /// Force closes the channel specified by given request. /// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct ForceCloseChannelRequest { /// The local `user_channel_id` of this channel. - #[prost(string, tag = "1")] - pub user_channel_id: ::prost::alloc::string::String, + pub user_channel_id: String, /// The hex-encoded public key of the node to close a channel with. - #[prost(string, tag = "2")] - pub counterparty_node_id: ::prost::alloc::string::String, + #[serde(with = "crate::serde_utils::hex_33")] + pub counterparty_node_id: [u8; 33], /// The reason for force-closing. - #[prost(string, optional, tag = "3")] - pub force_close_reason: ::core::option::Option<::prost::alloc::string::String>, + pub force_close_reason: Option, } /// The response `content` for the `ForceCloseChannel` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct ForceCloseChannelResponse {} /// Returns a list of known channels. /// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct ListChannelsRequest {} /// The response `content` for the `ListChannels` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct ListChannelsResponse { /// List of channels. - #[prost(message, repeated, tag = "1")] - pub channels: ::prost::alloc::vec::Vec, + pub channels: Vec, } /// Returns payment details for a given payment_id. /// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct GetPaymentDetailsRequest { /// An identifier used to uniquely identify a payment in hex-encoded form. - #[prost(string, tag = "1")] - pub payment_id: ::prost::alloc::string::String, + #[serde(with = "crate::serde_utils::hex_32")] + pub payment_id: [u8; 32], } /// The response `content` for the `GetPaymentDetails` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct GetPaymentDetailsResponse { /// Represents a payment. /// Will be `None` if payment doesn't exist. - #[prost(message, optional, tag = "1")] - pub payment: ::core::option::Option, + pub payment: Option, } /// Retrieves list of all payments. /// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct ListPaymentsRequest { /// `page_token` is a pagination token. /// @@ -683,19 +487,14 @@ pub struct ListPaymentsRequest { /// /// For subsequent pages, use the value that was returned as `next_page_token` in the previous /// page's response. - #[prost(message, optional, tag = "1")] - pub page_token: ::core::option::Option, + pub page_token: Option, } /// The response `content` for the `ListPayments` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct ListPaymentsResponse { /// List of payments. - #[prost(message, repeated, tag = "1")] - pub payments: ::prost::alloc::vec::Vec, + pub payments: Vec, /// `next_page_token` is a pagination token, used to retrieve the next page of results. /// Use this value to query for next-page of paginated operation, by specifying /// this value as the `page_token` in the next request. @@ -709,15 +508,11 @@ pub struct ListPaymentsResponse { /// /// **Caution**: Clients must not assume a specific number of records to be present in a page for /// paginated response. - #[prost(message, optional, tag = "2")] - pub next_page_token: ::core::option::Option, + pub next_page_token: Option, } /// Retrieves list of all forwarded payments. /// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct ListForwardedPaymentsRequest { /// `page_token` is a pagination token. /// @@ -725,19 +520,14 @@ pub struct ListForwardedPaymentsRequest { /// /// For subsequent pages, use the value that was returned as `next_page_token` in the previous /// page's response. - #[prost(message, optional, tag = "1")] - pub page_token: ::core::option::Option, + pub page_token: Option, } /// The response `content` for the `ListForwardedPayments` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct ListForwardedPaymentsResponse { /// List of forwarded payments. - #[prost(message, repeated, tag = "1")] - pub forwarded_payments: ::prost::alloc::vec::Vec, + pub forwarded_payments: Vec, /// `next_page_token` is a pagination token, used to retrieve the next page of results. /// Use this value to query for next-page of paginated operation, by specifying /// this value as the `page_token` in the next request. @@ -751,109 +541,77 @@ pub struct ListForwardedPaymentsResponse { /// /// **Caution**: Clients must not assume a specific number of records to be present in a page for /// paginated response. - #[prost(message, optional, tag = "2")] - pub next_page_token: ::core::option::Option, + pub next_page_token: Option, } /// Sign a message with the node's secret key. /// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct SignMessageRequest { /// The message to sign, as raw bytes. - #[prost(bytes = "bytes", tag = "1")] - pub message: ::prost::bytes::Bytes, + #[serde(with = "crate::serde_utils::bytes_hex")] + pub message: Vec, } /// The response `content` for the `SignMessage` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct SignMessageResponse { /// The signature of the message, as a zbase32-encoded string. - #[prost(string, tag = "1")] - pub signature: ::prost::alloc::string::String, + pub signature: String, } /// Verify a signature against a message and public key. /// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct VerifySignatureRequest { /// The message that was signed, as raw bytes. - #[prost(bytes = "bytes", tag = "1")] - pub message: ::prost::bytes::Bytes, + #[serde(with = "crate::serde_utils::bytes_hex")] + pub message: Vec, /// The signature to verify, as a zbase32-encoded string. - #[prost(string, tag = "2")] - pub signature: ::prost::alloc::string::String, + pub signature: String, /// The hex-encoded public key of the signer. - #[prost(string, tag = "3")] - pub public_key: ::prost::alloc::string::String, + #[serde(with = "crate::serde_utils::hex_33")] + pub public_key: [u8; 33], } /// The response `content` for the `VerifySignature` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct VerifySignatureResponse { /// Whether the signature is valid. - #[prost(bool, tag = "1")] pub valid: bool, } /// Export the pathfinding scores used by the router. /// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct ExportPathfindingScoresRequest {} /// The response `content` for the `ExportPathfindingScores` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct ExportPathfindingScoresResponse { /// The serialized pathfinding scores data. - #[prost(bytes = "bytes", tag = "1")] - pub scores: ::prost::bytes::Bytes, + #[serde(with = "crate::serde_utils::bytes_hex")] + pub scores: Vec, } /// Retrieves an overview of all known balances. /// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct GetBalancesRequest {} /// The response `content` for the `GetBalances` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct GetBalancesResponse { /// The total balance of our on-chain wallet. - #[prost(uint64, tag = "1")] pub total_onchain_balance_sats: u64, /// The currently spendable balance of our on-chain wallet. /// /// This includes any sufficiently confirmed funds, minus `total_anchor_channels_reserve_sats`. - #[prost(uint64, tag = "2")] pub spendable_onchain_balance_sats: u64, /// The share of our total balance that we retain as an emergency reserve to (hopefully) be /// able to spend the Anchor outputs when one of our channels is closed. - #[prost(uint64, tag = "3")] pub total_anchor_channels_reserve_sats: u64, /// The total balance that we would be able to claim across all our Lightning channels. /// /// Note this excludes balances that we are unsure if we are able to claim (e.g., as we are /// waiting for a preimage or for a timeout to expire). These balances will however be included /// as `MaybePreimageClaimableHTLC` and `MaybeTimeoutClaimableHTLC` in `lightning_balances`. - #[prost(uint64, tag = "4")] pub total_lightning_balance_sats: u64, /// A detailed list of all known Lightning balances that would be claimable on channel closure. /// @@ -861,8 +619,7 @@ pub struct GetBalancesResponse { /// restrictions apply. Please refer to `Channel::outbound_capacity_msat` and /// Channel::next_outbound_htlc_limit_msat as returned by `ListChannels` /// for a better approximation of the spendable amounts. - #[prost(message, repeated, tag = "5")] - pub lightning_balances: ::prost::alloc::vec::Vec, + pub lightning_balances: Vec, /// A detailed list of balances currently being swept from the Lightning to the on-chain /// wallet. /// @@ -871,129 +628,84 @@ pub struct GetBalancesResponse { /// /// Note that, depending on the sync status of the wallets, swept balances listed here might or /// might not already be accounted for in `total_onchain_balance_sats`. - #[prost(message, repeated, tag = "6")] - pub pending_balances_from_channel_closures: - ::prost::alloc::vec::Vec, + pub pending_balances_from_channel_closures: Vec, } /// Connect to a peer on the Lightning Network. /// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct ConnectPeerRequest { /// The hex-encoded public key of the node to connect to. - #[prost(string, tag = "1")] - pub node_pubkey: ::prost::alloc::string::String, + #[serde(with = "crate::serde_utils::hex_33")] + pub node_pubkey: [u8; 33], /// An address which can be used to connect to a remote peer. /// It can be of type IPv4:port, IPv6:port, OnionV3:port or hostname:port - #[prost(string, tag = "2")] - pub address: ::prost::alloc::string::String, + pub address: String, /// Whether to persist the peer connection, i.e., whether the peer will be re-connected on /// restart. - #[prost(bool, tag = "3")] pub persist: bool, } /// The response `content` for the `ConnectPeer` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct ConnectPeerResponse {} /// Disconnect from a peer and remove it from the peer store. /// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct DisconnectPeerRequest { /// The hex-encoded public key of the node to disconnect from. - #[prost(string, tag = "1")] - pub node_pubkey: ::prost::alloc::string::String, + #[serde(with = "crate::serde_utils::hex_33")] + pub node_pubkey: [u8; 33], } /// The response `content` for the `DisconnectPeer` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct DisconnectPeerResponse {} /// Returns a list of peers. /// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct ListPeersRequest {} /// The response `content` for the `ListPeers` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct ListPeersResponse { /// List of peers. - #[prost(message, repeated, tag = "1")] - pub peers: ::prost::alloc::vec::Vec, + pub peers: Vec, } /// Returns a list of all known short channel IDs in the network graph. /// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct GraphListChannelsRequest {} /// The response `content` for the `GraphListChannels` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct GraphListChannelsResponse { /// List of short channel IDs known to the network graph. - #[prost(uint64, repeated, tag = "1")] - pub short_channel_ids: ::prost::alloc::vec::Vec, + pub short_channel_ids: Vec, } /// Returns information on a channel with the given short channel ID from the network graph. /// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct GraphGetChannelRequest { /// The short channel ID to look up. - #[prost(uint64, tag = "1")] pub short_channel_id: u64, } /// The response `content` for the `GraphGetChannel` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct GraphGetChannelResponse { /// The channel information. - #[prost(message, optional, tag = "1")] - pub channel: ::core::option::Option, + pub channel: Option, } /// Returns a list of all known node IDs in the network graph. /// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct GraphListNodesRequest {} /// The response `content` for the `GraphListNodes` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct GraphListNodesResponse { /// List of hex-encoded node IDs known to the network graph. - #[prost(string, repeated, tag = "1")] - pub node_ids: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + pub node_ids: Vec, } /// Send a payment given a BIP 21 URI or BIP 353 Human-Readable Name. /// @@ -1001,69 +713,48 @@ pub struct GraphListNodesResponse { /// has an offer and/or invoice, it will try to pay the offer first followed by the invoice. /// If they both fail, the on-chain payment will be paid. /// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct UnifiedSendRequest { /// A BIP 21 URI or BIP 353 Human-Readable Name to pay. - #[prost(string, tag = "1")] - pub uri: ::prost::alloc::string::String, + pub uri: String, /// The amount in millisatoshis to send. Required for "zero-amount" or variable-amount URIs. - #[prost(uint64, optional, tag = "2")] - pub amount_msat: ::core::option::Option, + pub amount_msat: Option, /// Configuration options for payment routing and pathfinding. - #[prost(message, optional, tag = "3")] - pub route_parameters: ::core::option::Option, + pub route_parameters: Option, } /// The response `content` for the `UnifiedSend` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct UnifiedSendResponse { - #[prost(oneof = "unified_send_response::PaymentResult", tags = "1, 2, 3")] - #[cfg_attr(feature = "serde", serde(flatten))] - pub payment_result: ::core::option::Option, + #[serde(flatten)] + pub payment_result: Option, } -/// Nested message and enum types in `UnifiedSendResponse`. -pub mod unified_send_response { - #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] - #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] - #[allow(clippy::derive_partial_eq_without_eq)] - #[derive(Clone, PartialEq, ::prost::Oneof)] - pub enum PaymentResult { - /// An on-chain payment was made. Contains the transaction ID. - #[prost(string, tag = "1")] - Txid(::prost::alloc::string::String), - /// A BOLT11 payment was made. Contains the payment ID in hex-encoded form. - #[prost(string, tag = "2")] - Bolt11PaymentId(::prost::alloc::string::String), - /// A BOLT12 payment was made. Contains the payment ID in hex-encoded form. - #[prost(string, tag = "3")] - Bolt12PaymentId(::prost::alloc::string::String), - } +/// The payment result of a unified send operation. +/// +/// Note: Variants use `String` instead of `[u8; 32]` because `#[serde(with)]` +/// is not supported on enum tuple variants. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum UnifiedSendPaymentResult { + /// An on-chain payment was made. Contains the hex-encoded transaction ID. + Txid(String), + /// A BOLT11 payment was made. Contains the hex-encoded payment ID. + Bolt11PaymentId(String), + /// A BOLT12 payment was made. Contains the payment ID in hex-encoded form. + Bolt12PaymentId(String), } /// Returns information on a node with the given ID from the network graph. /// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct GraphGetNodeRequest { /// The hex-encoded node ID to look up. - #[prost(string, tag = "1")] - pub node_id: ::prost::alloc::string::String, + #[serde(with = "crate::serde_utils::hex_33")] + pub node_id: [u8; 33], } /// The response `content` for the `GraphGetNode` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct GraphGetNodeResponse { /// The node information. - #[prost(message, optional, tag = "1")] - pub node: ::core::option::Option, + pub node: Option, } diff --git a/ldk-server-protos/src/endpoints.rs b/ldk-server-json-models/src/endpoints.rs similarity index 100% rename from ldk-server-protos/src/endpoints.rs rename to ldk-server-json-models/src/endpoints.rs diff --git a/ldk-server-json-models/src/error.rs b/ldk-server-json-models/src/error.rs new file mode 100644 index 00000000..2ee3fd0f --- /dev/null +++ b/ldk-server-json-models/src/error.rs @@ -0,0 +1,42 @@ +// This file is Copyright its original authors, visible in version control +// history. +// +// This file is licensed under the Apache License, Version 2.0 or the MIT license +// , at your option. +// You may not use this file except in accordance with one or both of these +// licenses. + +use serde::{Deserialize, Serialize}; + +/// When HttpStatusCode is not ok (200), the response `content` contains a serialized `ErrorResponse` +/// with the relevant ErrorCode and `message` +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct ErrorResponse { + /// The error message containing a generic description of the error condition in English. + /// It is intended for a human audience only and should not be parsed to extract any information + /// programmatically. Client-side code may use it for logging only. + pub message: String, + /// The error code uniquely identifying an error condition. + /// It is meant to be read and understood programmatically by code that detects/handles errors by + /// type. + pub error_code: ErrorCode, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)] +pub enum ErrorCode { + /// Will never be used as `error_code` by server. + UnknownError, + /// Used in the following cases: + /// - The request was missing a required argument. + /// - The specified argument was invalid, incomplete or in the wrong format. + /// - The request body of api cannot be deserialized. + /// - The request does not follow api contract. + InvalidRequestError, + /// Used when authentication fails or in case of an unauthorized request. + AuthError, + /// Used to represent an error while doing a Lightning operation. + LightningError, + /// Used when an internal server error occurred. The client is probably at no fault. + InternalServerError, +} diff --git a/ldk-server-json-models/src/events.rs b/ldk-server-json-models/src/events.rs new file mode 100644 index 00000000..713951f7 --- /dev/null +++ b/ldk-server-json-models/src/events.rs @@ -0,0 +1,59 @@ +// This file is Copyright its original authors, visible in version control +// history. +// +// This file is licensed under the Apache License, Version 2.0 or the MIT license +// , at your option. +// You may not use this file except in accordance with one or both of these +// licenses. + +use serde::{Deserialize, Serialize}; + +/// An event emitted by the LDK Server to notify consumers of payment lifecycle changes. +/// +/// Events are published to the configured messaging system (e.g., RabbitMQ) as JSON. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum Event { + PaymentReceived(PaymentReceived), + PaymentSuccessful(PaymentSuccessful), + PaymentFailed(PaymentFailed), + PaymentForwarded(PaymentForwarded), + PaymentClaimable(PaymentClaimable), +} + +/// PaymentReceived indicates a payment has been received. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct PaymentReceived { + /// The payment details for the received payment. + pub payment: super::types::Payment, +} + +/// PaymentSuccessful indicates a sent payment was successful. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct PaymentSuccessful { + /// The payment details for the successful payment. + pub payment: super::types::Payment, +} + +/// PaymentFailed indicates a sent payment has failed. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct PaymentFailed { + /// The payment details for the failed payment. + pub payment: super::types::Payment, +} + +/// PaymentClaimable indicates a payment has arrived and is waiting to be manually claimed or failed. +/// This event is only emitted for payments created via `Bolt11ReceiveForHash`. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct PaymentClaimable { + /// The payment details for the claimable payment. + pub payment: super::types::Payment, +} + +/// PaymentForwarded indicates a payment was forwarded through the node. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct PaymentForwarded { + /// The forwarded payment details. + pub forwarded_payment: super::types::ForwardedPayment, +} diff --git a/ldk-server-protos/src/lib.rs b/ldk-server-json-models/src/lib.rs similarity index 95% rename from ldk-server-protos/src/lib.rs rename to ldk-server-json-models/src/lib.rs index f76f2f73..9b8bed3d 100644 --- a/ldk-server-protos/src/lib.rs +++ b/ldk-server-json-models/src/lib.rs @@ -11,6 +11,5 @@ pub mod api; pub mod endpoints; pub mod error; pub mod events; -#[cfg(feature = "serde")] pub mod serde_utils; pub mod types; diff --git a/ldk-server-json-models/src/serde_utils.rs b/ldk-server-json-models/src/serde_utils.rs new file mode 100644 index 00000000..5fb88ebe --- /dev/null +++ b/ldk-server-json-models/src/serde_utils.rs @@ -0,0 +1,143 @@ +// This file is Copyright its original authors, visible in version control +// history. +// +// This file is licensed under the Apache License, Version 2.0 or the MIT license +// , at your option. +// You may not use this file except in accordance with one or both of these +// licenses. + +//! Custom serde serializers/deserializers for byte fields. +//! +//! These are used via `#[serde(with = "...")]` attributes on fields to produce +//! human-readable hex output for byte data. + +use std::fmt::Write; + +use serde::{Deserialize, Deserializer, Serializer}; + +/// Module for serializing/deserializing `Vec` as a hex string. +pub mod bytes_hex { + use super::*; + + pub fn serialize(value: &[u8], serializer: S) -> Result + where + S: Serializer, + { + let hex = value.iter().fold(String::with_capacity(value.len() * 2), |mut acc, b| { + let _ = write!(acc, "{b:02x}"); + acc + }); + serializer.serialize_str(&hex) + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + let hex = String::deserialize(deserializer)?; + hex_to_bytes(&hex).map_err(serde::de::Error::custom) + } +} + +/// Module for serializing/deserializing `[u8; 32]` as a hex string. +pub mod hex_32 { + use super::*; + + pub fn serialize(value: &[u8; 32], serializer: S) -> Result + where + S: Serializer, + { + let hex = value.iter().fold(String::with_capacity(64), |mut acc, b| { + let _ = write!(acc, "{b:02x}"); + acc + }); + serializer.serialize_str(&hex) + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result<[u8; 32], D::Error> + where + D: Deserializer<'de>, + { + let hex = String::deserialize(deserializer)?; + hex_to_32_bytes(&hex).map_err(serde::de::Error::custom) + } +} + +/// Module for serializing/deserializing `Option<[u8; 32]>` as a hex string (or null). +pub mod opt_hex_32 { + use super::*; + + pub fn serialize(value: &Option<[u8; 32]>, serializer: S) -> Result + where + S: Serializer, + { + match value { + Some(bytes) => { + let hex = bytes.iter().fold(String::with_capacity(64), |mut acc, b| { + let _ = write!(acc, "{b:02x}"); + acc + }); + serializer.serialize_some(&hex) + }, + None => serializer.serialize_none(), + } + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + let opt: Option = Option::deserialize(deserializer)?; + match opt { + Some(hex) => { + let bytes = hex_to_32_bytes(&hex).map_err(serde::de::Error::custom)?; + Ok(Some(bytes)) + }, + None => Ok(None), + } + } +} + +fn hex_to_bytes(hex: &str) -> Result, String> { + if hex.len() % 2 != 0 { + return Err("Hex string must have even length".to_string()); + } + (0..hex.len()) + .step_by(2) + .map(|i| u8::from_str_radix(&hex[i..i + 2], 16).map_err(|e| e.to_string())) + .collect() +} + +/// Module for serializing/deserializing `[u8; 33]` as a hex string. +pub mod hex_33 { + use super::*; + + pub fn serialize(value: &[u8; 33], serializer: S) -> Result + where + S: Serializer, + { + let hex = value.iter().fold(String::with_capacity(66), |mut acc, b| { + let _ = write!(acc, "{b:02x}"); + acc + }); + serializer.serialize_str(&hex) + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result<[u8; 33], D::Error> + where + D: Deserializer<'de>, + { + let hex = String::deserialize(deserializer)?; + hex_to_fixed::<33>(&hex).map_err(serde::de::Error::custom) + } +} + +fn hex_to_fixed(hex: &str) -> Result<[u8; N], String> { + let bytes = hex_to_bytes(hex)?; + bytes.try_into().map_err(|v: Vec| format!("expected {} bytes, got {}", N, v.len())) +} + +fn hex_to_32_bytes(hex: &str) -> Result<[u8; 32], String> { + hex_to_fixed::<32>(hex) +} diff --git a/ldk-server-json-models/src/types.rs b/ldk-server-json-models/src/types.rs new file mode 100644 index 00000000..a607a461 --- /dev/null +++ b/ldk-server-json-models/src/types.rs @@ -0,0 +1,756 @@ +// This file is Copyright its original authors, visible in version control +// history. +// +// This file is licensed under the Apache License, Version 2.0 or the MIT license +// , at your option. +// You may not use this file except in accordance with one or both of these +// licenses. + +use serde::{Deserialize, Serialize}; + +/// Represents a payment. +/// See more: +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Payment { + /// An identifier used to uniquely identify a payment in hex-encoded form. + #[serde(with = "crate::serde_utils::hex_32")] + pub id: [u8; 32], + /// The kind of the payment. + pub kind: PaymentKind, + /// The amount transferred. + pub amount_msat: Option, + /// The fees that were paid for this payment. + /// + /// For Lightning payments, this will only be updated for outbound payments once they + /// succeeded. + pub fee_paid_msat: Option, + /// The direction of the payment. + pub direction: PaymentDirection, + /// The status of the payment. + pub status: PaymentStatus, + /// The timestamp, in seconds since start of the UNIX epoch, when this entry was last updated. + pub latest_update_timestamp: u64, +} +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum PaymentKind { + Onchain(Onchain), + Bolt11(Bolt11), + Bolt11Jit(Bolt11Jit), + Bolt12Offer(Bolt12Offer), + Bolt12Refund(Bolt12Refund), + Spontaneous(Spontaneous), +} +/// Represents an on-chain payment. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Onchain { + /// The transaction identifier of this payment. + #[serde(with = "crate::serde_utils::hex_32")] + pub txid: [u8; 32], + /// The confirmation status of this payment. + pub status: ConfirmationStatus, +} +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum ConfirmationStatus { + Confirmed(Confirmed), + Unconfirmed(Unconfirmed), +} +/// The on-chain transaction is confirmed in the best chain. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Confirmed { + /// The hex representation of hash of the block in which the transaction was confirmed. + #[serde(with = "crate::serde_utils::hex_32")] + pub block_hash: [u8; 32], + /// The height under which the block was confirmed. + pub height: u32, + /// The timestamp, in seconds since start of the UNIX epoch, when this entry was last updated. + pub timestamp: u64, +} +/// The on-chain transaction is unconfirmed. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Unconfirmed {} +/// Represents a BOLT 11 payment. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Bolt11 { + /// The payment hash, i.e., the hash of the preimage. + #[serde(with = "crate::serde_utils::hex_32")] + pub hash: [u8; 32], + /// The pre-image used by the payment. + #[serde(default, with = "crate::serde_utils::opt_hex_32")] + pub preimage: Option<[u8; 32]>, + /// The secret used by the payment. + #[serde(default, with = "crate::serde_utils::opt_hex_32")] + pub secret: Option<[u8; 32]>, +} +/// Represents a BOLT 11 payment intended to open an LSPS 2 just-in-time channel. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Bolt11Jit { + /// The payment hash, i.e., the hash of the preimage. + #[serde(with = "crate::serde_utils::hex_32")] + pub hash: [u8; 32], + /// The pre-image used by the payment. + #[serde(default, with = "crate::serde_utils::opt_hex_32")] + pub preimage: Option<[u8; 32]>, + /// The secret used by the payment. + #[serde(default, with = "crate::serde_utils::opt_hex_32")] + pub secret: Option<[u8; 32]>, + /// Limits applying to how much fee we allow an LSP to deduct from the payment amount. + /// + /// Allowing them to deduct this fee from the first inbound payment will pay for the LSP's channel opening fees. + /// + /// See \[`LdkChannelConfig::accept_underpaying_htlcs`\]() + /// for more information. + pub lsp_fee_limits: Option, + /// The value, in thousands of a satoshi, that was deducted from this payment as an extra + /// fee taken by our channel counterparty. + /// + /// Will only be `Some` once we received the payment. + pub counterparty_skimmed_fee_msat: Option, +} +/// Represents a BOLT 12 'offer' payment, i.e., a payment for an Offer. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Bolt12Offer { + /// The payment hash, i.e., the hash of the preimage. + #[serde(default, with = "crate::serde_utils::opt_hex_32")] + pub hash: Option<[u8; 32]>, + /// The pre-image used by the payment. + #[serde(default, with = "crate::serde_utils::opt_hex_32")] + pub preimage: Option<[u8; 32]>, + /// The secret used by the payment. + #[serde(default, with = "crate::serde_utils::opt_hex_32")] + pub secret: Option<[u8; 32]>, + /// The hex-encoded ID of the offer this payment is for. + #[serde(with = "crate::serde_utils::hex_32")] + pub offer_id: [u8; 32], + /// The payer's note for the payment. + /// Truncated to \[PAYER_NOTE_LIMIT\](). + /// + /// **Caution**: The `payer_note` field may come from an untrusted source. To prevent potential misuse, + /// all non-printable characters will be sanitized and replaced with safe characters. + pub payer_note: Option, + /// The quantity of an item requested in the offer. + pub quantity: Option, +} +/// Represents a BOLT 12 'refund' payment, i.e., a payment for a Refund. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Bolt12Refund { + /// The payment hash, i.e., the hash of the preimage. + #[serde(default, with = "crate::serde_utils::opt_hex_32")] + pub hash: Option<[u8; 32]>, + /// The pre-image used by the payment. + #[serde(default, with = "crate::serde_utils::opt_hex_32")] + pub preimage: Option<[u8; 32]>, + /// The secret used by the payment. + #[serde(default, with = "crate::serde_utils::opt_hex_32")] + pub secret: Option<[u8; 32]>, + /// The payer's note for the payment. + /// Truncated to \[PAYER_NOTE_LIMIT\](). + /// + /// **Caution**: The `payer_note` field may come from an untrusted source. To prevent potential misuse, + /// all non-printable characters will be sanitized and replaced with safe characters. + pub payer_note: Option, + /// The quantity of an item requested in the offer. + pub quantity: Option, +} +/// Represents a spontaneous ("keysend") payment. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Spontaneous { + /// The payment hash, i.e., the hash of the preimage. + #[serde(with = "crate::serde_utils::hex_32")] + pub hash: [u8; 32], + /// The pre-image used by the payment. + #[serde(default, with = "crate::serde_utils::opt_hex_32")] + pub preimage: Option<[u8; 32]>, +} +/// Limits applying to how much fee we allow an LSP to deduct from the payment amount. +/// See \[`LdkChannelConfig::accept_underpaying_htlcs`\] for more information. +/// +/// \[`LdkChannelConfig::accept_underpaying_htlcs`\]: lightning::util::config::ChannelConfig::accept_underpaying_htlcs +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct LspFeeLimits { + /// The maximal total amount we allow any configured LSP withhold from us when forwarding the + /// payment. + pub max_total_opening_fee_msat: Option, + /// The maximal proportional fee, in parts-per-million millisatoshi, we allow any configured + /// LSP withhold from us when forwarding the payment. + pub max_proportional_opening_fee_ppm_msat: Option, +} +/// A forwarded payment through our node. +/// See more: +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct ForwardedPayment { + /// The channel id of the incoming channel between the previous node and us. + #[serde(with = "crate::serde_utils::hex_32")] + pub prev_channel_id: [u8; 32], + /// The channel id of the outgoing channel between the next node and us. + #[serde(with = "crate::serde_utils::hex_32")] + pub next_channel_id: [u8; 32], + /// The `user_channel_id` of the incoming channel between the previous node and us. + pub prev_user_channel_id: String, + /// The node id of the previous node. + #[serde(with = "crate::serde_utils::hex_33")] + pub prev_node_id: [u8; 33], + /// The node id of the next node. + #[serde(with = "crate::serde_utils::hex_33")] + pub next_node_id: [u8; 33], + /// The `user_channel_id` of the outgoing channel between the next node and us. + /// This will be `None` if the payment was settled via an on-chain transaction. + /// See the caveat described for the `total_fee_earned_msat` field. + pub next_user_channel_id: Option, + /// The total fee, in milli-satoshis, which was earned as a result of the payment. + /// + /// Note that if we force-closed the channel over which we forwarded an HTLC while the HTLC was pending, the amount the + /// next hop claimed will have been rounded down to the nearest whole satoshi. Thus, the fee calculated here may be + /// higher than expected as we still claimed the full value in millisatoshis from the source. + /// In this case, `claim_from_onchain_tx` will be set. + /// + /// If the channel which sent us the payment has been force-closed, we will claim the funds via an on-chain transaction. + /// In that case we do not yet know the on-chain transaction fees which we will spend and will instead set this to `None`. + pub total_fee_earned_msat: Option, + /// The share of the total fee, in milli-satoshis, which was withheld in addition to the forwarding fee. + /// This will only be set if we forwarded an intercepted HTLC with less than the expected amount. This means our + /// counterparty accepted to receive less than the invoice amount. + /// + /// The caveat described above the `total_fee_earned_msat` field applies here as well. + pub skimmed_fee_msat: Option, + /// If this is true, the forwarded HTLC was claimed by our counterparty via an on-chain transaction. + pub claim_from_onchain_tx: bool, + /// The final amount forwarded, in milli-satoshis, after the fee is deducted. + /// + /// The caveat described above the `total_fee_earned_msat` field applies here as well. + pub outbound_amount_forwarded_msat: Option, +} +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Channel { + /// The channel ID (prior to funding transaction generation, this is a random 32-byte + /// identifier, afterwards this is the transaction ID of the funding transaction XOR the + /// funding transaction output). + /// + /// Note that this means this value is *not* persistent - it can change once during the + /// lifetime of the channel. + #[serde(with = "crate::serde_utils::hex_32")] + pub channel_id: [u8; 32], + /// The node ID of our the channel's remote counterparty. + #[serde(with = "crate::serde_utils::hex_33")] + pub counterparty_node_id: [u8; 33], + /// The channel's funding transaction output, if we've negotiated the funding transaction with + /// our counterparty already. + pub funding_txo: Option, + /// The hex-encoded local `user_channel_id` of this channel. + pub user_channel_id: String, + /// The value, in satoshis, that must always be held as a reserve in the channel for us. This + /// value ensures that if we broadcast a revoked state, our counterparty can punish us by + /// claiming at least this value on chain. + /// + /// This value is not included in \[`outbound_capacity_msat`\] as it can never be spent. + /// + /// This value will be `None` for outbound channels until the counterparty accepts the channel. + pub unspendable_punishment_reserve: Option, + /// The value, in satoshis, of this channel as it appears in the funding output. + pub channel_value_sats: u64, + /// The currently negotiated fee rate denominated in satoshi per 1000 weight units, + /// which is applied to commitment and HTLC transactions. + pub feerate_sat_per_1000_weight: u32, + /// The available outbound capacity for sending HTLCs to the remote peer. + /// + /// The amount does not include any pending HTLCs which are not yet resolved (and, thus, whose + /// balance is not available for inclusion in new outbound HTLCs). This further does not include + /// any pending outgoing HTLCs which are awaiting some other resolution to be sent. + pub outbound_capacity_msat: u64, + /// The available outbound capacity for sending HTLCs to the remote peer. + /// + /// The amount does not include any pending HTLCs which are not yet resolved + /// (and, thus, whose balance is not available for inclusion in new inbound HTLCs). This further + /// does not include any pending outgoing HTLCs which are awaiting some other resolution to be + /// sent. + pub inbound_capacity_msat: u64, + /// The number of required confirmations on the funding transactions before the funding is + /// considered "locked". The amount is selected by the channel fundee. + /// + /// The value will be `None` for outbound channels until the counterparty accepts the channel. + pub confirmations_required: Option, + /// The current number of confirmations on the funding transaction. + pub confirmations: Option, + /// Is `true` if the channel was initiated (and therefore funded) by us. + pub is_outbound: bool, + /// Is `true` if both parties have exchanged `channel_ready` messages, and the channel is + /// not currently being shut down. Both parties exchange `channel_ready` messages upon + /// independently verifying that the required confirmations count provided by + /// `confirmations_required` has been reached. + pub is_channel_ready: bool, + /// Is `true` if the channel (a) `channel_ready` messages have been exchanged, (b) the + /// peer is connected, and (c) the channel is not currently negotiating shutdown. + /// + /// This is a strict superset of `is_channel_ready`. + pub is_usable: bool, + /// Is `true` if this channel is (or will be) publicly-announced + pub is_announced: bool, + /// Set of configurable parameters set by self that affect channel operation. + pub channel_config: Option, + /// The available outbound capacity for sending a single HTLC to the remote peer. This is + /// similar to `outbound_capacity_msat` but it may be further restricted by + /// the current state and per-HTLC limit(s). This is intended for use when routing, allowing us + /// to use a limit as close as possible to the HTLC limit we can currently send. + pub next_outbound_htlc_limit_msat: u64, + /// The minimum value for sending a single HTLC to the remote peer. This is the equivalent of + /// `next_outbound_htlc_limit_msat` but represents a lower-bound, rather than + /// an upper-bound. This is intended for use when routing, allowing us to ensure we pick a + /// route which is valid. + pub next_outbound_htlc_minimum_msat: u64, + /// The number of blocks (after our commitment transaction confirms) that we will need to wait + /// until we can claim our funds after we force-close the channel. During this time our + /// counterparty is allowed to punish us if we broadcasted a stale state. If our counterparty + /// force-closes the channel and broadcasts a commitment transaction we do not have to wait any + /// time to claim our non-HTLC-encumbered funds. + /// + /// This value will be `None` for outbound channels until the counterparty accepts the channel. + pub force_close_spend_delay: Option, + /// The smallest value HTLC (in msat) the remote peer will accept, for this channel. + /// + /// This field is only `None` before we have received either the `OpenChannel` or + /// `AcceptChannel` message from the remote peer. + pub counterparty_outbound_htlc_minimum_msat: Option, + /// The largest value HTLC (in msat) the remote peer currently will accept, for this channel. + pub counterparty_outbound_htlc_maximum_msat: Option, + /// The value, in satoshis, that must always be held in the channel for our counterparty. This + /// value ensures that if our counterparty broadcasts a revoked state, we can punish them by + /// claiming at least this value on chain. + /// + /// This value is not included in `inbound_capacity_msat` as it can never be spent. + pub counterparty_unspendable_punishment_reserve: u64, + /// Base routing fee in millisatoshis. + pub counterparty_forwarding_info_fee_base_msat: Option, + /// Proportional fee, in millionths of a satoshi the channel will charge per transferred satoshi. + pub counterparty_forwarding_info_fee_proportional_millionths: Option, + /// The minimum difference in CLTV expiry between an ingoing HTLC and its outgoing counterpart, + /// such that the outgoing HTLC is forwardable to this counterparty. + pub counterparty_forwarding_info_cltv_expiry_delta: Option, +} +/// ChannelConfig represents the configuration settings for a channel in a Lightning Network node. +/// See more: +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct ChannelConfig { + /// Amount (in millionths of a satoshi) charged per satoshi for payments forwarded outbound + /// over the channel. + /// See more: + pub forwarding_fee_proportional_millionths: Option, + /// Amount (in milli-satoshi) charged for payments forwarded outbound over the channel, + /// in excess of forwarding_fee_proportional_millionths. + /// See more: + pub forwarding_fee_base_msat: Option, + /// The difference in the CLTV value between incoming HTLCs and an outbound HTLC forwarded + /// over the channel this config applies to. + /// See more: + pub cltv_expiry_delta: Option, + /// The maximum additional fee we're willing to pay to avoid waiting for the counterparty's + /// to_self_delay to reclaim funds. + /// See more: + pub force_close_avoidance_max_fee_satoshis: Option, + /// If set, allows this channel's counterparty to skim an additional fee off this node's + /// inbound HTLCs. Useful for liquidity providers to offload on-chain channel costs to end users. + /// See more: + pub accept_underpaying_htlcs: Option, + /// Limit our total exposure to potential loss to on-chain fees on close, including + /// in-flight HTLCs which are burned to fees as they are too small to claim on-chain + /// and fees on commitment transaction(s) broadcasted by our counterparty in excess of + /// our own fee estimate. + /// See more: + pub max_dust_htlc_exposure: Option, +} +/// Limit our total exposure to potential loss to on-chain fees on close, including +/// in-flight HTLCs which are burned to fees as they are too small to claim on-chain +/// and fees on commitment transaction(s) broadcasted by our counterparty in excess of +/// our own fee estimate. +/// See more: +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum MaxDustHtlcExposure { + /// This sets a fixed limit on the total dust exposure in millisatoshis. + /// See more: + FixedLimitMsat(u64), + /// This sets a multiplier on the ConfirmationTarget::OnChainSweep feerate (in sats/KW) to determine the maximum allowed dust exposure. + /// See more: + FeeRateMultiplier(u64), +} +/// Represent a transaction outpoint. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct OutPoint { + /// The referenced transaction's txid. + #[serde(with = "crate::serde_utils::hex_32")] + pub txid: [u8; 32], + /// The index of the referenced output in its transaction's vout. + pub vout: u32, +} +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct BestBlock { + /// The block's hash + #[serde(with = "crate::serde_utils::hex_32")] + pub block_hash: [u8; 32], + /// The height at which the block was confirmed. + pub height: u32, +} +/// Details about the status of a known Lightning balance. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum LightningBalance { + ClaimableOnChannelClose(ClaimableOnChannelClose), + ClaimableAwaitingConfirmations(ClaimableAwaitingConfirmations), + ContentiousClaimable(ContentiousClaimable), + MaybeTimeoutClaimableHtlc(MaybeTimeoutClaimableHtlc), + MaybePreimageClaimableHtlc(MaybePreimageClaimableHtlc), + CounterpartyRevokedOutputClaimable(CounterpartyRevokedOutputClaimable), +} +/// The channel is not yet closed (or the commitment or closing transaction has not yet appeared in a block). +/// The given balance is claimable (less on-chain fees) if the channel is force-closed now. +/// See more: +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct ClaimableOnChannelClose { + /// The identifier of the channel this balance belongs to. + #[serde(with = "crate::serde_utils::hex_32")] + pub channel_id: [u8; 32], + /// The identifier of our channel counterparty. + #[serde(with = "crate::serde_utils::hex_33")] + pub counterparty_node_id: [u8; 33], + /// The amount available to claim, in satoshis, excluding the on-chain fees which will be required to do so. + pub amount_satoshis: u64, + /// The transaction fee we pay for the closing commitment transaction. + /// This amount is not included in the `amount_satoshis` value. + /// + /// Note that if this channel is inbound (and thus our counterparty pays the commitment transaction fee) this value + /// will be zero. + pub transaction_fee_satoshis: u64, + /// The amount of millisatoshis which has been burned to fees from HTLCs which are outbound from us and are related to + /// a payment which was sent by us. This is the sum of the millisatoshis part of all HTLCs which are otherwise + /// represented by `LightningBalance::MaybeTimeoutClaimableHTLC` with their + /// `LightningBalance::MaybeTimeoutClaimableHTLC::outbound_payment` flag set, as well as any dust HTLCs which would + /// otherwise be represented the same. + /// + /// This amount (rounded up to a whole satoshi value) will not be included in `amount_satoshis`. + pub outbound_payment_htlc_rounded_msat: u64, + /// The amount of millisatoshis which has been burned to fees from HTLCs which are outbound from us and are related to + /// a forwarded HTLC. This is the sum of the millisatoshis part of all HTLCs which are otherwise represented by + /// `LightningBalance::MaybeTimeoutClaimableHTLC` with their `LightningBalance::MaybeTimeoutClaimableHTLC::outbound_payment` + /// flag not set, as well as any dust HTLCs which would otherwise be represented the same. + /// + /// This amount (rounded up to a whole satoshi value) will not be included in `amount_satoshis`. + pub outbound_forwarded_htlc_rounded_msat: u64, + /// The amount of millisatoshis which has been burned to fees from HTLCs which are inbound to us and for which we know + /// the preimage. This is the sum of the millisatoshis part of all HTLCs which would be represented by + /// `LightningBalance::ContentiousClaimable` on channel close, but whose current value is included in `amount_satoshis`, + /// as well as any dust HTLCs which would otherwise be represented the same. + /// + /// This amount (rounded up to a whole satoshi value) will not be included in `amount_satoshis`. + pub inbound_claiming_htlc_rounded_msat: u64, + /// The amount of millisatoshis which has been burned to fees from HTLCs which are inbound to us and for which we do + /// not know the preimage. This is the sum of the millisatoshis part of all HTLCs which would be represented by + /// `LightningBalance::MaybePreimageClaimableHTLC` on channel close, as well as any dust HTLCs which would otherwise be + /// represented the same. + /// + /// This amount (rounded up to a whole satoshi value) will not be included in the counterparty's `amount_satoshis`. + pub inbound_htlc_rounded_msat: u64, +} +/// The channel has been closed, and the given balance is ours but awaiting confirmations until we consider it spendable. +/// See more: +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct ClaimableAwaitingConfirmations { + /// The identifier of the channel this balance belongs to. + #[serde(with = "crate::serde_utils::hex_32")] + pub channel_id: [u8; 32], + /// The identifier of our channel counterparty. + #[serde(with = "crate::serde_utils::hex_33")] + pub counterparty_node_id: [u8; 33], + /// The amount available to claim, in satoshis, possibly excluding the on-chain fees which were spent in broadcasting + /// the transaction. + pub amount_satoshis: u64, + /// The height at which we start tracking it as `SpendableOutput`. + pub confirmation_height: u32, + /// Whether this balance is a result of cooperative close, a force-close, or an HTLC. + pub source: BalanceSource, +} +/// The channel has been closed, and the given balance should be ours but awaiting spending transaction confirmation. +/// If the spending transaction does not confirm in time, it is possible our counterparty can take the funds by +/// broadcasting an HTLC timeout on-chain. +/// +/// Once the spending transaction confirms, before it has reached enough confirmations to be considered safe from chain +/// reorganizations, the balance will instead be provided via `LightningBalance::ClaimableAwaitingConfirmations`. +/// See more: +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct ContentiousClaimable { + /// The identifier of the channel this balance belongs to. + #[serde(with = "crate::serde_utils::hex_32")] + pub channel_id: [u8; 32], + /// The identifier of our channel counterparty. + #[serde(with = "crate::serde_utils::hex_33")] + pub counterparty_node_id: [u8; 33], + /// The amount available to claim, in satoshis, excluding the on-chain fees which were spent in broadcasting + /// the transaction. + pub amount_satoshis: u64, + /// The height at which the counterparty may be able to claim the balance if we have not done so. + pub timeout_height: u32, + /// The payment hash that locks this HTLC. + #[serde(with = "crate::serde_utils::hex_32")] + pub payment_hash: [u8; 32], + /// The preimage that can be used to claim this HTLC. + #[serde(with = "crate::serde_utils::hex_32")] + pub payment_preimage: [u8; 32], +} +/// HTLCs which we sent to our counterparty which are claimable after a timeout (less on-chain fees) if the counterparty +/// does not know the preimage for the HTLCs. These are somewhat likely to be claimed by our counterparty before we do. +/// See more: +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct MaybeTimeoutClaimableHtlc { + /// The identifier of the channel this balance belongs to. + #[serde(with = "crate::serde_utils::hex_32")] + pub channel_id: [u8; 32], + /// The identifier of our channel counterparty. + #[serde(with = "crate::serde_utils::hex_33")] + pub counterparty_node_id: [u8; 33], + /// The amount available to claim, in satoshis, excluding the on-chain fees which were spent in broadcasting + /// the transaction. + pub amount_satoshis: u64, + /// The height at which we will be able to claim the balance if our counterparty has not done so. + pub claimable_height: u32, + /// The payment hash whose preimage our counterparty needs to claim this HTLC. + #[serde(with = "crate::serde_utils::hex_32")] + pub payment_hash: [u8; 32], + /// Indicates whether this HTLC represents a payment which was sent outbound from us. + pub outbound_payment: bool, +} +/// HTLCs which we received from our counterparty which are claimable with a preimage which we do not currently have. +/// This will only be claimable if we receive the preimage from the node to which we forwarded this HTLC before the +/// timeout. +/// See more: +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct MaybePreimageClaimableHtlc { + /// The identifier of the channel this balance belongs to. + #[serde(with = "crate::serde_utils::hex_32")] + pub channel_id: [u8; 32], + /// The identifier of our channel counterparty. + #[serde(with = "crate::serde_utils::hex_33")] + pub counterparty_node_id: [u8; 33], + /// The amount available to claim, in satoshis, excluding the on-chain fees which were spent in broadcasting + /// the transaction. + pub amount_satoshis: u64, + /// The height at which our counterparty will be able to claim the balance if we have not yet received the preimage and + /// claimed it ourselves. + pub expiry_height: u32, + /// The payment hash whose preimage we need to claim this HTLC. + #[serde(with = "crate::serde_utils::hex_32")] + pub payment_hash: [u8; 32], +} +/// The channel has been closed, and our counterparty broadcasted a revoked commitment transaction. +/// +/// Thus, we're able to claim all outputs in the commitment transaction, one of which has the following amount. +/// +/// See more: +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct CounterpartyRevokedOutputClaimable { + /// The identifier of the channel this balance belongs to. + #[serde(with = "crate::serde_utils::hex_32")] + pub channel_id: [u8; 32], + /// The identifier of our channel counterparty. + #[serde(with = "crate::serde_utils::hex_33")] + pub counterparty_node_id: [u8; 33], + /// The amount, in satoshis, of the output which we can claim. + pub amount_satoshis: u64, +} +/// Details about the status of a known balance currently being swept to our on-chain wallet. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum PendingSweepBalance { + PendingBroadcast(PendingBroadcast), + BroadcastAwaitingConfirmation(BroadcastAwaitingConfirmation), + AwaitingThresholdConfirmations(AwaitingThresholdConfirmations), +} +/// The spendable output is about to be swept, but a spending transaction has yet to be generated and broadcast. +/// See more: +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct PendingBroadcast { + /// The identifier of the channel this balance belongs to. + #[serde(default, with = "crate::serde_utils::opt_hex_32")] + pub channel_id: Option<[u8; 32]>, + /// The amount, in satoshis, of the output being swept. + pub amount_satoshis: u64, +} +/// A spending transaction has been generated and broadcast and is awaiting confirmation on-chain. +/// See more: +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct BroadcastAwaitingConfirmation { + /// The identifier of the channel this balance belongs to. + #[serde(default, with = "crate::serde_utils::opt_hex_32")] + pub channel_id: Option<[u8; 32]>, + /// The best height when we last broadcast a transaction spending the output being swept. + pub latest_broadcast_height: u32, + /// The identifier of the transaction spending the swept output we last broadcast. + #[serde(with = "crate::serde_utils::hex_32")] + pub latest_spending_txid: [u8; 32], + /// The amount, in satoshis, of the output being swept. + pub amount_satoshis: u64, +} +/// A spending transaction has been confirmed on-chain and is awaiting threshold confirmations. +/// +/// It will be considered irrevocably confirmed after reaching `ANTI_REORG_DELAY`. +/// See more: +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct AwaitingThresholdConfirmations { + /// The identifier of the channel this balance belongs to. + #[serde(default, with = "crate::serde_utils::opt_hex_32")] + pub channel_id: Option<[u8; 32]>, + /// The identifier of the confirmed transaction spending the swept output. + #[serde(with = "crate::serde_utils::hex_32")] + pub latest_spending_txid: [u8; 32], + /// The hash of the block in which the spending transaction was confirmed. + #[serde(with = "crate::serde_utils::hex_32")] + pub confirmation_hash: [u8; 32], + /// The height at which the spending transaction was confirmed. + pub confirmation_height: u32, + /// The amount, in satoshis, of the output being swept. + pub amount_satoshis: u64, +} +/// Token used to determine start of next page in paginated APIs. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct PageToken { + pub token: String, + pub index: i64, +} +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum Bolt11InvoiceDescription { + Direct(String), + Hash(String), +} +/// Configuration options for payment routing and pathfinding. +/// See for more details on each field. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RouteParametersConfig { + /// The maximum total fees, in millisatoshi, that may accrue during route finding. + /// Defaults to 1% of the payment amount + 50 sats + pub max_total_routing_fee_msat: Option, + /// The maximum total CLTV delta we accept for the route. + /// Defaults to 1008. + pub max_total_cltv_expiry_delta: u32, + /// The maximum number of paths that may be used by (MPP) payments. + /// Defaults to 10. + pub max_path_count: u32, + /// Selects the maximum share of a channel's total capacity which will be + /// sent over a channel, as a power of 1/2. + /// Default value: 2 + pub max_channel_saturation_power_of_half: u32, +} +/// Routing fees for a channel as part of the network graph. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct GraphRoutingFees { + /// Flat routing fee in millisatoshis. + pub base_msat: u32, + /// Liquidity-based routing fee in millionths of a routed amount. + pub proportional_millionths: u32, +} +/// Details about one direction of a channel in the network graph, +/// as received within a `ChannelUpdate`. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct GraphChannelUpdate { + /// When the last update to the channel direction was issued. + /// Value is opaque, as set in the announcement. + pub last_update: u32, + /// Whether the channel can be currently used for payments (in this one direction). + pub enabled: bool, + /// The difference in CLTV values that you must have when routing through this channel. + pub cltv_expiry_delta: u32, + /// The minimum value, which must be relayed to the next hop via the channel. + pub htlc_minimum_msat: u64, + /// The maximum value which may be relayed to the next hop via the channel. + pub htlc_maximum_msat: u64, + /// Fees charged when the channel is used for routing. + pub fees: GraphRoutingFees, +} +/// Details about a channel in the network graph (both directions). +/// Received within a channel announcement. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct GraphChannel { + /// Source node of the first direction of the channel (hex-encoded public key). + #[serde(with = "crate::serde_utils::hex_33")] + pub node_one: [u8; 33], + /// Source node of the second direction of the channel (hex-encoded public key). + #[serde(with = "crate::serde_utils::hex_33")] + pub node_two: [u8; 33], + /// The channel capacity as seen on-chain, if chain lookup is available. + pub capacity_sats: Option, + /// Details about the first direction of a channel. + pub one_to_two: Option, + /// Details about the second direction of a channel. + pub two_to_one: Option, +} +/// Information received in the latest node_announcement from this node. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct GraphNodeAnnouncement { + /// When the last known update to the node state was issued. + /// Value is opaque, as set in the announcement. + pub last_update: u32, + /// Moniker assigned to the node. + /// May be invalid or malicious (eg control chars), should not be exposed to the user. + pub alias: String, + /// Color assigned to the node as a hex-encoded RGB string, e.g. "ff0000". + pub rgb: String, + /// List of addresses on which this node is reachable. + pub addresses: Vec, +} +/// Details of a known Lightning peer. +/// See more: +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Peer { + /// The hex-encoded node ID of the peer. + #[serde(with = "crate::serde_utils::hex_33")] + pub node_id: [u8; 33], + /// The network address of the peer. + pub address: String, + /// Indicates whether we'll try to reconnect to this peer after restarts. + pub is_persisted: bool, + /// Indicates whether we currently have an active connection with the peer. + pub is_connected: bool, +} +/// Details about a node in the network graph, known from the network announcement. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct GraphNode { + /// All valid channels a node has announced. + pub channels: Vec, + /// More information about a node from node_announcement. + /// Optional because we store a node entry after learning about it from + /// a channel announcement, but before receiving a node announcement. + pub announcement_info: Option, +} +/// Represents the direction of a payment. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum PaymentDirection { + /// The payment is inbound. + Inbound, + /// The payment is outbound. + Outbound, +} +/// Represents the current status of a payment. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum PaymentStatus { + /// The payment is still pending. + Pending, + /// The payment succeeded. + Succeeded, + /// The payment failed. + Failed, +} +/// Indicates whether the balance is derived from a cooperative close, a force-close (for holder or counterparty), +/// or whether it is for an HTLC. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum BalanceSource { + /// The channel was force closed by the holder. + HolderForceClosed, + /// The channel was force closed by the counterparty. + CounterpartyForceClosed, + /// The channel was cooperatively closed. + CoopClose, + /// This balance is the result of an HTLC. + Htlc, +} diff --git a/ldk-server-protos/Cargo.toml b/ldk-server-protos/Cargo.toml deleted file mode 100644 index c971d6f5..00000000 --- a/ldk-server-protos/Cargo.toml +++ /dev/null @@ -1,25 +0,0 @@ -[package] -name = "ldk-server-protos" -version = "0.1.0" -edition = "2021" - -build = "build.rs" - -# We use a cfg instead of a feature for genproto to prevent it from being -# enabled with --all-features. Proto generation is a developer-only tool that -# requires external dependencies (protoc) and shouldn't be triggered accidentally. -# This lint configuration tells Cargo that genproto is an expected custom cfg. -[lints.rust] -unexpected_cfgs = { level = "warn", check-cfg = ['cfg(genproto)'] } - -[features] -default = [] -serde = ["dep:serde", "dep:bytes"] - -[dependencies] -prost = { version = "0.11.6", default-features = false, features = ["std", "prost-derive"] } -serde = { version = "1.0", features = ["derive"], optional = true } -bytes = { version = "1", features = ["serde"], optional = true } - -[target.'cfg(genproto)'.build-dependencies] -prost-build = { version = "0.11.6", default-features = false } diff --git a/ldk-server-protos/build.rs b/ldk-server-protos/build.rs deleted file mode 100644 index 13e54d53..00000000 --- a/ldk-server-protos/build.rs +++ /dev/null @@ -1,95 +0,0 @@ -// This file is Copyright its original authors, visible in version control -// history. -// -// This file is licensed under the Apache License, Version 2.0 or the MIT license -// , at your option. -// You may not use this file except in accordance with one or both of these -// licenses. - -#[cfg(genproto)] -extern crate prost_build; - -#[cfg(genproto)] -use std::{env, fs, io::Write, path::Path}; - -#[cfg(genproto)] -const COPYRIGHT_HEADER: &str = - "// This file is Copyright its original authors, visible in version control -// history. -// -// This file is licensed under the Apache License, Version 2.0 or the MIT license -// , at your option. -// You may not use this file except in accordance with one or both of these -// licenses. - -"; - -/// To generate updated proto objects, run `RUSTFLAGS="--cfg genproto" cargo build` -fn main() { - #[cfg(genproto)] - generate_protos(); -} - -#[cfg(genproto)] -fn generate_protos() { - prost_build::Config::new() - .bytes(&["."]) - .type_attribute( - ".", - "#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]", - ) - .type_attribute(".", "#[cfg_attr(feature = \"serde\", serde(rename_all = \"snake_case\"))]") - .field_attribute( - "types.Bolt11.secret", - "#[cfg_attr(feature = \"serde\", serde(serialize_with = \"crate::serde_utils::serialize_opt_bytes_hex\"))]", - ) - .field_attribute( - "types.Bolt11Jit.secret", - "#[cfg_attr(feature = \"serde\", serde(serialize_with = \"crate::serde_utils::serialize_opt_bytes_hex\"))]", - ) - .field_attribute( - "types.Bolt12Offer.secret", - "#[cfg_attr(feature = \"serde\", serde(serialize_with = \"crate::serde_utils::serialize_opt_bytes_hex\"))]", - ) - .field_attribute( - "types.Bolt12Refund.secret", - "#[cfg_attr(feature = \"serde\", serde(serialize_with = \"crate::serde_utils::serialize_opt_bytes_hex\"))]", - ) - .field_attribute( - "types.Payment.direction", - "#[cfg_attr(feature = \"serde\", serde(serialize_with = \"crate::serde_utils::serialize_payment_direction\"))]", - ) - .field_attribute( - "types.Payment.status", - "#[cfg_attr(feature = \"serde\", serde(serialize_with = \"crate::serde_utils::serialize_payment_status\"))]", - ) - .field_attribute( - "types.ClaimableAwaitingConfirmations.source", - "#[cfg_attr(feature = \"serde\", serde(serialize_with = \"crate::serde_utils::serialize_balance_source\"))]", - ) - .field_attribute( - "api.UnifiedSendResponse.payment_result", - "#[cfg_attr(feature = \"serde\", serde(flatten))]", - ) - .compile_protos( - &[ - "src/proto/api.proto", - "src/proto/types.proto", - "src/proto/events.proto", - "src/proto/error.proto", - ], - &["src/proto/"], - ) - .expect("protobuf compilation failed"); - let out_dir = env::var("OUT_DIR").unwrap(); - println!("OUT_DIR: {}", &out_dir); - for file in &["api.rs", "types.rs", "events.rs", "error.rs"] { - let from_path = Path::new(&out_dir).join(file); - let content = fs::read(&from_path).unwrap(); - let mut dest = fs::File::create(Path::new("src").join(file)).unwrap(); - dest.write_all(COPYRIGHT_HEADER.as_bytes()).unwrap(); - dest.write_all(&content).unwrap(); - } -} diff --git a/ldk-server-protos/src/error.rs b/ldk-server-protos/src/error.rs deleted file mode 100644 index 41e73ee4..00000000 --- a/ldk-server-protos/src/error.rs +++ /dev/null @@ -1,79 +0,0 @@ -// This file is Copyright its original authors, visible in version control -// history. -// -// This file is licensed under the Apache License, Version 2.0 or the MIT license -// , at your option. -// You may not use this file except in accordance with one or both of these -// licenses. - -/// When HttpStatusCode is not ok (200), the response `content` contains a serialized `ErrorResponse` -/// with the relevant ErrorCode and `message` -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct ErrorResponse { - /// The error message containing a generic description of the error condition in English. - /// It is intended for a human audience only and should not be parsed to extract any information - /// programmatically. Client-side code may use it for logging only. - #[prost(string, tag = "1")] - pub message: ::prost::alloc::string::String, - /// The error code uniquely identifying an error condition. - /// It is meant to be read and understood programmatically by code that detects/handles errors by - /// type. - /// - /// **Caution**: If a new type of `error_code` is introduced in the `ErrorCode` enum, `error_code` field will be set to - /// `UnknownError`. - #[prost(enumeration = "ErrorCode", tag = "2")] - pub error_code: i32, -} -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] -#[repr(i32)] -pub enum ErrorCode { - /// Will never be used as `error_code` by server. - /// - /// **Caution**: If a new type of `error_code` is introduced in the `ErrorCode` enum, `error_code` field will be set to - /// `UnknownError`. - UnknownError = 0, - /// Used in the following cases: - /// - The request was missing a required argument. - /// - The specified argument was invalid, incomplete or in the wrong format. - /// - The request body of api cannot be deserialized into corresponding protobuf object. - /// - The request does not follow api contract. - InvalidRequestError = 1, - /// Used when authentication fails or in case of an unauthorized request. - AuthError = 2, - /// Used to represent an error while doing a Lightning operation. - LightningError = 3, - /// Used when an internal server error occurred. The client is probably at no fault. - InternalServerError = 4, -} -impl ErrorCode { - /// String value of the enum field names used in the ProtoBuf definition. - /// - /// The values are not transformed in any way and thus are considered stable - /// (if the ProtoBuf definition does not change) and safe for programmatic use. - pub fn as_str_name(&self) -> &'static str { - match self { - ErrorCode::UnknownError => "UNKNOWN_ERROR", - ErrorCode::InvalidRequestError => "INVALID_REQUEST_ERROR", - ErrorCode::AuthError => "AUTH_ERROR", - ErrorCode::LightningError => "LIGHTNING_ERROR", - ErrorCode::InternalServerError => "INTERNAL_SERVER_ERROR", - } - } - /// Creates an enum from field names used in the ProtoBuf definition. - pub fn from_str_name(value: &str) -> ::core::option::Option { - match value { - "UNKNOWN_ERROR" => Some(Self::UnknownError), - "INVALID_REQUEST_ERROR" => Some(Self::InvalidRequestError), - "AUTH_ERROR" => Some(Self::AuthError), - "LIGHTNING_ERROR" => Some(Self::LightningError), - "INTERNAL_SERVER_ERROR" => Some(Self::InternalServerError), - _ => None, - } - } -} diff --git a/ldk-server-protos/src/events.rs b/ldk-server-protos/src/events.rs deleted file mode 100644 index a41446aa..00000000 --- a/ldk-server-protos/src/events.rs +++ /dev/null @@ -1,87 +0,0 @@ -// This file is Copyright its original authors, visible in version control -// history. -// -// This file is licensed under the Apache License, Version 2.0 or the MIT license -// , at your option. -// You may not use this file except in accordance with one or both of these -// licenses. - -/// EventEnvelope wraps different event types in a single message to be used by EventPublisher. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct EventEnvelope { - #[prost(oneof = "event_envelope::Event", tags = "2, 3, 4, 6, 7")] - pub event: ::core::option::Option, -} -/// Nested message and enum types in `EventEnvelope`. -pub mod event_envelope { - #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] - #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] - #[allow(clippy::derive_partial_eq_without_eq)] - #[derive(Clone, PartialEq, ::prost::Oneof)] - pub enum Event { - #[prost(message, tag = "2")] - PaymentReceived(super::PaymentReceived), - #[prost(message, tag = "3")] - PaymentSuccessful(super::PaymentSuccessful), - #[prost(message, tag = "4")] - PaymentFailed(super::PaymentFailed), - #[prost(message, tag = "6")] - PaymentForwarded(super::PaymentForwarded), - #[prost(message, tag = "7")] - PaymentClaimable(super::PaymentClaimable), - } -} -/// PaymentReceived indicates a payment has been received. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct PaymentReceived { - /// The payment details for the payment in event. - #[prost(message, optional, tag = "1")] - pub payment: ::core::option::Option, -} -/// PaymentSuccessful indicates a sent payment was successful. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct PaymentSuccessful { - /// The payment details for the payment in event. - #[prost(message, optional, tag = "1")] - pub payment: ::core::option::Option, -} -/// PaymentFailed indicates a sent payment has failed. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct PaymentFailed { - /// The payment details for the payment in event. - #[prost(message, optional, tag = "1")] - pub payment: ::core::option::Option, -} -/// PaymentClaimable indicates a payment has arrived and is waiting to be manually claimed or failed. -/// This event is only emitted for payments created via `Bolt11ReceiveForHash`. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct PaymentClaimable { - /// The payment details for the claimable payment. - #[prost(message, optional, tag = "1")] - pub payment: ::core::option::Option, -} -/// PaymentForwarded indicates a payment was forwarded through the node. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct PaymentForwarded { - #[prost(message, optional, tag = "1")] - pub forwarded_payment: ::core::option::Option, -} diff --git a/ldk-server-protos/src/proto/api.proto b/ldk-server-protos/src/proto/api.proto deleted file mode 100644 index 3eb505d3..00000000 --- a/ldk-server-protos/src/proto/api.proto +++ /dev/null @@ -1,824 +0,0 @@ -syntax = "proto3"; -package api; - -import 'types.proto'; - -// Retrieve the latest node info like `node_id`, `current_best_block` etc. -// See more: -// - https://docs.rs/ldk-node/latest/ldk_node/struct.Node.html#method.node_id -// - https://docs.rs/ldk-node/latest/ldk_node/struct.Node.html#method.status -message GetNodeInfoRequest { -} - -// The response `content` for the `GetNodeInfo` API, when HttpStatusCode is OK (200). -// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -message GetNodeInfoResponse { - - // The hex-encoded `node-id` or public key for our own lightning node. - string node_id = 1; - - // The best block to which our Lightning wallet is currently synced. - // - // Should be always set, will never be `None`. - types.BestBlock current_best_block = 3; - - // The timestamp, in seconds since start of the UNIX epoch, when we last successfully synced our Lightning wallet to - // the chain tip. - // - // Will be `None` if the wallet hasn't been synced yet. - optional uint64 latest_lightning_wallet_sync_timestamp = 4; - - // The timestamp, in seconds since start of the UNIX epoch, when we last successfully synced our on-chain - // wallet to the chain tip. - // - // Will be `None` if the wallet hasn’t been synced since the node was initialized. - optional uint64 latest_onchain_wallet_sync_timestamp = 5; - - // The timestamp, in seconds since start of the UNIX epoch, when we last successfully update our fee rate cache. - // - // Will be `None` if the cache hasn’t been updated since the node was initialized. - optional uint64 latest_fee_rate_cache_update_timestamp = 6; - - // The timestamp, in seconds since start of the UNIX epoch, when the last rapid gossip sync (RGS) snapshot we - // successfully applied was generated. - // - // Will be `None` if RGS isn’t configured or the snapshot hasn’t been updated since the node was initialized. - optional uint64 latest_rgs_snapshot_timestamp = 7; - - // The timestamp, in seconds since start of the UNIX epoch, when we last broadcasted a node announcement. - // - // Will be `None` if we have no public channels or we haven’t broadcasted since the node was initialized. - optional uint64 latest_node_announcement_broadcast_timestamp = 8; - - // The addresses the node is currently listening on for incoming connections. - // - // Will be empty if the node is not listening on any addresses. - repeated string listening_addresses = 9; - - // The addresses the node announces to the network. - // - // Will be empty if no announcement addresses are configured. - repeated string announcement_addresses = 10; - - // The node alias, if configured. - // - // Will be `None` if no alias is configured. - optional string node_alias = 11; - - // The node URIs that can be used to connect to this node, in the format `node_id@address`. - // - // These are constructed from the announcement addresses and the node's public key. - // Will be empty if no announcement addresses are configured. - repeated string node_uris = 12; -} - -// Retrieve a new on-chain funding address. -// See more: https://docs.rs/ldk-node/latest/ldk_node/payment/struct.OnchainPayment.html#method.new_address -message OnchainReceiveRequest { -} - -// The response `content` for the `OnchainReceive` API, when HttpStatusCode is OK (200). -// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`.. -message OnchainReceiveResponse { - - // A Bitcoin on-chain address. - string address = 1; -} - -// Send an on-chain payment to the given address. -message OnchainSendRequest { - - // The address to send coins to. - string address = 1; - - // The amount in satoshis to send. - // While sending the specified amount, we will respect any on-chain reserve we need to keep, - // i.e., won't allow to cut into `total_anchor_channels_reserve_sats`. - // See more: https://docs.rs/ldk-node/latest/ldk_node/payment/struct.OnchainPayment.html#method.send_to_address - optional uint64 amount_sats = 2; - - // If set, the amount_sats field should be unset. - // It indicates that node will send full balance to the specified address. - // - // Please note that when send_all is used this operation will **not** retain any on-chain reserves, - // which might be potentially dangerous if you have open Anchor channels for which you can't trust - // the counterparty to spend the Anchor output after channel closure. - // See more: https://docs.rs/ldk-node/latest/ldk_node/payment/struct.OnchainPayment.html#method.send_all_to_address - optional bool send_all = 3; - - // If `fee_rate_sat_per_vb` is set it will be used on the resulting transaction. Otherwise we'll retrieve - // a reasonable estimate from BitcoinD. - optional uint64 fee_rate_sat_per_vb = 4; -} - -// The response `content` for the `OnchainSend` API, when HttpStatusCode is OK (200). -// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -message OnchainSendResponse { - - // The transaction ID of the broadcasted transaction. - string txid = 1; -} - -// Return a BOLT11 payable invoice that can be used to request and receive a payment -// for the given amount, if specified. -// The inbound payment will be automatically claimed upon arrival. -// See more: -// - https://docs.rs/ldk-node/latest/ldk_node/payment/struct.Bolt11Payment.html#method.receive -// - https://docs.rs/ldk-node/latest/ldk_node/payment/struct.Bolt11Payment.html#method.receive_variable_amount -message Bolt11ReceiveRequest { - - // The amount in millisatoshi to send. If unset, a "zero-amount" or variable-amount invoice is returned. - optional uint64 amount_msat = 1; - - // An optional description to attach along with the invoice. - // Will be set in the description field of the encoded payment request. - types.Bolt11InvoiceDescription description = 2; - - // Invoice expiry time in seconds. - uint32 expiry_secs = 3; -} - -// The response `content` for the `Bolt11Receive` API, when HttpStatusCode is OK (200). -// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -message Bolt11ReceiveResponse { - - // An invoice for a payment within the Lightning Network. - // With the details of the invoice, the sender has all the data necessary to send a payment - // to the recipient. - string invoice = 1; - - // The hex-encoded 32-byte payment hash. - string payment_hash = 2; - - // The hex-encoded 32-byte payment secret. - string payment_secret = 3; -} - -// Return a BOLT11 payable invoice for a given payment hash. -// The inbound payment will NOT be automatically claimed upon arrival. -// Instead, the payment will need to be manually claimed by calling `Bolt11ClaimForHash` -// or manually failed by calling `Bolt11FailForHash`. -// See more: -// - https://docs.rs/ldk-node/latest/ldk_node/payment/struct.Bolt11Payment.html#method.receive_for_hash -// - https://docs.rs/ldk-node/latest/ldk_node/payment/struct.Bolt11Payment.html#method.receive_variable_amount_for_hash -message Bolt11ReceiveForHashRequest { - - // The amount in millisatoshi to receive. If unset, a "zero-amount" or variable-amount invoice is returned. - optional uint64 amount_msat = 1; - - // An optional description to attach along with the invoice. - // Will be set in the description field of the encoded payment request. - types.Bolt11InvoiceDescription description = 2; - - // Invoice expiry time in seconds. - uint32 expiry_secs = 3; - - // The hex-encoded 32-byte payment hash to use for the invoice. - string payment_hash = 4; -} - -// The response `content` for the `Bolt11ReceiveForHash` API, when HttpStatusCode is OK (200). -// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -message Bolt11ReceiveForHashResponse { - - // An invoice for a payment within the Lightning Network. - // With the details of the invoice, the sender has all the data necessary to send a payment - // to the recipient. - string invoice = 1; -} - -// Manually claim a payment for a given payment hash with the corresponding preimage. -// This should be used to claim payments created via `Bolt11ReceiveForHash`. -// See more: https://docs.rs/ldk-node/latest/ldk_node/payment/struct.Bolt11Payment.html#method.claim_for_hash -message Bolt11ClaimForHashRequest { - - // The hex-encoded 32-byte payment hash. - // If provided, it will be used to verify that the preimage matches. - optional string payment_hash = 1; - - // The amount in millisatoshi that is claimable. - // If not provided, skips amount verification. - optional uint64 claimable_amount_msat = 2; - - // The hex-encoded 32-byte payment preimage. - string preimage = 3; -} - -// The response `content` for the `Bolt11ClaimForHash` API, when HttpStatusCode is OK (200). -// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -message Bolt11ClaimForHashResponse {} - -// Manually fail a payment for a given payment hash. -// This should be used to reject payments created via `Bolt11ReceiveForHash`. -// See more: https://docs.rs/ldk-node/latest/ldk_node/payment/struct.Bolt11Payment.html#method.fail_for_hash -message Bolt11FailForHashRequest { - - // The hex-encoded 32-byte payment hash. - string payment_hash = 1; -} - -// The response `content` for the `Bolt11FailForHash` API, when HttpStatusCode is OK (200). -// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -message Bolt11FailForHashResponse {} - -// Return a BOLT11 payable invoice that can be used to request and receive a payment via an -// LSPS2 just-in-time channel. -// See more: https://docs.rs/ldk-node/latest/ldk_node/payment/struct.Bolt11Payment.html#method.receive_via_jit_channel -message Bolt11ReceiveViaJitChannelRequest { - - // The amount in millisatoshi to request. - uint64 amount_msat = 1; - - // An optional description to attach along with the invoice. - // Will be set in the description field of the encoded payment request. - types.Bolt11InvoiceDescription description = 2; - - // Invoice expiry time in seconds. - uint32 expiry_secs = 3; - - // Optional upper bound for the total fee an LSP may deduct when opening the JIT channel. - optional uint64 max_total_lsp_fee_limit_msat = 4; -} - -// The response `content` for the `Bolt11ReceiveViaJitChannel` API, when HttpStatusCode is OK (200). -// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -message Bolt11ReceiveViaJitChannelResponse { - - // An invoice for a payment within the Lightning Network. - string invoice = 1; -} - -// Return a variable-amount BOLT11 invoice that can be used to receive a payment via an LSPS2 -// just-in-time channel. -// See more: https://docs.rs/ldk-node/latest/ldk_node/payment/struct.Bolt11Payment.html#method.receive_variable_amount_via_jit_channel -message Bolt11ReceiveVariableAmountViaJitChannelRequest { - - // An optional description to attach along with the invoice. - // Will be set in the description field of the encoded payment request. - types.Bolt11InvoiceDescription description = 1; - - // Invoice expiry time in seconds. - uint32 expiry_secs = 2; - - // Optional upper bound for the proportional fee, in parts-per-million millisatoshis, that an - // LSP may deduct when opening the JIT channel. - optional uint64 max_proportional_lsp_fee_limit_ppm_msat = 3; -} - -// The response `content` for the `Bolt11ReceiveVariableAmountViaJitChannel` API, when HttpStatusCode is OK (200). -// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -message Bolt11ReceiveVariableAmountViaJitChannelResponse { - - // An invoice for a payment within the Lightning Network. - string invoice = 1; -} - - -// Send a payment for a BOLT11 invoice. -// See more: https://docs.rs/ldk-node/latest/ldk_node/payment/struct.Bolt11Payment.html#method.send -message Bolt11SendRequest { - - // An invoice for a payment within the Lightning Network. - string invoice = 1; - - // Set this field when paying a so-called "zero-amount" invoice, i.e., an invoice that leaves the - // amount paid to be determined by the user. - // This operation will fail if the amount specified is less than the value required by the given invoice. - optional uint64 amount_msat = 2; - - // Configuration options for payment routing and pathfinding. - optional types.RouteParametersConfig route_parameters = 3; - -} - -// The response `content` for the `Bolt11Send` API, when HttpStatusCode is OK (200). -// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -message Bolt11SendResponse { - - // An identifier used to uniquely identify a payment in hex-encoded form. - string payment_id = 1; -} - -// Returns a BOLT12 offer for the given amount, if specified. -// -// See more: -// - https://docs.rs/ldk-node/latest/ldk_node/payment/struct.Bolt12Payment.html#method.receive -// - https://docs.rs/ldk-node/latest/ldk_node/payment/struct.Bolt12Payment.html#method.receive_variable_amount -message Bolt12ReceiveRequest { - - // An optional description to attach along with the offer. - // Will be set in the description field of the encoded offer. - string description = 1; - - // The amount in millisatoshi to send. If unset, a "zero-amount" or variable-amount offer is returned. - optional uint64 amount_msat = 2; - - // Offer expiry time in seconds. - optional uint32 expiry_secs = 3; - - // If set, it represents the number of items requested, can only be set for fixed-amount offers. - optional uint64 quantity = 4; -} - -// The response `content` for the `Bolt12Receive` API, when HttpStatusCode is OK (200). -// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -message Bolt12ReceiveResponse { - - // An offer for a payment within the Lightning Network. - // With the details of the offer, the sender has all the data necessary to send a payment - // to the recipient. - string offer = 1; - - // The hex-encoded offer id. - string offer_id = 2; -} - -// Send a payment for a BOLT12 offer. -// See more: -// - https://docs.rs/ldk-node/latest/ldk_node/payment/struct.Bolt12Payment.html#method.send -// - https://docs.rs/ldk-node/latest/ldk_node/payment/struct.Bolt12Payment.html#method.send_using_amount -message Bolt12SendRequest { - - // An offer for a payment within the Lightning Network. - string offer = 1; - - // Set this field when paying a so-called "zero-amount" offer, i.e., an offer that leaves the - // amount paid to be determined by the user. - // This operation will fail if the amount specified is less than the value required by the given offer. - optional uint64 amount_msat = 2; - - // If set, it represents the number of items requested. - optional uint64 quantity = 3; - - // If set, it will be seen by the recipient and reflected back in the invoice. - optional string payer_note = 4; - - // Configuration options for payment routing and pathfinding. - optional types.RouteParametersConfig route_parameters = 5; -} - -// The response `content` for the `Bolt12Send` API, when HttpStatusCode is OK (200). -// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -message Bolt12SendResponse { - - // An identifier used to uniquely identify a payment in hex-encoded form. - string payment_id = 1; -} - -// Send a spontaneous payment, also known as "keysend", to a node. -// See more: https://docs.rs/ldk-node/latest/ldk_node/payment/struct.SpontaneousPayment.html#method.send -message SpontaneousSendRequest { - // The amount in millisatoshis to send. - uint64 amount_msat = 1; - - // The hex-encoded public key of the node to send the payment to. - string node_id = 2; - - // Configuration options for payment routing and pathfinding. - optional types.RouteParametersConfig route_parameters = 3; -} - -// The response `content` for the `SpontaneousSend` API, when HttpStatusCode is OK (200). -// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -message SpontaneousSendResponse { - // An identifier used to uniquely identify a payment in hex-encoded form. - string payment_id = 1; -} - -// Creates a new outbound channel to the given remote node. -// See more: https://docs.rs/ldk-node/latest/ldk_node/struct.Node.html#method.connect_open_channel -message OpenChannelRequest { - - // The hex-encoded public key of the node to open a channel with. - string node_pubkey = 1; - - // An address which can be used to connect to a remote peer. - // It can be of type IPv4:port, IPv6:port, OnionV3:port or hostname:port - string address = 2; - - // The amount of satoshis the caller is willing to commit to the channel. - uint64 channel_amount_sats = 3; - - // The amount of satoshis to push to the remote side as part of the initial commitment state. - optional uint64 push_to_counterparty_msat = 4; - - // The channel configuration to be used for opening this channel. If unset, default ChannelConfig is used. - optional types.ChannelConfig channel_config = 5; - - // Whether the channel should be public. - bool announce_channel = 6; -} - -// The response `content` for the `OpenChannel` API, when HttpStatusCode is OK (200). -// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -message OpenChannelResponse { - - // The local channel id of the created channel that user can use to refer to channel. - string user_channel_id = 1; -} - -// Increases the channel balance by the given amount. -// See more: https://docs.rs/ldk-node/latest/ldk_node/struct.Node.html#method.splice_in -message SpliceInRequest { - - // The local `user_channel_id` of the channel. - string user_channel_id = 1; - - // The hex-encoded public key of the channel's counterparty node. - string counterparty_node_id = 2; - - // The amount of sats to splice into the channel. - uint64 splice_amount_sats = 3; -} - -// The response `content` for the `SpliceIn` API, when HttpStatusCode is OK (200). -// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -message SpliceInResponse {} - -// Decreases the channel balance by the given amount. -// See more: https://docs.rs/ldk-node/latest/ldk_node/struct.Node.html#method.splice_out -message SpliceOutRequest { - - // The local `user_channel_id` of this channel. - string user_channel_id = 1; - - // The hex-encoded public key of the channel's counterparty node. - string counterparty_node_id = 2; - - // A Bitcoin on-chain address to send the spliced-out funds. - // - // If not set, an address from the node's on-chain wallet will be used. - optional string address = 3; - - // The amount of sats to splice out of the channel. - uint64 splice_amount_sats = 4; -} - -// The response `content` for the `SpliceOut` API, when HttpStatusCode is OK (200). -// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -message SpliceOutResponse { - - // The Bitcoin on-chain address where the funds will be sent. - string address = 1; -} - -// Update the config for a previously opened channel. -// See more: https://docs.rs/ldk-node/latest/ldk_node/struct.Node.html#method.update_channel_config -message UpdateChannelConfigRequest { - - // The local `user_channel_id` of this channel. - string user_channel_id = 1; - - // The hex-encoded public key of the counterparty node to update channel config with. - string counterparty_node_id = 2; - - // The updated channel configuration settings for a channel. - types.ChannelConfig channel_config = 3; -} - -// The response `content` for the `UpdateChannelConfig` API, when HttpStatusCode is OK (200). -// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -message UpdateChannelConfigResponse { -} - -// Closes the channel specified by given request. -// See more: https://docs.rs/ldk-node/latest/ldk_node/struct.Node.html#method.close_channel -message CloseChannelRequest { - - // The local `user_channel_id` of this channel. - string user_channel_id = 1; - - // The hex-encoded public key of the node to close a channel with. - string counterparty_node_id = 2; -} - -// The response `content` for the `CloseChannel` API, when HttpStatusCode is OK (200). -// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -message CloseChannelResponse {} - -// Force closes the channel specified by given request. -// See more: https://docs.rs/ldk-node/latest/ldk_node/struct.Node.html#method.force_close_channel -message ForceCloseChannelRequest { - // The local `user_channel_id` of this channel. - string user_channel_id = 1; - // The hex-encoded public key of the node to close a channel with. - string counterparty_node_id = 2; - // The reason for force-closing. - optional string force_close_reason = 3; -} - -// The response `content` for the `ForceCloseChannel` API, when HttpStatusCode is OK (200). -// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -message ForceCloseChannelResponse {} - -// Returns a list of known channels. -// See more: https://docs.rs/ldk-node/latest/ldk_node/struct.Node.html#method.list_channels -message ListChannelsRequest {} - -// The response `content` for the `ListChannels` API, when HttpStatusCode is OK (200). -// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -message ListChannelsResponse { - - // List of channels. - repeated types.Channel channels = 1; -} - -// Returns payment details for a given payment_id. -// See more: https://docs.rs/ldk-node/latest/ldk_node/struct.Node.html#method.payment -message GetPaymentDetailsRequest { - // An identifier used to uniquely identify a payment in hex-encoded form. - string payment_id = 1; -} - -// The response `content` for the `GetPaymentDetails` API, when HttpStatusCode is OK (200). -// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -message GetPaymentDetailsResponse { - // Represents a payment. - // Will be `None` if payment doesn't exist. - types.Payment payment = 1; -} - -// Retrieves list of all payments. -// See more: https://docs.rs/ldk-node/latest/ldk_node/struct.Node.html#method.list_payments -message ListPaymentsRequest { - // `page_token` is a pagination token. - // - // To query for the first page, `page_token` must not be specified. - // - // For subsequent pages, use the value that was returned as `next_page_token` in the previous - // page's response. - optional types.PageToken page_token = 1; -} - -// The response `content` for the `ListPayments` API, when HttpStatusCode is OK (200). -// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -message ListPaymentsResponse { - // List of payments. - repeated types.Payment payments = 1; - - // `next_page_token` is a pagination token, used to retrieve the next page of results. - // Use this value to query for next-page of paginated operation, by specifying - // this value as the `page_token` in the next request. - // - // If `next_page_token` is `None`, then the "last page" of results has been processed and - // there is no more data to be retrieved. - // - // If `next_page_token` is not `None`, it does not necessarily mean that there is more data in the - // result set. The only way to know when you have reached the end of the result set is when - // `next_page_token` is `None`. - // - // **Caution**: Clients must not assume a specific number of records to be present in a page for - // paginated response. - optional types.PageToken next_page_token = 2; -} - -// Retrieves list of all forwarded payments. -// See more: https://docs.rs/ldk-node/latest/ldk_node/enum.Event.html#variant.PaymentForwarded -message ListForwardedPaymentsRequest { - // `page_token` is a pagination token. - // - // To query for the first page, `page_token` must not be specified. - // - // For subsequent pages, use the value that was returned as `next_page_token` in the previous - // page's response. - optional types.PageToken page_token = 1; -} - -// The response `content` for the `ListForwardedPayments` API, when HttpStatusCode is OK (200). -// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -message ListForwardedPaymentsResponse { - // List of forwarded payments. - repeated types.ForwardedPayment forwarded_payments = 1; - - // `next_page_token` is a pagination token, used to retrieve the next page of results. - // Use this value to query for next-page of paginated operation, by specifying - // this value as the `page_token` in the next request. - // - // If `next_page_token` is `None`, then the "last page" of results has been processed and - // there is no more data to be retrieved. - // - // If `next_page_token` is not `None`, it does not necessarily mean that there is more data in the - // result set. The only way to know when you have reached the end of the result set is when - // `next_page_token` is `None`. - // - // **Caution**: Clients must not assume a specific number of records to be present in a page for - // paginated response. - optional types.PageToken next_page_token = 2; -} - -// Sign a message with the node's secret key. -// See more: https://docs.rs/ldk-node/latest/ldk_node/struct.Node.html#method.sign_message -message SignMessageRequest { - // The message to sign, as raw bytes. - bytes message = 1; -} - -// The response `content` for the `SignMessage` API, when HttpStatusCode is OK (200). -// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -message SignMessageResponse { - // The signature of the message, as a zbase32-encoded string. - string signature = 1; -} - -// Verify a signature against a message and public key. -// See more: https://docs.rs/ldk-node/latest/ldk_node/struct.Node.html#method.verify_signature -message VerifySignatureRequest { - // The message that was signed, as raw bytes. - bytes message = 1; - - // The signature to verify, as a zbase32-encoded string. - string signature = 2; - - // The hex-encoded public key of the signer. - string public_key = 3; -} - -// The response `content` for the `VerifySignature` API, when HttpStatusCode is OK (200). -// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -message VerifySignatureResponse { - // Whether the signature is valid. - bool valid = 1; -} - -// Export the pathfinding scores used by the router. -// See more: https://docs.rs/ldk-node/latest/ldk_node/struct.Node.html#method.export_pathfinding_scores -message ExportPathfindingScoresRequest {} - -// The response `content` for the `ExportPathfindingScores` API, when HttpStatusCode is OK (200). -// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -message ExportPathfindingScoresResponse { - // The serialized pathfinding scores data. - bytes scores = 1; -} - -// Retrieves an overview of all known balances. -// See more: https://docs.rs/ldk-node/latest/ldk_node/struct.Node.html#method.list_balances -message GetBalancesRequest {} - -// The response `content` for the `GetBalances` API, when HttpStatusCode is OK (200). -// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -message GetBalancesResponse { - // The total balance of our on-chain wallet. - uint64 total_onchain_balance_sats = 1; - - // The currently spendable balance of our on-chain wallet. - // - // This includes any sufficiently confirmed funds, minus `total_anchor_channels_reserve_sats`. - uint64 spendable_onchain_balance_sats = 2; - - // The share of our total balance that we retain as an emergency reserve to (hopefully) be - // able to spend the Anchor outputs when one of our channels is closed. - uint64 total_anchor_channels_reserve_sats = 3; - - // The total balance that we would be able to claim across all our Lightning channels. - // - // Note this excludes balances that we are unsure if we are able to claim (e.g., as we are - // waiting for a preimage or for a timeout to expire). These balances will however be included - // as `MaybePreimageClaimableHTLC` and `MaybeTimeoutClaimableHTLC` in `lightning_balances`. - uint64 total_lightning_balance_sats = 4; - - // A detailed list of all known Lightning balances that would be claimable on channel closure. - // - // Note that less than the listed amounts are spendable over lightning as further reserve - // restrictions apply. Please refer to `Channel::outbound_capacity_msat` and - // Channel::next_outbound_htlc_limit_msat as returned by `ListChannels` - // for a better approximation of the spendable amounts. - repeated types.LightningBalance lightning_balances = 5; - - // A detailed list of balances currently being swept from the Lightning to the on-chain - // wallet. - // - // These are balances resulting from channel closures that may have been encumbered by a - // delay, but are now being claimed and useable once sufficiently confirmed on-chain. - // - // Note that, depending on the sync status of the wallets, swept balances listed here might or - // might not already be accounted for in `total_onchain_balance_sats`. - repeated types.PendingSweepBalance pending_balances_from_channel_closures = 6; -} - -// Connect to a peer on the Lightning Network. -// See more: https://docs.rs/ldk-node/latest/ldk_node/struct.Node.html#method.connect -message ConnectPeerRequest { - // The hex-encoded public key of the node to connect to. - string node_pubkey = 1; - - // An address which can be used to connect to a remote peer. - // It can be of type IPv4:port, IPv6:port, OnionV3:port or hostname:port - string address = 2; - - // Whether to persist the peer connection, i.e., whether the peer will be re-connected on - // restart. - bool persist = 3; -} - -// The response `content` for the `ConnectPeer` API, when HttpStatusCode is OK (200). -// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -message ConnectPeerResponse {} - -// Disconnect from a peer and remove it from the peer store. -// See more: https://docs.rs/ldk-node/latest/ldk_node/struct.Node.html#method.disconnect -message DisconnectPeerRequest { - // The hex-encoded public key of the node to disconnect from. - string node_pubkey = 1; -} - -// The response `content` for the `DisconnectPeer` API, when HttpStatusCode is OK (200). -// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -message DisconnectPeerResponse {} - -// Returns a list of peers. -// See more: https://docs.rs/ldk-node/latest/ldk_node/struct.Node.html#method.list_peers -message ListPeersRequest {} - -// The response `content` for the `ListPeers` API, when HttpStatusCode is OK (200). -// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -message ListPeersResponse { - - // List of peers. - repeated types.Peer peers = 1; -} - -// Returns a list of all known short channel IDs in the network graph. -// See more: https://docs.rs/ldk-node/latest/ldk_node/graph/struct.NetworkGraph.html#method.list_channels -message GraphListChannelsRequest {} - -// The response `content` for the `GraphListChannels` API, when HttpStatusCode is OK (200). -// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -message GraphListChannelsResponse { - // List of short channel IDs known to the network graph. - repeated uint64 short_channel_ids = 1; -} - -// Returns information on a channel with the given short channel ID from the network graph. -// See more: https://docs.rs/ldk-node/latest/ldk_node/graph/struct.NetworkGraph.html#method.channel -message GraphGetChannelRequest { - // The short channel ID to look up. - uint64 short_channel_id = 1; -} - -// The response `content` for the `GraphGetChannel` API, when HttpStatusCode is OK (200). -// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -message GraphGetChannelResponse { - // The channel information. - types.GraphChannel channel = 1; -} - -// Returns a list of all known node IDs in the network graph. -// See more: https://docs.rs/ldk-node/latest/ldk_node/graph/struct.NetworkGraph.html#method.list_nodes -message GraphListNodesRequest {} - -// The response `content` for the `GraphListNodes` API, when HttpStatusCode is OK (200). -// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -message GraphListNodesResponse { - // List of hex-encoded node IDs known to the network graph. - repeated string node_ids = 1; -} - -// Send a payment given a BIP 21 URI or BIP 353 Human-Readable Name. -// -// This method parses the provided URI string and attempts to send the payment. If the URI -// has an offer and/or invoice, it will try to pay the offer first followed by the invoice. -// If they both fail, the on-chain payment will be paid. -// See more: https://docs.rs/ldk-node/latest/ldk_node/payment/struct.UnifiedPayment.html#method.send -message UnifiedSendRequest { - - // A BIP 21 URI or BIP 353 Human-Readable Name to pay. - string uri = 1; - - // The amount in millisatoshis to send. Required for "zero-amount" or variable-amount URIs. - optional uint64 amount_msat = 2; - - // Configuration options for payment routing and pathfinding. - optional types.RouteParametersConfig route_parameters = 3; -} - -// The response `content` for the `UnifiedSend` API, when HttpStatusCode is OK (200). -// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -message UnifiedSendResponse { - - oneof payment_result { - - // An on-chain payment was made. Contains the transaction ID. - string txid = 1; - - // A BOLT11 payment was made. Contains the payment ID in hex-encoded form. - string bolt11_payment_id = 2; - - // A BOLT12 payment was made. Contains the payment ID in hex-encoded form. - string bolt12_payment_id = 3; - } -} - -// Returns information on a node with the given ID from the network graph. -// See more: https://docs.rs/ldk-node/latest/ldk_node/graph/struct.NetworkGraph.html#method.node -message GraphGetNodeRequest { - // The hex-encoded node ID to look up. - string node_id = 1; -} - -// The response `content` for the `GraphGetNode` API, when HttpStatusCode is OK (200). -// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -message GraphGetNodeResponse { - // The node information. - types.GraphNode node = 1; -} diff --git a/ldk-server-protos/src/proto/error.proto b/ldk-server-protos/src/proto/error.proto deleted file mode 100644 index c5a75d7d..00000000 --- a/ldk-server-protos/src/proto/error.proto +++ /dev/null @@ -1,45 +0,0 @@ -syntax = "proto3"; -package error; - -// When HttpStatusCode is not ok (200), the response `content` contains a serialized `ErrorResponse` -// with the relevant ErrorCode and `message` -message ErrorResponse { - - // The error message containing a generic description of the error condition in English. - // It is intended for a human audience only and should not be parsed to extract any information - // programmatically. Client-side code may use it for logging only. - string message = 1; - - // The error code uniquely identifying an error condition. - // It is meant to be read and understood programmatically by code that detects/handles errors by - // type. - // - // **Caution**: If a new type of `error_code` is introduced in the `ErrorCode` enum, `error_code` field will be set to - // `UnknownError`. - ErrorCode error_code = 2; -} - -enum ErrorCode { - - // Will never be used as `error_code` by server. - // - // **Caution**: If a new type of `error_code` is introduced in the `ErrorCode` enum, `error_code` field will be set to - // `UnknownError`. - UNKNOWN_ERROR = 0; - - // Used in the following cases: - // - The request was missing a required argument. - // - The specified argument was invalid, incomplete or in the wrong format. - // - The request body of api cannot be deserialized into corresponding protobuf object. - // - The request does not follow api contract. - INVALID_REQUEST_ERROR = 1; - - // Used when authentication fails or in case of an unauthorized request. - AUTH_ERROR = 2; - - // Used to represent an error while doing a Lightning operation. - LIGHTNING_ERROR = 3; - - // Used when an internal server error occurred. The client is probably at no fault. - INTERNAL_SERVER_ERROR = 4; -} diff --git a/ldk-server-protos/src/proto/events.proto b/ldk-server-protos/src/proto/events.proto deleted file mode 100644 index 5c5ce2c3..00000000 --- a/ldk-server-protos/src/proto/events.proto +++ /dev/null @@ -1,44 +0,0 @@ -syntax = "proto3"; -import "types.proto"; -package events; - -// EventEnvelope wraps different event types in a single message to be used by EventPublisher. -message EventEnvelope { - oneof event { - PaymentReceived payment_received = 2; - PaymentSuccessful payment_successful = 3; - PaymentFailed payment_failed = 4; - PaymentForwarded payment_forwarded = 6; - PaymentClaimable payment_claimable = 7; - } -} - -// PaymentReceived indicates a payment has been received. -message PaymentReceived { - // The payment details for the payment in event. - types.Payment payment = 1; -} - -// PaymentSuccessful indicates a sent payment was successful. -message PaymentSuccessful { - // The payment details for the payment in event. - types.Payment payment = 1; -} - -// PaymentFailed indicates a sent payment has failed. -message PaymentFailed { - // The payment details for the payment in event. - types.Payment payment = 1; -} - -// PaymentClaimable indicates a payment has arrived and is waiting to be manually claimed or failed. -// This event is only emitted for payments created via `Bolt11ReceiveForHash`. -message PaymentClaimable { - // The payment details for the claimable payment. - types.Payment payment = 1; -} - -// PaymentForwarded indicates a payment was forwarded through the node. -message PaymentForwarded { - types.ForwardedPayment forwarded_payment = 1; -} diff --git a/ldk-server-protos/src/proto/types.proto b/ldk-server-protos/src/proto/types.proto deleted file mode 100644 index e48908ef..00000000 --- a/ldk-server-protos/src/proto/types.proto +++ /dev/null @@ -1,820 +0,0 @@ -syntax = "proto3"; -package types; - -// Represents a payment. -// See more: https://docs.rs/ldk-node/latest/ldk_node/payment/struct.PaymentDetails.html -message Payment { - // An identifier used to uniquely identify a payment in hex-encoded form. - string id = 1; - - // The kind of the payment. - PaymentKind kind = 2; - - // The amount transferred. - optional uint64 amount_msat = 3; - - // The fees that were paid for this payment. - // - // For Lightning payments, this will only be updated for outbound payments once they - // succeeded. - optional uint64 fee_paid_msat = 7; - - // The direction of the payment. - PaymentDirection direction = 4; - - // The status of the payment. - PaymentStatus status = 5; - - // The timestamp, in seconds since start of the UNIX epoch, when this entry was last updated. - uint64 latest_update_timestamp = 6; -} - -message PaymentKind { - oneof kind { - Onchain onchain = 1; - Bolt11 bolt11 = 2; - Bolt11Jit bolt11_jit = 3; - Bolt12Offer bolt12_offer = 4; - Bolt12Refund bolt12_refund = 5; - Spontaneous spontaneous = 6; - } -} - -// Represents an on-chain payment. -message Onchain { - // The transaction identifier of this payment. - string txid = 1; - - // The confirmation status of this payment. - ConfirmationStatus status = 2; -} - -message ConfirmationStatus { - oneof status { - Confirmed confirmed = 1; - Unconfirmed unconfirmed = 2; - } -} - -// The on-chain transaction is confirmed in the best chain. -message Confirmed { - // The hex representation of hash of the block in which the transaction was confirmed. - string block_hash = 1; - - // The height under which the block was confirmed. - uint32 height = 2; - - // The timestamp, in seconds since start of the UNIX epoch, when this entry was last updated. - uint64 timestamp = 3; -} - -// The on-chain transaction is unconfirmed. -message Unconfirmed {} - -// Represents a BOLT 11 payment. -message Bolt11 { - // The payment hash, i.e., the hash of the preimage. - string hash = 1; - - // The pre-image used by the payment. - optional string preimage = 2; - - // The secret used by the payment. - optional bytes secret = 3; -} - -// Represents a BOLT 11 payment intended to open an LSPS 2 just-in-time channel. -message Bolt11Jit { - // The payment hash, i.e., the hash of the preimage. - string hash = 1; - - // The pre-image used by the payment. - optional string preimage = 2; - - // The secret used by the payment. - optional bytes secret = 3; - - // Limits applying to how much fee we allow an LSP to deduct from the payment amount. - // - // Allowing them to deduct this fee from the first inbound payment will pay for the LSP’s channel opening fees. - // - // See [`LdkChannelConfig::accept_underpaying_htlcs`](https://docs.rs/lightning/latest/lightning/util/config/struct.ChannelConfig.html#structfield.accept_underpaying_htlcs) - // for more information. - LSPFeeLimits lsp_fee_limits = 4; - - // The value, in thousands of a satoshi, that was deducted from this payment as an extra - // fee taken by our channel counterparty. - // - // Will only be `Some` once we received the payment. - optional uint64 counterparty_skimmed_fee_msat = 5; -} - -// Represents a BOLT 12 ‘offer’ payment, i.e., a payment for an Offer. -message Bolt12Offer { - // The payment hash, i.e., the hash of the preimage. - optional string hash = 1; - - // The pre-image used by the payment. - optional string preimage = 2; - - // The secret used by the payment. - optional bytes secret = 3; - - // The hex-encoded ID of the offer this payment is for. - string offer_id = 4; - - // The payer's note for the payment. - // Truncated to [PAYER_NOTE_LIMIT](https://docs.rs/lightning/latest/lightning/offers/invoice_request/constant.PAYER_NOTE_LIMIT.html). - // - // **Caution**: The `payer_note` field may come from an untrusted source. To prevent potential misuse, - // all non-printable characters will be sanitized and replaced with safe characters. - optional string payer_note = 5; - - // The quantity of an item requested in the offer. - optional uint64 quantity = 6; -} - -// Represents a BOLT 12 ‘refund’ payment, i.e., a payment for a Refund. -message Bolt12Refund { - // The payment hash, i.e., the hash of the preimage. - optional string hash = 1; - - // The pre-image used by the payment. - optional string preimage = 2; - - // The secret used by the payment. - optional bytes secret = 3; - - // The payer's note for the payment. - // Truncated to [PAYER_NOTE_LIMIT](https://docs.rs/lightning/latest/lightning/offers/invoice_request/constant.PAYER_NOTE_LIMIT.html). - // - // **Caution**: The `payer_note` field may come from an untrusted source. To prevent potential misuse, - // all non-printable characters will be sanitized and replaced with safe characters. - optional string payer_note = 5; - - // The quantity of an item requested in the offer. - optional uint64 quantity = 6; - -} - -// Represents a spontaneous (“keysend”) payment. -message Spontaneous { - // The payment hash, i.e., the hash of the preimage. - string hash = 1; - - // The pre-image used by the payment. - optional string preimage = 2; -} - -// Limits applying to how much fee we allow an LSP to deduct from the payment amount. -// See [`LdkChannelConfig::accept_underpaying_htlcs`] for more information. -// -// [`LdkChannelConfig::accept_underpaying_htlcs`]: lightning::util::config::ChannelConfig::accept_underpaying_htlcs -message LSPFeeLimits { - // The maximal total amount we allow any configured LSP withhold from us when forwarding the - // payment. - optional uint64 max_total_opening_fee_msat = 1; - - // The maximal proportional fee, in parts-per-million millisatoshi, we allow any configured - // LSP withhold from us when forwarding the payment. - optional uint64 max_proportional_opening_fee_ppm_msat = 2; -} - -// Represents the direction of a payment. -enum PaymentDirection { - // The payment is inbound. - INBOUND = 0; - - // The payment is outbound. - OUTBOUND = 1; -} - -// Represents the current status of a payment. -enum PaymentStatus { - // The payment is still pending. - PENDING = 0; - - // The payment succeeded. - SUCCEEDED = 1; - - // The payment failed. - FAILED = 2; -} - -// A forwarded payment through our node. -// See more: https://docs.rs/ldk-node/latest/ldk_node/enum.Event.html#variant.PaymentForwarded -message ForwardedPayment{ - // The channel id of the incoming channel between the previous node and us. - string prev_channel_id = 1; - - // The channel id of the outgoing channel between the next node and us. - string next_channel_id = 2; - - // The `user_channel_id` of the incoming channel between the previous node and us. - string prev_user_channel_id = 3; - - // The node id of the previous node. - string prev_node_id = 9; - - // The node id of the next node. - string next_node_id = 10; - - // The `user_channel_id` of the outgoing channel between the next node and us. - // This will be `None` if the payment was settled via an on-chain transaction. - // See the caveat described for the `total_fee_earned_msat` field. - optional string next_user_channel_id = 4; - - // The total fee, in milli-satoshis, which was earned as a result of the payment. - // - // Note that if we force-closed the channel over which we forwarded an HTLC while the HTLC was pending, the amount the - // next hop claimed will have been rounded down to the nearest whole satoshi. Thus, the fee calculated here may be - // higher than expected as we still claimed the full value in millisatoshis from the source. - // In this case, `claim_from_onchain_tx` will be set. - // - // If the channel which sent us the payment has been force-closed, we will claim the funds via an on-chain transaction. - // In that case we do not yet know the on-chain transaction fees which we will spend and will instead set this to `None`. - optional uint64 total_fee_earned_msat = 5; - - // The share of the total fee, in milli-satoshis, which was withheld in addition to the forwarding fee. - // This will only be set if we forwarded an intercepted HTLC with less than the expected amount. This means our - // counterparty accepted to receive less than the invoice amount. - // - // The caveat described above the `total_fee_earned_msat` field applies here as well. - optional uint64 skimmed_fee_msat = 6; - - // If this is true, the forwarded HTLC was claimed by our counterparty via an on-chain transaction. - bool claim_from_onchain_tx = 7; - - // The final amount forwarded, in milli-satoshis, after the fee is deducted. - // - // The caveat described above the `total_fee_earned_msat` field applies here as well. - optional uint64 outbound_amount_forwarded_msat = 8; - -} - -message Channel { - // The channel ID (prior to funding transaction generation, this is a random 32-byte - // identifier, afterwards this is the transaction ID of the funding transaction XOR the - // funding transaction output). - // - // Note that this means this value is *not* persistent - it can change once during the - // lifetime of the channel. - string channel_id = 1; - - // The node ID of our the channel's remote counterparty. - string counterparty_node_id = 2; - - // The channel's funding transaction output, if we've negotiated the funding transaction with - // our counterparty already. - optional OutPoint funding_txo = 3; - - // The hex-encoded local `user_channel_id` of this channel. - string user_channel_id = 4; - - // The value, in satoshis, that must always be held as a reserve in the channel for us. This - // value ensures that if we broadcast a revoked state, our counterparty can punish us by - // claiming at least this value on chain. - // - // This value is not included in [`outbound_capacity_msat`] as it can never be spent. - // - // This value will be `None` for outbound channels until the counterparty accepts the channel. - optional uint64 unspendable_punishment_reserve = 5; - - // The value, in satoshis, of this channel as it appears in the funding output. - uint64 channel_value_sats = 6; - - // The currently negotiated fee rate denominated in satoshi per 1000 weight units, - // which is applied to commitment and HTLC transactions. - uint32 feerate_sat_per_1000_weight = 7; - - // The available outbound capacity for sending HTLCs to the remote peer. - // - // The amount does not include any pending HTLCs which are not yet resolved (and, thus, whose - // balance is not available for inclusion in new outbound HTLCs). This further does not include - // any pending outgoing HTLCs which are awaiting some other resolution to be sent. - uint64 outbound_capacity_msat = 8; - - // The available outbound capacity for sending HTLCs to the remote peer. - // - // The amount does not include any pending HTLCs which are not yet resolved - // (and, thus, whose balance is not available for inclusion in new inbound HTLCs). This further - // does not include any pending outgoing HTLCs which are awaiting some other resolution to be - // sent. - uint64 inbound_capacity_msat = 9; - - // The number of required confirmations on the funding transactions before the funding is - // considered "locked". The amount is selected by the channel fundee. - // - // The value will be `None` for outbound channels until the counterparty accepts the channel. - optional uint32 confirmations_required = 10; - - // The current number of confirmations on the funding transaction. - optional uint32 confirmations = 11; - - // Is `true` if the channel was initiated (and therefore funded) by us. - bool is_outbound = 12; - - // Is `true` if both parties have exchanged `channel_ready` messages, and the channel is - // not currently being shut down. Both parties exchange `channel_ready` messages upon - // independently verifying that the required confirmations count provided by - // `confirmations_required` has been reached. - bool is_channel_ready = 13; - - // Is `true` if the channel (a) `channel_ready` messages have been exchanged, (b) the - // peer is connected, and (c) the channel is not currently negotiating shutdown. - // - // This is a strict superset of `is_channel_ready`. - bool is_usable = 14; - - // Is `true` if this channel is (or will be) publicly-announced - bool is_announced = 15; - - // Set of configurable parameters set by self that affect channel operation. - ChannelConfig channel_config = 16; - - // The available outbound capacity for sending a single HTLC to the remote peer. This is - // similar to `outbound_capacity_msat` but it may be further restricted by - // the current state and per-HTLC limit(s). This is intended for use when routing, allowing us - // to use a limit as close as possible to the HTLC limit we can currently send. - uint64 next_outbound_htlc_limit_msat = 17; - - // The minimum value for sending a single HTLC to the remote peer. This is the equivalent of - // `next_outbound_htlc_limit_msat` but represents a lower-bound, rather than - // an upper-bound. This is intended for use when routing, allowing us to ensure we pick a - // route which is valid. - uint64 next_outbound_htlc_minimum_msat = 18; - - // The number of blocks (after our commitment transaction confirms) that we will need to wait - // until we can claim our funds after we force-close the channel. During this time our - // counterparty is allowed to punish us if we broadcasted a stale state. If our counterparty - // force-closes the channel and broadcasts a commitment transaction we do not have to wait any - // time to claim our non-HTLC-encumbered funds. - // - // This value will be `None` for outbound channels until the counterparty accepts the channel. - optional uint32 force_close_spend_delay = 19; - - // The smallest value HTLC (in msat) the remote peer will accept, for this channel. - // - // This field is only `None` before we have received either the `OpenChannel` or - // `AcceptChannel` message from the remote peer. - optional uint64 counterparty_outbound_htlc_minimum_msat = 20; - - // The largest value HTLC (in msat) the remote peer currently will accept, for this channel. - optional uint64 counterparty_outbound_htlc_maximum_msat = 21; - - // The value, in satoshis, that must always be held in the channel for our counterparty. This - // value ensures that if our counterparty broadcasts a revoked state, we can punish them by - // claiming at least this value on chain. - // - // This value is not included in `inbound_capacity_msat` as it can never be spent. - uint64 counterparty_unspendable_punishment_reserve = 22; - - // Base routing fee in millisatoshis. - optional uint32 counterparty_forwarding_info_fee_base_msat = 23; - - // Proportional fee, in millionths of a satoshi the channel will charge per transferred satoshi. - optional uint32 counterparty_forwarding_info_fee_proportional_millionths = 24; - - // The minimum difference in CLTV expiry between an ingoing HTLC and its outgoing counterpart, - // such that the outgoing HTLC is forwardable to this counterparty. - optional uint32 counterparty_forwarding_info_cltv_expiry_delta = 25; -} - -// ChannelConfig represents the configuration settings for a channel in a Lightning Network node. -// See more: https://docs.rs/lightning/latest/lightning/util/config/struct.ChannelConfig.html -message ChannelConfig { - // Amount (in millionths of a satoshi) charged per satoshi for payments forwarded outbound - // over the channel. - // See more: https://docs.rs/lightning/latest/lightning/util/config/struct.ChannelConfig.html#structfield.forwarding_fee_proportional_millionths - optional uint32 forwarding_fee_proportional_millionths = 1; - - // Amount (in milli-satoshi) charged for payments forwarded outbound over the channel, - // in excess of forwarding_fee_proportional_millionths. - // See more: https://docs.rs/lightning/latest/lightning/util/config/struct.ChannelConfig.html#structfield.forwarding_fee_base_msat - optional uint32 forwarding_fee_base_msat = 2; - - // The difference in the CLTV value between incoming HTLCs and an outbound HTLC forwarded - // over the channel this config applies to. - // See more: https://docs.rs/lightning/latest/lightning/util/config/struct.ChannelConfig.html#structfield.cltv_expiry_delta - optional uint32 cltv_expiry_delta = 3; - - // The maximum additional fee we’re willing to pay to avoid waiting for the counterparty’s - // to_self_delay to reclaim funds. - // See more: https://docs.rs/lightning/latest/lightning/util/config/struct.ChannelConfig.html#structfield.force_close_avoidance_max_fee_satoshis - optional uint64 force_close_avoidance_max_fee_satoshis = 4; - - // If set, allows this channel’s counterparty to skim an additional fee off this node’s - // inbound HTLCs. Useful for liquidity providers to offload on-chain channel costs to end users. - // See more: https://docs.rs/lightning/latest/lightning/util/config/struct.ChannelConfig.html#structfield.accept_underpaying_htlcs - optional bool accept_underpaying_htlcs = 5; - - // Limit our total exposure to potential loss to on-chain fees on close, including - // in-flight HTLCs which are burned to fees as they are too small to claim on-chain - // and fees on commitment transaction(s) broadcasted by our counterparty in excess of - // our own fee estimate. - // See more: https://docs.rs/lightning/latest/lightning/util/config/struct.ChannelConfig.html#structfield.max_dust_htlc_exposure - oneof max_dust_htlc_exposure { - - // This sets a fixed limit on the total dust exposure in millisatoshis. - // See more: https://docs.rs/lightning/latest/lightning/util/config/enum.MaxDustHTLCExposure.html#variant.FixedLimitMsat - uint64 fixed_limit_msat = 6; - - // This sets a multiplier on the ConfirmationTarget::OnChainSweep feerate (in sats/KW) to determine the maximum allowed dust exposure. - // See more: https://docs.rs/lightning/latest/lightning/util/config/enum.MaxDustHTLCExposure.html#variant.FeeRateMultiplier - uint64 fee_rate_multiplier = 7; - } -} - -// Represent a transaction outpoint. -message OutPoint { - // The referenced transaction's txid. - string txid = 1; - - // The index of the referenced output in its transaction's vout. - uint32 vout = 2; -} - -message BestBlock { - // The block’s hash - string block_hash = 1; - - // The height at which the block was confirmed. - uint32 height = 2; -} - -// Details about the status of a known Lightning balance. -message LightningBalance { - oneof balance_type { - ClaimableOnChannelClose claimable_on_channel_close = 1; - ClaimableAwaitingConfirmations claimable_awaiting_confirmations = 2; - ContentiousClaimable contentious_claimable = 3; - MaybeTimeoutClaimableHTLC maybe_timeout_claimable_htlc = 4; - MaybePreimageClaimableHTLC maybe_preimage_claimable_htlc = 5; - CounterpartyRevokedOutputClaimable counterparty_revoked_output_claimable = 6; - } -} - -// The channel is not yet closed (or the commitment or closing transaction has not yet appeared in a block). -// The given balance is claimable (less on-chain fees) if the channel is force-closed now. -// See more: https://docs.rs/ldk-node/latest/ldk_node/enum.LightningBalance.html#variant.ClaimableOnChannelClose -message ClaimableOnChannelClose { - // The identifier of the channel this balance belongs to. - string channel_id = 1; - - // The identifier of our channel counterparty. - string counterparty_node_id = 2; - - // The amount available to claim, in satoshis, excluding the on-chain fees which will be required to do so. - uint64 amount_satoshis = 3; - - // The transaction fee we pay for the closing commitment transaction. - // This amount is not included in the `amount_satoshis` value. - // - // Note that if this channel is inbound (and thus our counterparty pays the commitment transaction fee) this value - // will be zero. - uint64 transaction_fee_satoshis = 4; - - // The amount of millisatoshis which has been burned to fees from HTLCs which are outbound from us and are related to - // a payment which was sent by us. This is the sum of the millisatoshis part of all HTLCs which are otherwise - // represented by `LightningBalance::MaybeTimeoutClaimableHTLC` with their - // `LightningBalance::MaybeTimeoutClaimableHTLC::outbound_payment` flag set, as well as any dust HTLCs which would - // otherwise be represented the same. - // - // This amount (rounded up to a whole satoshi value) will not be included in `amount_satoshis`. - uint64 outbound_payment_htlc_rounded_msat = 5; - - // The amount of millisatoshis which has been burned to fees from HTLCs which are outbound from us and are related to - // a forwarded HTLC. This is the sum of the millisatoshis part of all HTLCs which are otherwise represented by - // `LightningBalance::MaybeTimeoutClaimableHTLC` with their `LightningBalance::MaybeTimeoutClaimableHTLC::outbound_payment` - // flag not set, as well as any dust HTLCs which would otherwise be represented the same. - // - // This amount (rounded up to a whole satoshi value) will not be included in `amount_satoshis`. - uint64 outbound_forwarded_htlc_rounded_msat = 6; - - // The amount of millisatoshis which has been burned to fees from HTLCs which are inbound to us and for which we know - // the preimage. This is the sum of the millisatoshis part of all HTLCs which would be represented by - // `LightningBalance::ContentiousClaimable` on channel close, but whose current value is included in `amount_satoshis`, - // as well as any dust HTLCs which would otherwise be represented the same. - // - // This amount (rounded up to a whole satoshi value) will not be included in `amount_satoshis`. - uint64 inbound_claiming_htlc_rounded_msat = 7; - - // The amount of millisatoshis which has been burned to fees from HTLCs which are inbound to us and for which we do - // not know the preimage. This is the sum of the millisatoshis part of all HTLCs which would be represented by - // `LightningBalance::MaybePreimageClaimableHTLC` on channel close, as well as any dust HTLCs which would otherwise be - // represented the same. - // - // This amount (rounded up to a whole satoshi value) will not be included in the counterparty’s `amount_satoshis`. - uint64 inbound_htlc_rounded_msat = 8; -} - -// The channel has been closed, and the given balance is ours but awaiting confirmations until we consider it spendable. -// See more: https://docs.rs/ldk-node/latest/ldk_node/enum.LightningBalance.html#variant.ClaimableAwaitingConfirmations -message ClaimableAwaitingConfirmations { - // The identifier of the channel this balance belongs to. - string channel_id = 1; - - // The identifier of our channel counterparty. - string counterparty_node_id = 2; - - // The amount available to claim, in satoshis, possibly excluding the on-chain fees which were spent in broadcasting - // the transaction. - uint64 amount_satoshis = 3; - - // The height at which we start tracking it as `SpendableOutput`. - uint32 confirmation_height = 4; - - // Whether this balance is a result of cooperative close, a force-close, or an HTLC. - BalanceSource source = 5; -} - -// Indicates whether the balance is derived from a cooperative close, a force-close (for holder or counterparty), -// or whether it is for an HTLC. -enum BalanceSource { - // The channel was force closed by the holder. - HOLDER_FORCE_CLOSED = 0; - - // The channel was force closed by the counterparty. - COUNTERPARTY_FORCE_CLOSED = 1; - - // The channel was cooperatively closed. - COOP_CLOSE = 2; - - // This balance is the result of an HTLC. - HTLC = 3; -} - -// The channel has been closed, and the given balance should be ours but awaiting spending transaction confirmation. -// If the spending transaction does not confirm in time, it is possible our counterparty can take the funds by -// broadcasting an HTLC timeout on-chain. -// -// Once the spending transaction confirms, before it has reached enough confirmations to be considered safe from chain -// reorganizations, the balance will instead be provided via `LightningBalance::ClaimableAwaitingConfirmations`. -// See more: https://docs.rs/ldk-node/latest/ldk_node/enum.LightningBalance.html#variant.ContentiousClaimable -message ContentiousClaimable { - // The identifier of the channel this balance belongs to. - string channel_id = 1; - - // The identifier of our channel counterparty. - string counterparty_node_id = 2; - - // The amount available to claim, in satoshis, excluding the on-chain fees which were spent in broadcasting - // the transaction. - uint64 amount_satoshis = 3; - - // The height at which the counterparty may be able to claim the balance if we have not done so. - uint32 timeout_height = 4; - - // The payment hash that locks this HTLC. - string payment_hash = 5; - - // The preimage that can be used to claim this HTLC. - string payment_preimage = 6; -} - -// HTLCs which we sent to our counterparty which are claimable after a timeout (less on-chain fees) if the counterparty -// does not know the preimage for the HTLCs. These are somewhat likely to be claimed by our counterparty before we do. -// See more: https://docs.rs/ldk-node/latest/ldk_node/enum.LightningBalance.html#variant.MaybeTimeoutClaimableHTLC -message MaybeTimeoutClaimableHTLC { - // The identifier of the channel this balance belongs to. - string channel_id = 1; - - // The identifier of our channel counterparty. - string counterparty_node_id = 2; - - // The amount available to claim, in satoshis, excluding the on-chain fees which were spent in broadcasting - // the transaction. - uint64 amount_satoshis = 3; - - // The height at which we will be able to claim the balance if our counterparty has not done so. - uint32 claimable_height = 4; - - // The payment hash whose preimage our counterparty needs to claim this HTLC. - string payment_hash = 5; - - // Indicates whether this HTLC represents a payment which was sent outbound from us. - bool outbound_payment = 6; -} - -// HTLCs which we received from our counterparty which are claimable with a preimage which we do not currently have. -// This will only be claimable if we receive the preimage from the node to which we forwarded this HTLC before the -// timeout. -// See more: https://docs.rs/ldk-node/latest/ldk_node/enum.LightningBalance.html#variant.MaybePreimageClaimableHTLC -message MaybePreimageClaimableHTLC { - // The identifier of the channel this balance belongs to. - string channel_id = 1; - - // The identifier of our channel counterparty. - string counterparty_node_id = 2; - - // The amount available to claim, in satoshis, excluding the on-chain fees which were spent in broadcasting - // the transaction. - uint64 amount_satoshis = 3; - - // The height at which our counterparty will be able to claim the balance if we have not yet received the preimage and - // claimed it ourselves. - uint32 expiry_height = 4; - - // The payment hash whose preimage we need to claim this HTLC. - string payment_hash = 5; -} -// The channel has been closed, and our counterparty broadcasted a revoked commitment transaction. -// -// Thus, we’re able to claim all outputs in the commitment transaction, one of which has the following amount. -// -// See more: https://docs.rs/ldk-node/latest/ldk_node/enum.LightningBalance.html#variant.CounterpartyRevokedOutputClaimable -message CounterpartyRevokedOutputClaimable { - // The identifier of the channel this balance belongs to. - string channel_id = 1; - - // The identifier of our channel counterparty. - string counterparty_node_id = 2; - - // The amount, in satoshis, of the output which we can claim. - uint64 amount_satoshis = 3; -} - -// Details about the status of a known balance currently being swept to our on-chain wallet. -message PendingSweepBalance { - oneof balance_type { - PendingBroadcast pending_broadcast = 1; - BroadcastAwaitingConfirmation broadcast_awaiting_confirmation = 2; - AwaitingThresholdConfirmations awaiting_threshold_confirmations = 3; - } -} - -// The spendable output is about to be swept, but a spending transaction has yet to be generated and broadcast. -// See more: https://docs.rs/ldk-node/latest/ldk_node/enum.PendingSweepBalance.html#variant.PendingBroadcast -message PendingBroadcast { - // The identifier of the channel this balance belongs to. - optional string channel_id = 1; - - // The amount, in satoshis, of the output being swept. - uint64 amount_satoshis = 2; -} - -// A spending transaction has been generated and broadcast and is awaiting confirmation on-chain. -// See more: https://docs.rs/ldk-node/latest/ldk_node/enum.PendingSweepBalance.html#variant.BroadcastAwaitingConfirmation -message BroadcastAwaitingConfirmation { - // The identifier of the channel this balance belongs to. - optional string channel_id = 1; - - // The best height when we last broadcast a transaction spending the output being swept. - uint32 latest_broadcast_height = 2; - - // The identifier of the transaction spending the swept output we last broadcast. - string latest_spending_txid = 3; - - // The amount, in satoshis, of the output being swept. - uint64 amount_satoshis = 4; -} - -// A spending transaction has been confirmed on-chain and is awaiting threshold confirmations. -// -// It will be considered irrevocably confirmed after reaching `ANTI_REORG_DELAY`. -// See more: https://docs.rs/ldk-node/latest/ldk_node/enum.PendingSweepBalance.html#variant.AwaitingThresholdConfirmations -message AwaitingThresholdConfirmations { - // The identifier of the channel this balance belongs to. - optional string channel_id = 1; - - // The identifier of the confirmed transaction spending the swept output. - string latest_spending_txid = 2; - - // The hash of the block in which the spending transaction was confirmed. - string confirmation_hash = 3; - - // The height at which the spending transaction was confirmed. - uint32 confirmation_height = 4; - - // The amount, in satoshis, of the output being swept. - uint64 amount_satoshis = 5; -} - -// Token used to determine start of next page in paginated APIs. -message PageToken { - string token = 1; - int64 index = 2; -} - -message Bolt11InvoiceDescription { - oneof kind { - string direct = 1; - string hash = 2; - } -} - -// Configuration options for payment routing and pathfinding. -// See https://docs.rs/lightning/0.2.0/lightning/routing/router/struct.RouteParametersConfig.html for more details on each field. -message RouteParametersConfig { - // The maximum total fees, in millisatoshi, that may accrue during route finding. - // Defaults to 1% of the payment amount + 50 sats - optional uint64 max_total_routing_fee_msat = 1; - - // The maximum total CLTV delta we accept for the route. - // Defaults to 1008. - uint32 max_total_cltv_expiry_delta = 2; - - // The maximum number of paths that may be used by (MPP) payments. - // Defaults to 10. - uint32 max_path_count = 3; - - // Selects the maximum share of a channel's total capacity which will be - // sent over a channel, as a power of 1/2. - // Default value: 2 - uint32 max_channel_saturation_power_of_half = 4; -} - -// Routing fees for a channel as part of the network graph. -message GraphRoutingFees { - // Flat routing fee in millisatoshis. - uint32 base_msat = 1; - - // Liquidity-based routing fee in millionths of a routed amount. - uint32 proportional_millionths = 2; -} - -// Details about one direction of a channel in the network graph, -// as received within a `ChannelUpdate`. -message GraphChannelUpdate { - // When the last update to the channel direction was issued. - // Value is opaque, as set in the announcement. - uint32 last_update = 1; - - // Whether the channel can be currently used for payments (in this one direction). - bool enabled = 2; - - // The difference in CLTV values that you must have when routing through this channel. - uint32 cltv_expiry_delta = 3; - - // The minimum value, which must be relayed to the next hop via the channel. - uint64 htlc_minimum_msat = 4; - - // The maximum value which may be relayed to the next hop via the channel. - uint64 htlc_maximum_msat = 5; - - // Fees charged when the channel is used for routing. - GraphRoutingFees fees = 6; -} - -// Details about a channel in the network graph (both directions). -// Received within a channel announcement. -message GraphChannel { - // Source node of the first direction of the channel (hex-encoded public key). - string node_one = 1; - - // Source node of the second direction of the channel (hex-encoded public key). - string node_two = 2; - - // The channel capacity as seen on-chain, if chain lookup is available. - optional uint64 capacity_sats = 3; - - // Details about the first direction of a channel. - GraphChannelUpdate one_to_two = 4; - - // Details about the second direction of a channel. - GraphChannelUpdate two_to_one = 5; -} - -// Information received in the latest node_announcement from this node. -message GraphNodeAnnouncement { - // When the last known update to the node state was issued. - // Value is opaque, as set in the announcement. - uint32 last_update = 1; - - // Moniker assigned to the node. - // May be invalid or malicious (eg control chars), should not be exposed to the user. - string alias = 2; - - // Color assigned to the node as a hex-encoded RGB string, e.g. "ff0000". - string rgb = 3; - - // List of addresses on which this node is reachable. - repeated string addresses = 4; -} - -// Details of a known Lightning peer. -// See more: https://docs.rs/ldk-node/latest/ldk_node/struct.Node.html#method.list_peers -message Peer { - // The hex-encoded node ID of the peer. - string node_id = 1; - - // The network address of the peer. - string address = 2; - - // Indicates whether we'll try to reconnect to this peer after restarts. - bool is_persisted = 3; - - // Indicates whether we currently have an active connection with the peer. - bool is_connected = 4; -} - -// Details about a node in the network graph, known from the network announcement. -message GraphNode { - // All valid channels a node has announced. - repeated uint64 channels = 1; - - // More information about a node from node_announcement. - // Optional because we store a node entry after learning about it from - // a channel announcement, but before receiving a node announcement. - GraphNodeAnnouncement announcement_info = 2; -} diff --git a/ldk-server-protos/src/serde_utils.rs b/ldk-server-protos/src/serde_utils.rs deleted file mode 100644 index 5f10d137..00000000 --- a/ldk-server-protos/src/serde_utils.rs +++ /dev/null @@ -1,58 +0,0 @@ -// This file is Copyright its original authors, visible in version control -// history. -// -// This file is licensed under the Apache License, Version 2.0 or the MIT license -// , at your option. -// You may not use this file except in accordance with one or both of these -// licenses. - -//! Custom serde serializers for proto types. -//! -//! These are used via `#[serde(serialize_with = "...")]` attributes on generated -//! proto fields to produce human-readable output (hex strings for bytes, enum -//! names for integer enum fields). - -use std::fmt::Write; - -use serde::Serializer; - -/// Generates a serde serializer that converts an `i32` proto enum field to its -/// string name via `from_i32()` and `as_str_name()`. -macro_rules! stringify_enum_serializer { - ($fn_name:ident, $enum_type:ty) => { - pub fn $fn_name(value: &i32, serializer: S) -> Result - where - S: serde::Serializer, - { - let name = match <$enum_type>::from_i32(*value) { - Some(v) => v.as_str_name(), - None => "UNKNOWN", - }; - serializer.serialize_str(name) - } - }; -} - -stringify_enum_serializer!(serialize_payment_direction, crate::types::PaymentDirection); -stringify_enum_serializer!(serialize_payment_status, crate::types::PaymentStatus); -stringify_enum_serializer!(serialize_balance_source, crate::types::BalanceSource); - -/// Serializes `Option` as a hex string (or null). -pub fn serialize_opt_bytes_hex( - value: &Option, serializer: S, -) -> Result -where - S: Serializer, -{ - match value { - Some(bytes) => { - let hex = bytes.iter().fold(String::with_capacity(bytes.len() * 2), |mut acc, b| { - let _ = write!(acc, "{b:02x}"); - acc - }); - serializer.serialize_some(&hex) - }, - None => serializer.serialize_none(), - } -} diff --git a/ldk-server-protos/src/types.rs b/ldk-server-protos/src/types.rs deleted file mode 100644 index 280e34f4..00000000 --- a/ldk-server-protos/src/types.rs +++ /dev/null @@ -1,1143 +0,0 @@ -// This file is Copyright its original authors, visible in version control -// history. -// -// This file is licensed under the Apache License, Version 2.0 or the MIT license -// , at your option. -// You may not use this file except in accordance with one or both of these -// licenses. - -/// Represents a payment. -/// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Payment { - /// An identifier used to uniquely identify a payment in hex-encoded form. - #[prost(string, tag = "1")] - pub id: ::prost::alloc::string::String, - /// The kind of the payment. - #[prost(message, optional, tag = "2")] - pub kind: ::core::option::Option, - /// The amount transferred. - #[prost(uint64, optional, tag = "3")] - pub amount_msat: ::core::option::Option, - /// The fees that were paid for this payment. - /// - /// For Lightning payments, this will only be updated for outbound payments once they - /// succeeded. - #[prost(uint64, optional, tag = "7")] - pub fee_paid_msat: ::core::option::Option, - /// The direction of the payment. - #[prost(enumeration = "PaymentDirection", tag = "4")] - #[cfg_attr( - feature = "serde", - serde(serialize_with = "crate::serde_utils::serialize_payment_direction") - )] - pub direction: i32, - /// The status of the payment. - #[prost(enumeration = "PaymentStatus", tag = "5")] - #[cfg_attr( - feature = "serde", - serde(serialize_with = "crate::serde_utils::serialize_payment_status") - )] - pub status: i32, - /// The timestamp, in seconds since start of the UNIX epoch, when this entry was last updated. - #[prost(uint64, tag = "6")] - pub latest_update_timestamp: u64, -} -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct PaymentKind { - #[prost(oneof = "payment_kind::Kind", tags = "1, 2, 3, 4, 5, 6")] - pub kind: ::core::option::Option, -} -/// Nested message and enum types in `PaymentKind`. -pub mod payment_kind { - #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] - #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] - #[allow(clippy::derive_partial_eq_without_eq)] - #[derive(Clone, PartialEq, ::prost::Oneof)] - pub enum Kind { - #[prost(message, tag = "1")] - Onchain(super::Onchain), - #[prost(message, tag = "2")] - Bolt11(super::Bolt11), - #[prost(message, tag = "3")] - Bolt11Jit(super::Bolt11Jit), - #[prost(message, tag = "4")] - Bolt12Offer(super::Bolt12Offer), - #[prost(message, tag = "5")] - Bolt12Refund(super::Bolt12Refund), - #[prost(message, tag = "6")] - Spontaneous(super::Spontaneous), - } -} -/// Represents an on-chain payment. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Onchain { - /// The transaction identifier of this payment. - #[prost(string, tag = "1")] - pub txid: ::prost::alloc::string::String, - /// The confirmation status of this payment. - #[prost(message, optional, tag = "2")] - pub status: ::core::option::Option, -} -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct ConfirmationStatus { - #[prost(oneof = "confirmation_status::Status", tags = "1, 2")] - pub status: ::core::option::Option, -} -/// Nested message and enum types in `ConfirmationStatus`. -pub mod confirmation_status { - #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] - #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] - #[allow(clippy::derive_partial_eq_without_eq)] - #[derive(Clone, PartialEq, ::prost::Oneof)] - pub enum Status { - #[prost(message, tag = "1")] - Confirmed(super::Confirmed), - #[prost(message, tag = "2")] - Unconfirmed(super::Unconfirmed), - } -} -/// The on-chain transaction is confirmed in the best chain. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Confirmed { - /// The hex representation of hash of the block in which the transaction was confirmed. - #[prost(string, tag = "1")] - pub block_hash: ::prost::alloc::string::String, - /// The height under which the block was confirmed. - #[prost(uint32, tag = "2")] - pub height: u32, - /// The timestamp, in seconds since start of the UNIX epoch, when this entry was last updated. - #[prost(uint64, tag = "3")] - pub timestamp: u64, -} -/// The on-chain transaction is unconfirmed. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Unconfirmed {} -/// Represents a BOLT 11 payment. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Bolt11 { - /// The payment hash, i.e., the hash of the preimage. - #[prost(string, tag = "1")] - pub hash: ::prost::alloc::string::String, - /// The pre-image used by the payment. - #[prost(string, optional, tag = "2")] - pub preimage: ::core::option::Option<::prost::alloc::string::String>, - /// The secret used by the payment. - #[prost(bytes = "bytes", optional, tag = "3")] - #[cfg_attr( - feature = "serde", - serde(serialize_with = "crate::serde_utils::serialize_opt_bytes_hex") - )] - pub secret: ::core::option::Option<::prost::bytes::Bytes>, -} -/// Represents a BOLT 11 payment intended to open an LSPS 2 just-in-time channel. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Bolt11Jit { - /// The payment hash, i.e., the hash of the preimage. - #[prost(string, tag = "1")] - pub hash: ::prost::alloc::string::String, - /// The pre-image used by the payment. - #[prost(string, optional, tag = "2")] - pub preimage: ::core::option::Option<::prost::alloc::string::String>, - /// The secret used by the payment. - #[prost(bytes = "bytes", optional, tag = "3")] - #[cfg_attr( - feature = "serde", - serde(serialize_with = "crate::serde_utils::serialize_opt_bytes_hex") - )] - pub secret: ::core::option::Option<::prost::bytes::Bytes>, - /// Limits applying to how much fee we allow an LSP to deduct from the payment amount. - /// - /// Allowing them to deduct this fee from the first inbound payment will pay for the LSP’s channel opening fees. - /// - /// See \[`LdkChannelConfig::accept_underpaying_htlcs`\]() - /// for more information. - #[prost(message, optional, tag = "4")] - pub lsp_fee_limits: ::core::option::Option, - /// The value, in thousands of a satoshi, that was deducted from this payment as an extra - /// fee taken by our channel counterparty. - /// - /// Will only be `Some` once we received the payment. - #[prost(uint64, optional, tag = "5")] - pub counterparty_skimmed_fee_msat: ::core::option::Option, -} -/// Represents a BOLT 12 ‘offer’ payment, i.e., a payment for an Offer. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Bolt12Offer { - /// The payment hash, i.e., the hash of the preimage. - #[prost(string, optional, tag = "1")] - pub hash: ::core::option::Option<::prost::alloc::string::String>, - /// The pre-image used by the payment. - #[prost(string, optional, tag = "2")] - pub preimage: ::core::option::Option<::prost::alloc::string::String>, - /// The secret used by the payment. - #[prost(bytes = "bytes", optional, tag = "3")] - #[cfg_attr( - feature = "serde", - serde(serialize_with = "crate::serde_utils::serialize_opt_bytes_hex") - )] - pub secret: ::core::option::Option<::prost::bytes::Bytes>, - /// The hex-encoded ID of the offer this payment is for. - #[prost(string, tag = "4")] - pub offer_id: ::prost::alloc::string::String, - /// The payer's note for the payment. - /// Truncated to \[PAYER_NOTE_LIMIT\](). - /// - /// **Caution**: The `payer_note` field may come from an untrusted source. To prevent potential misuse, - /// all non-printable characters will be sanitized and replaced with safe characters. - #[prost(string, optional, tag = "5")] - pub payer_note: ::core::option::Option<::prost::alloc::string::String>, - /// The quantity of an item requested in the offer. - #[prost(uint64, optional, tag = "6")] - pub quantity: ::core::option::Option, -} -/// Represents a BOLT 12 ‘refund’ payment, i.e., a payment for a Refund. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Bolt12Refund { - /// The payment hash, i.e., the hash of the preimage. - #[prost(string, optional, tag = "1")] - pub hash: ::core::option::Option<::prost::alloc::string::String>, - /// The pre-image used by the payment. - #[prost(string, optional, tag = "2")] - pub preimage: ::core::option::Option<::prost::alloc::string::String>, - /// The secret used by the payment. - #[prost(bytes = "bytes", optional, tag = "3")] - #[cfg_attr( - feature = "serde", - serde(serialize_with = "crate::serde_utils::serialize_opt_bytes_hex") - )] - pub secret: ::core::option::Option<::prost::bytes::Bytes>, - /// The payer's note for the payment. - /// Truncated to \[PAYER_NOTE_LIMIT\](). - /// - /// **Caution**: The `payer_note` field may come from an untrusted source. To prevent potential misuse, - /// all non-printable characters will be sanitized and replaced with safe characters. - #[prost(string, optional, tag = "5")] - pub payer_note: ::core::option::Option<::prost::alloc::string::String>, - /// The quantity of an item requested in the offer. - #[prost(uint64, optional, tag = "6")] - pub quantity: ::core::option::Option, -} -/// Represents a spontaneous (“keysend”) payment. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Spontaneous { - /// The payment hash, i.e., the hash of the preimage. - #[prost(string, tag = "1")] - pub hash: ::prost::alloc::string::String, - /// The pre-image used by the payment. - #[prost(string, optional, tag = "2")] - pub preimage: ::core::option::Option<::prost::alloc::string::String>, -} -/// Limits applying to how much fee we allow an LSP to deduct from the payment amount. -/// See \[`LdkChannelConfig::accept_underpaying_htlcs`\] for more information. -/// -/// \[`LdkChannelConfig::accept_underpaying_htlcs`\]: lightning::util::config::ChannelConfig::accept_underpaying_htlcs -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct LspFeeLimits { - /// The maximal total amount we allow any configured LSP withhold from us when forwarding the - /// payment. - #[prost(uint64, optional, tag = "1")] - pub max_total_opening_fee_msat: ::core::option::Option, - /// The maximal proportional fee, in parts-per-million millisatoshi, we allow any configured - /// LSP withhold from us when forwarding the payment. - #[prost(uint64, optional, tag = "2")] - pub max_proportional_opening_fee_ppm_msat: ::core::option::Option, -} -/// A forwarded payment through our node. -/// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct ForwardedPayment { - /// The channel id of the incoming channel between the previous node and us. - #[prost(string, tag = "1")] - pub prev_channel_id: ::prost::alloc::string::String, - /// The channel id of the outgoing channel between the next node and us. - #[prost(string, tag = "2")] - pub next_channel_id: ::prost::alloc::string::String, - /// The `user_channel_id` of the incoming channel between the previous node and us. - #[prost(string, tag = "3")] - pub prev_user_channel_id: ::prost::alloc::string::String, - /// The node id of the previous node. - #[prost(string, tag = "9")] - pub prev_node_id: ::prost::alloc::string::String, - /// The node id of the next node. - #[prost(string, tag = "10")] - pub next_node_id: ::prost::alloc::string::String, - /// The `user_channel_id` of the outgoing channel between the next node and us. - /// This will be `None` if the payment was settled via an on-chain transaction. - /// See the caveat described for the `total_fee_earned_msat` field. - #[prost(string, optional, tag = "4")] - pub next_user_channel_id: ::core::option::Option<::prost::alloc::string::String>, - /// The total fee, in milli-satoshis, which was earned as a result of the payment. - /// - /// Note that if we force-closed the channel over which we forwarded an HTLC while the HTLC was pending, the amount the - /// next hop claimed will have been rounded down to the nearest whole satoshi. Thus, the fee calculated here may be - /// higher than expected as we still claimed the full value in millisatoshis from the source. - /// In this case, `claim_from_onchain_tx` will be set. - /// - /// If the channel which sent us the payment has been force-closed, we will claim the funds via an on-chain transaction. - /// In that case we do not yet know the on-chain transaction fees which we will spend and will instead set this to `None`. - #[prost(uint64, optional, tag = "5")] - pub total_fee_earned_msat: ::core::option::Option, - /// The share of the total fee, in milli-satoshis, which was withheld in addition to the forwarding fee. - /// This will only be set if we forwarded an intercepted HTLC with less than the expected amount. This means our - /// counterparty accepted to receive less than the invoice amount. - /// - /// The caveat described above the `total_fee_earned_msat` field applies here as well. - #[prost(uint64, optional, tag = "6")] - pub skimmed_fee_msat: ::core::option::Option, - /// If this is true, the forwarded HTLC was claimed by our counterparty via an on-chain transaction. - #[prost(bool, tag = "7")] - pub claim_from_onchain_tx: bool, - /// The final amount forwarded, in milli-satoshis, after the fee is deducted. - /// - /// The caveat described above the `total_fee_earned_msat` field applies here as well. - #[prost(uint64, optional, tag = "8")] - pub outbound_amount_forwarded_msat: ::core::option::Option, -} -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Channel { - /// The channel ID (prior to funding transaction generation, this is a random 32-byte - /// identifier, afterwards this is the transaction ID of the funding transaction XOR the - /// funding transaction output). - /// - /// Note that this means this value is *not* persistent - it can change once during the - /// lifetime of the channel. - #[prost(string, tag = "1")] - pub channel_id: ::prost::alloc::string::String, - /// The node ID of our the channel's remote counterparty. - #[prost(string, tag = "2")] - pub counterparty_node_id: ::prost::alloc::string::String, - /// The channel's funding transaction output, if we've negotiated the funding transaction with - /// our counterparty already. - #[prost(message, optional, tag = "3")] - pub funding_txo: ::core::option::Option, - /// The hex-encoded local `user_channel_id` of this channel. - #[prost(string, tag = "4")] - pub user_channel_id: ::prost::alloc::string::String, - /// The value, in satoshis, that must always be held as a reserve in the channel for us. This - /// value ensures that if we broadcast a revoked state, our counterparty can punish us by - /// claiming at least this value on chain. - /// - /// This value is not included in \[`outbound_capacity_msat`\] as it can never be spent. - /// - /// This value will be `None` for outbound channels until the counterparty accepts the channel. - #[prost(uint64, optional, tag = "5")] - pub unspendable_punishment_reserve: ::core::option::Option, - /// The value, in satoshis, of this channel as it appears in the funding output. - #[prost(uint64, tag = "6")] - pub channel_value_sats: u64, - /// The currently negotiated fee rate denominated in satoshi per 1000 weight units, - /// which is applied to commitment and HTLC transactions. - #[prost(uint32, tag = "7")] - pub feerate_sat_per_1000_weight: u32, - /// The available outbound capacity for sending HTLCs to the remote peer. - /// - /// The amount does not include any pending HTLCs which are not yet resolved (and, thus, whose - /// balance is not available for inclusion in new outbound HTLCs). This further does not include - /// any pending outgoing HTLCs which are awaiting some other resolution to be sent. - #[prost(uint64, tag = "8")] - pub outbound_capacity_msat: u64, - /// The available outbound capacity for sending HTLCs to the remote peer. - /// - /// The amount does not include any pending HTLCs which are not yet resolved - /// (and, thus, whose balance is not available for inclusion in new inbound HTLCs). This further - /// does not include any pending outgoing HTLCs which are awaiting some other resolution to be - /// sent. - #[prost(uint64, tag = "9")] - pub inbound_capacity_msat: u64, - /// The number of required confirmations on the funding transactions before the funding is - /// considered "locked". The amount is selected by the channel fundee. - /// - /// The value will be `None` for outbound channels until the counterparty accepts the channel. - #[prost(uint32, optional, tag = "10")] - pub confirmations_required: ::core::option::Option, - /// The current number of confirmations on the funding transaction. - #[prost(uint32, optional, tag = "11")] - pub confirmations: ::core::option::Option, - /// Is `true` if the channel was initiated (and therefore funded) by us. - #[prost(bool, tag = "12")] - pub is_outbound: bool, - /// Is `true` if both parties have exchanged `channel_ready` messages, and the channel is - /// not currently being shut down. Both parties exchange `channel_ready` messages upon - /// independently verifying that the required confirmations count provided by - /// `confirmations_required` has been reached. - #[prost(bool, tag = "13")] - pub is_channel_ready: bool, - /// Is `true` if the channel (a) `channel_ready` messages have been exchanged, (b) the - /// peer is connected, and (c) the channel is not currently negotiating shutdown. - /// - /// This is a strict superset of `is_channel_ready`. - #[prost(bool, tag = "14")] - pub is_usable: bool, - /// Is `true` if this channel is (or will be) publicly-announced - #[prost(bool, tag = "15")] - pub is_announced: bool, - /// Set of configurable parameters set by self that affect channel operation. - #[prost(message, optional, tag = "16")] - pub channel_config: ::core::option::Option, - /// The available outbound capacity for sending a single HTLC to the remote peer. This is - /// similar to `outbound_capacity_msat` but it may be further restricted by - /// the current state and per-HTLC limit(s). This is intended for use when routing, allowing us - /// to use a limit as close as possible to the HTLC limit we can currently send. - #[prost(uint64, tag = "17")] - pub next_outbound_htlc_limit_msat: u64, - /// The minimum value for sending a single HTLC to the remote peer. This is the equivalent of - /// `next_outbound_htlc_limit_msat` but represents a lower-bound, rather than - /// an upper-bound. This is intended for use when routing, allowing us to ensure we pick a - /// route which is valid. - #[prost(uint64, tag = "18")] - pub next_outbound_htlc_minimum_msat: u64, - /// The number of blocks (after our commitment transaction confirms) that we will need to wait - /// until we can claim our funds after we force-close the channel. During this time our - /// counterparty is allowed to punish us if we broadcasted a stale state. If our counterparty - /// force-closes the channel and broadcasts a commitment transaction we do not have to wait any - /// time to claim our non-HTLC-encumbered funds. - /// - /// This value will be `None` for outbound channels until the counterparty accepts the channel. - #[prost(uint32, optional, tag = "19")] - pub force_close_spend_delay: ::core::option::Option, - /// The smallest value HTLC (in msat) the remote peer will accept, for this channel. - /// - /// This field is only `None` before we have received either the `OpenChannel` or - /// `AcceptChannel` message from the remote peer. - #[prost(uint64, optional, tag = "20")] - pub counterparty_outbound_htlc_minimum_msat: ::core::option::Option, - /// The largest value HTLC (in msat) the remote peer currently will accept, for this channel. - #[prost(uint64, optional, tag = "21")] - pub counterparty_outbound_htlc_maximum_msat: ::core::option::Option, - /// The value, in satoshis, that must always be held in the channel for our counterparty. This - /// value ensures that if our counterparty broadcasts a revoked state, we can punish them by - /// claiming at least this value on chain. - /// - /// This value is not included in `inbound_capacity_msat` as it can never be spent. - #[prost(uint64, tag = "22")] - pub counterparty_unspendable_punishment_reserve: u64, - /// Base routing fee in millisatoshis. - #[prost(uint32, optional, tag = "23")] - pub counterparty_forwarding_info_fee_base_msat: ::core::option::Option, - /// Proportional fee, in millionths of a satoshi the channel will charge per transferred satoshi. - #[prost(uint32, optional, tag = "24")] - pub counterparty_forwarding_info_fee_proportional_millionths: ::core::option::Option, - /// The minimum difference in CLTV expiry between an ingoing HTLC and its outgoing counterpart, - /// such that the outgoing HTLC is forwardable to this counterparty. - #[prost(uint32, optional, tag = "25")] - pub counterparty_forwarding_info_cltv_expiry_delta: ::core::option::Option, -} -/// ChannelConfig represents the configuration settings for a channel in a Lightning Network node. -/// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct ChannelConfig { - /// Amount (in millionths of a satoshi) charged per satoshi for payments forwarded outbound - /// over the channel. - /// See more: - #[prost(uint32, optional, tag = "1")] - pub forwarding_fee_proportional_millionths: ::core::option::Option, - /// Amount (in milli-satoshi) charged for payments forwarded outbound over the channel, - /// in excess of forwarding_fee_proportional_millionths. - /// See more: - #[prost(uint32, optional, tag = "2")] - pub forwarding_fee_base_msat: ::core::option::Option, - /// The difference in the CLTV value between incoming HTLCs and an outbound HTLC forwarded - /// over the channel this config applies to. - /// See more: - #[prost(uint32, optional, tag = "3")] - pub cltv_expiry_delta: ::core::option::Option, - /// The maximum additional fee we’re willing to pay to avoid waiting for the counterparty’s - /// to_self_delay to reclaim funds. - /// See more: - #[prost(uint64, optional, tag = "4")] - pub force_close_avoidance_max_fee_satoshis: ::core::option::Option, - /// If set, allows this channel’s counterparty to skim an additional fee off this node’s - /// inbound HTLCs. Useful for liquidity providers to offload on-chain channel costs to end users. - /// See more: - #[prost(bool, optional, tag = "5")] - pub accept_underpaying_htlcs: ::core::option::Option, - /// Limit our total exposure to potential loss to on-chain fees on close, including - /// in-flight HTLCs which are burned to fees as they are too small to claim on-chain - /// and fees on commitment transaction(s) broadcasted by our counterparty in excess of - /// our own fee estimate. - /// See more: - #[prost(oneof = "channel_config::MaxDustHtlcExposure", tags = "6, 7")] - pub max_dust_htlc_exposure: ::core::option::Option, -} -/// Nested message and enum types in `ChannelConfig`. -pub mod channel_config { - /// Limit our total exposure to potential loss to on-chain fees on close, including - /// in-flight HTLCs which are burned to fees as they are too small to claim on-chain - /// and fees on commitment transaction(s) broadcasted by our counterparty in excess of - /// our own fee estimate. - /// See more: - #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] - #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] - #[allow(clippy::derive_partial_eq_without_eq)] - #[derive(Clone, PartialEq, ::prost::Oneof)] - pub enum MaxDustHtlcExposure { - /// This sets a fixed limit on the total dust exposure in millisatoshis. - /// See more: - #[prost(uint64, tag = "6")] - FixedLimitMsat(u64), - /// This sets a multiplier on the ConfirmationTarget::OnChainSweep feerate (in sats/KW) to determine the maximum allowed dust exposure. - /// See more: - #[prost(uint64, tag = "7")] - FeeRateMultiplier(u64), - } -} -/// Represent a transaction outpoint. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct OutPoint { - /// The referenced transaction's txid. - #[prost(string, tag = "1")] - pub txid: ::prost::alloc::string::String, - /// The index of the referenced output in its transaction's vout. - #[prost(uint32, tag = "2")] - pub vout: u32, -} -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct BestBlock { - /// The block’s hash - #[prost(string, tag = "1")] - pub block_hash: ::prost::alloc::string::String, - /// The height at which the block was confirmed. - #[prost(uint32, tag = "2")] - pub height: u32, -} -/// Details about the status of a known Lightning balance. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct LightningBalance { - #[prost(oneof = "lightning_balance::BalanceType", tags = "1, 2, 3, 4, 5, 6")] - pub balance_type: ::core::option::Option, -} -/// Nested message and enum types in `LightningBalance`. -pub mod lightning_balance { - #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] - #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] - #[allow(clippy::derive_partial_eq_without_eq)] - #[derive(Clone, PartialEq, ::prost::Oneof)] - pub enum BalanceType { - #[prost(message, tag = "1")] - ClaimableOnChannelClose(super::ClaimableOnChannelClose), - #[prost(message, tag = "2")] - ClaimableAwaitingConfirmations(super::ClaimableAwaitingConfirmations), - #[prost(message, tag = "3")] - ContentiousClaimable(super::ContentiousClaimable), - #[prost(message, tag = "4")] - MaybeTimeoutClaimableHtlc(super::MaybeTimeoutClaimableHtlc), - #[prost(message, tag = "5")] - MaybePreimageClaimableHtlc(super::MaybePreimageClaimableHtlc), - #[prost(message, tag = "6")] - CounterpartyRevokedOutputClaimable(super::CounterpartyRevokedOutputClaimable), - } -} -/// The channel is not yet closed (or the commitment or closing transaction has not yet appeared in a block). -/// The given balance is claimable (less on-chain fees) if the channel is force-closed now. -/// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct ClaimableOnChannelClose { - /// The identifier of the channel this balance belongs to. - #[prost(string, tag = "1")] - pub channel_id: ::prost::alloc::string::String, - /// The identifier of our channel counterparty. - #[prost(string, tag = "2")] - pub counterparty_node_id: ::prost::alloc::string::String, - /// The amount available to claim, in satoshis, excluding the on-chain fees which will be required to do so. - #[prost(uint64, tag = "3")] - pub amount_satoshis: u64, - /// The transaction fee we pay for the closing commitment transaction. - /// This amount is not included in the `amount_satoshis` value. - /// - /// Note that if this channel is inbound (and thus our counterparty pays the commitment transaction fee) this value - /// will be zero. - #[prost(uint64, tag = "4")] - pub transaction_fee_satoshis: u64, - /// The amount of millisatoshis which has been burned to fees from HTLCs which are outbound from us and are related to - /// a payment which was sent by us. This is the sum of the millisatoshis part of all HTLCs which are otherwise - /// represented by `LightningBalance::MaybeTimeoutClaimableHTLC` with their - /// `LightningBalance::MaybeTimeoutClaimableHTLC::outbound_payment` flag set, as well as any dust HTLCs which would - /// otherwise be represented the same. - /// - /// This amount (rounded up to a whole satoshi value) will not be included in `amount_satoshis`. - #[prost(uint64, tag = "5")] - pub outbound_payment_htlc_rounded_msat: u64, - /// The amount of millisatoshis which has been burned to fees from HTLCs which are outbound from us and are related to - /// a forwarded HTLC. This is the sum of the millisatoshis part of all HTLCs which are otherwise represented by - /// `LightningBalance::MaybeTimeoutClaimableHTLC` with their `LightningBalance::MaybeTimeoutClaimableHTLC::outbound_payment` - /// flag not set, as well as any dust HTLCs which would otherwise be represented the same. - /// - /// This amount (rounded up to a whole satoshi value) will not be included in `amount_satoshis`. - #[prost(uint64, tag = "6")] - pub outbound_forwarded_htlc_rounded_msat: u64, - /// The amount of millisatoshis which has been burned to fees from HTLCs which are inbound to us and for which we know - /// the preimage. This is the sum of the millisatoshis part of all HTLCs which would be represented by - /// `LightningBalance::ContentiousClaimable` on channel close, but whose current value is included in `amount_satoshis`, - /// as well as any dust HTLCs which would otherwise be represented the same. - /// - /// This amount (rounded up to a whole satoshi value) will not be included in `amount_satoshis`. - #[prost(uint64, tag = "7")] - pub inbound_claiming_htlc_rounded_msat: u64, - /// The amount of millisatoshis which has been burned to fees from HTLCs which are inbound to us and for which we do - /// not know the preimage. This is the sum of the millisatoshis part of all HTLCs which would be represented by - /// `LightningBalance::MaybePreimageClaimableHTLC` on channel close, as well as any dust HTLCs which would otherwise be - /// represented the same. - /// - /// This amount (rounded up to a whole satoshi value) will not be included in the counterparty’s `amount_satoshis`. - #[prost(uint64, tag = "8")] - pub inbound_htlc_rounded_msat: u64, -} -/// The channel has been closed, and the given balance is ours but awaiting confirmations until we consider it spendable. -/// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct ClaimableAwaitingConfirmations { - /// The identifier of the channel this balance belongs to. - #[prost(string, tag = "1")] - pub channel_id: ::prost::alloc::string::String, - /// The identifier of our channel counterparty. - #[prost(string, tag = "2")] - pub counterparty_node_id: ::prost::alloc::string::String, - /// The amount available to claim, in satoshis, possibly excluding the on-chain fees which were spent in broadcasting - /// the transaction. - #[prost(uint64, tag = "3")] - pub amount_satoshis: u64, - /// The height at which we start tracking it as `SpendableOutput`. - #[prost(uint32, tag = "4")] - pub confirmation_height: u32, - /// Whether this balance is a result of cooperative close, a force-close, or an HTLC. - #[prost(enumeration = "BalanceSource", tag = "5")] - #[cfg_attr( - feature = "serde", - serde(serialize_with = "crate::serde_utils::serialize_balance_source") - )] - pub source: i32, -} -/// The channel has been closed, and the given balance should be ours but awaiting spending transaction confirmation. -/// If the spending transaction does not confirm in time, it is possible our counterparty can take the funds by -/// broadcasting an HTLC timeout on-chain. -/// -/// Once the spending transaction confirms, before it has reached enough confirmations to be considered safe from chain -/// reorganizations, the balance will instead be provided via `LightningBalance::ClaimableAwaitingConfirmations`. -/// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct ContentiousClaimable { - /// The identifier of the channel this balance belongs to. - #[prost(string, tag = "1")] - pub channel_id: ::prost::alloc::string::String, - /// The identifier of our channel counterparty. - #[prost(string, tag = "2")] - pub counterparty_node_id: ::prost::alloc::string::String, - /// The amount available to claim, in satoshis, excluding the on-chain fees which were spent in broadcasting - /// the transaction. - #[prost(uint64, tag = "3")] - pub amount_satoshis: u64, - /// The height at which the counterparty may be able to claim the balance if we have not done so. - #[prost(uint32, tag = "4")] - pub timeout_height: u32, - /// The payment hash that locks this HTLC. - #[prost(string, tag = "5")] - pub payment_hash: ::prost::alloc::string::String, - /// The preimage that can be used to claim this HTLC. - #[prost(string, tag = "6")] - pub payment_preimage: ::prost::alloc::string::String, -} -/// HTLCs which we sent to our counterparty which are claimable after a timeout (less on-chain fees) if the counterparty -/// does not know the preimage for the HTLCs. These are somewhat likely to be claimed by our counterparty before we do. -/// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct MaybeTimeoutClaimableHtlc { - /// The identifier of the channel this balance belongs to. - #[prost(string, tag = "1")] - pub channel_id: ::prost::alloc::string::String, - /// The identifier of our channel counterparty. - #[prost(string, tag = "2")] - pub counterparty_node_id: ::prost::alloc::string::String, - /// The amount available to claim, in satoshis, excluding the on-chain fees which were spent in broadcasting - /// the transaction. - #[prost(uint64, tag = "3")] - pub amount_satoshis: u64, - /// The height at which we will be able to claim the balance if our counterparty has not done so. - #[prost(uint32, tag = "4")] - pub claimable_height: u32, - /// The payment hash whose preimage our counterparty needs to claim this HTLC. - #[prost(string, tag = "5")] - pub payment_hash: ::prost::alloc::string::String, - /// Indicates whether this HTLC represents a payment which was sent outbound from us. - #[prost(bool, tag = "6")] - pub outbound_payment: bool, -} -/// HTLCs which we received from our counterparty which are claimable with a preimage which we do not currently have. -/// This will only be claimable if we receive the preimage from the node to which we forwarded this HTLC before the -/// timeout. -/// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct MaybePreimageClaimableHtlc { - /// The identifier of the channel this balance belongs to. - #[prost(string, tag = "1")] - pub channel_id: ::prost::alloc::string::String, - /// The identifier of our channel counterparty. - #[prost(string, tag = "2")] - pub counterparty_node_id: ::prost::alloc::string::String, - /// The amount available to claim, in satoshis, excluding the on-chain fees which were spent in broadcasting - /// the transaction. - #[prost(uint64, tag = "3")] - pub amount_satoshis: u64, - /// The height at which our counterparty will be able to claim the balance if we have not yet received the preimage and - /// claimed it ourselves. - #[prost(uint32, tag = "4")] - pub expiry_height: u32, - /// The payment hash whose preimage we need to claim this HTLC. - #[prost(string, tag = "5")] - pub payment_hash: ::prost::alloc::string::String, -} -/// The channel has been closed, and our counterparty broadcasted a revoked commitment transaction. -/// -/// Thus, we’re able to claim all outputs in the commitment transaction, one of which has the following amount. -/// -/// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct CounterpartyRevokedOutputClaimable { - /// The identifier of the channel this balance belongs to. - #[prost(string, tag = "1")] - pub channel_id: ::prost::alloc::string::String, - /// The identifier of our channel counterparty. - #[prost(string, tag = "2")] - pub counterparty_node_id: ::prost::alloc::string::String, - /// The amount, in satoshis, of the output which we can claim. - #[prost(uint64, tag = "3")] - pub amount_satoshis: u64, -} -/// Details about the status of a known balance currently being swept to our on-chain wallet. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct PendingSweepBalance { - #[prost(oneof = "pending_sweep_balance::BalanceType", tags = "1, 2, 3")] - pub balance_type: ::core::option::Option, -} -/// Nested message and enum types in `PendingSweepBalance`. -pub mod pending_sweep_balance { - #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] - #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] - #[allow(clippy::derive_partial_eq_without_eq)] - #[derive(Clone, PartialEq, ::prost::Oneof)] - pub enum BalanceType { - #[prost(message, tag = "1")] - PendingBroadcast(super::PendingBroadcast), - #[prost(message, tag = "2")] - BroadcastAwaitingConfirmation(super::BroadcastAwaitingConfirmation), - #[prost(message, tag = "3")] - AwaitingThresholdConfirmations(super::AwaitingThresholdConfirmations), - } -} -/// The spendable output is about to be swept, but a spending transaction has yet to be generated and broadcast. -/// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct PendingBroadcast { - /// The identifier of the channel this balance belongs to. - #[prost(string, optional, tag = "1")] - pub channel_id: ::core::option::Option<::prost::alloc::string::String>, - /// The amount, in satoshis, of the output being swept. - #[prost(uint64, tag = "2")] - pub amount_satoshis: u64, -} -/// A spending transaction has been generated and broadcast and is awaiting confirmation on-chain. -/// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct BroadcastAwaitingConfirmation { - /// The identifier of the channel this balance belongs to. - #[prost(string, optional, tag = "1")] - pub channel_id: ::core::option::Option<::prost::alloc::string::String>, - /// The best height when we last broadcast a transaction spending the output being swept. - #[prost(uint32, tag = "2")] - pub latest_broadcast_height: u32, - /// The identifier of the transaction spending the swept output we last broadcast. - #[prost(string, tag = "3")] - pub latest_spending_txid: ::prost::alloc::string::String, - /// The amount, in satoshis, of the output being swept. - #[prost(uint64, tag = "4")] - pub amount_satoshis: u64, -} -/// A spending transaction has been confirmed on-chain and is awaiting threshold confirmations. -/// -/// It will be considered irrevocably confirmed after reaching `ANTI_REORG_DELAY`. -/// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct AwaitingThresholdConfirmations { - /// The identifier of the channel this balance belongs to. - #[prost(string, optional, tag = "1")] - pub channel_id: ::core::option::Option<::prost::alloc::string::String>, - /// The identifier of the confirmed transaction spending the swept output. - #[prost(string, tag = "2")] - pub latest_spending_txid: ::prost::alloc::string::String, - /// The hash of the block in which the spending transaction was confirmed. - #[prost(string, tag = "3")] - pub confirmation_hash: ::prost::alloc::string::String, - /// The height at which the spending transaction was confirmed. - #[prost(uint32, tag = "4")] - pub confirmation_height: u32, - /// The amount, in satoshis, of the output being swept. - #[prost(uint64, tag = "5")] - pub amount_satoshis: u64, -} -/// Token used to determine start of next page in paginated APIs. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct PageToken { - #[prost(string, tag = "1")] - pub token: ::prost::alloc::string::String, - #[prost(int64, tag = "2")] - pub index: i64, -} -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Bolt11InvoiceDescription { - #[prost(oneof = "bolt11_invoice_description::Kind", tags = "1, 2")] - pub kind: ::core::option::Option, -} -/// Nested message and enum types in `Bolt11InvoiceDescription`. -pub mod bolt11_invoice_description { - #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] - #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] - #[allow(clippy::derive_partial_eq_without_eq)] - #[derive(Clone, PartialEq, ::prost::Oneof)] - pub enum Kind { - #[prost(string, tag = "1")] - Direct(::prost::alloc::string::String), - #[prost(string, tag = "2")] - Hash(::prost::alloc::string::String), - } -} -/// Configuration options for payment routing and pathfinding. -/// See for more details on each field. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct RouteParametersConfig { - /// The maximum total fees, in millisatoshi, that may accrue during route finding. - /// Defaults to 1% of the payment amount + 50 sats - #[prost(uint64, optional, tag = "1")] - pub max_total_routing_fee_msat: ::core::option::Option, - /// The maximum total CLTV delta we accept for the route. - /// Defaults to 1008. - #[prost(uint32, tag = "2")] - pub max_total_cltv_expiry_delta: u32, - /// The maximum number of paths that may be used by (MPP) payments. - /// Defaults to 10. - #[prost(uint32, tag = "3")] - pub max_path_count: u32, - /// Selects the maximum share of a channel's total capacity which will be - /// sent over a channel, as a power of 1/2. - /// Default value: 2 - #[prost(uint32, tag = "4")] - pub max_channel_saturation_power_of_half: u32, -} -/// Routing fees for a channel as part of the network graph. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct GraphRoutingFees { - /// Flat routing fee in millisatoshis. - #[prost(uint32, tag = "1")] - pub base_msat: u32, - /// Liquidity-based routing fee in millionths of a routed amount. - #[prost(uint32, tag = "2")] - pub proportional_millionths: u32, -} -/// Details about one direction of a channel in the network graph, -/// as received within a `ChannelUpdate`. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct GraphChannelUpdate { - /// When the last update to the channel direction was issued. - /// Value is opaque, as set in the announcement. - #[prost(uint32, tag = "1")] - pub last_update: u32, - /// Whether the channel can be currently used for payments (in this one direction). - #[prost(bool, tag = "2")] - pub enabled: bool, - /// The difference in CLTV values that you must have when routing through this channel. - #[prost(uint32, tag = "3")] - pub cltv_expiry_delta: u32, - /// The minimum value, which must be relayed to the next hop via the channel. - #[prost(uint64, tag = "4")] - pub htlc_minimum_msat: u64, - /// The maximum value which may be relayed to the next hop via the channel. - #[prost(uint64, tag = "5")] - pub htlc_maximum_msat: u64, - /// Fees charged when the channel is used for routing. - #[prost(message, optional, tag = "6")] - pub fees: ::core::option::Option, -} -/// Details about a channel in the network graph (both directions). -/// Received within a channel announcement. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct GraphChannel { - /// Source node of the first direction of the channel (hex-encoded public key). - #[prost(string, tag = "1")] - pub node_one: ::prost::alloc::string::String, - /// Source node of the second direction of the channel (hex-encoded public key). - #[prost(string, tag = "2")] - pub node_two: ::prost::alloc::string::String, - /// The channel capacity as seen on-chain, if chain lookup is available. - #[prost(uint64, optional, tag = "3")] - pub capacity_sats: ::core::option::Option, - /// Details about the first direction of a channel. - #[prost(message, optional, tag = "4")] - pub one_to_two: ::core::option::Option, - /// Details about the second direction of a channel. - #[prost(message, optional, tag = "5")] - pub two_to_one: ::core::option::Option, -} -/// Information received in the latest node_announcement from this node. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct GraphNodeAnnouncement { - /// When the last known update to the node state was issued. - /// Value is opaque, as set in the announcement. - #[prost(uint32, tag = "1")] - pub last_update: u32, - /// Moniker assigned to the node. - /// May be invalid or malicious (eg control chars), should not be exposed to the user. - #[prost(string, tag = "2")] - pub alias: ::prost::alloc::string::String, - /// Color assigned to the node as a hex-encoded RGB string, e.g. "ff0000". - #[prost(string, tag = "3")] - pub rgb: ::prost::alloc::string::String, - /// List of addresses on which this node is reachable. - #[prost(string, repeated, tag = "4")] - pub addresses: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, -} -/// Details of a known Lightning peer. -/// See more: -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Peer { - /// The hex-encoded node ID of the peer. - #[prost(string, tag = "1")] - pub node_id: ::prost::alloc::string::String, - /// The network address of the peer. - #[prost(string, tag = "2")] - pub address: ::prost::alloc::string::String, - /// Indicates whether we'll try to reconnect to this peer after restarts. - #[prost(bool, tag = "3")] - pub is_persisted: bool, - /// Indicates whether we currently have an active connection with the peer. - #[prost(bool, tag = "4")] - pub is_connected: bool, -} -/// Details about a node in the network graph, known from the network announcement. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct GraphNode { - /// All valid channels a node has announced. - #[prost(uint64, repeated, tag = "1")] - pub channels: ::prost::alloc::vec::Vec, - /// More information about a node from node_announcement. - /// Optional because we store a node entry after learning about it from - /// a channel announcement, but before receiving a node announcement. - #[prost(message, optional, tag = "2")] - pub announcement_info: ::core::option::Option, -} -/// Represents the direction of a payment. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] -#[repr(i32)] -pub enum PaymentDirection { - /// The payment is inbound. - Inbound = 0, - /// The payment is outbound. - Outbound = 1, -} -impl PaymentDirection { - /// String value of the enum field names used in the ProtoBuf definition. - /// - /// The values are not transformed in any way and thus are considered stable - /// (if the ProtoBuf definition does not change) and safe for programmatic use. - pub fn as_str_name(&self) -> &'static str { - match self { - PaymentDirection::Inbound => "INBOUND", - PaymentDirection::Outbound => "OUTBOUND", - } - } - /// Creates an enum from field names used in the ProtoBuf definition. - pub fn from_str_name(value: &str) -> ::core::option::Option { - match value { - "INBOUND" => Some(Self::Inbound), - "OUTBOUND" => Some(Self::Outbound), - _ => None, - } - } -} -/// Represents the current status of a payment. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] -#[repr(i32)] -pub enum PaymentStatus { - /// The payment is still pending. - Pending = 0, - /// The payment succeeded. - Succeeded = 1, - /// The payment failed. - Failed = 2, -} -impl PaymentStatus { - /// String value of the enum field names used in the ProtoBuf definition. - /// - /// The values are not transformed in any way and thus are considered stable - /// (if the ProtoBuf definition does not change) and safe for programmatic use. - pub fn as_str_name(&self) -> &'static str { - match self { - PaymentStatus::Pending => "PENDING", - PaymentStatus::Succeeded => "SUCCEEDED", - PaymentStatus::Failed => "FAILED", - } - } - /// Creates an enum from field names used in the ProtoBuf definition. - pub fn from_str_name(value: &str) -> ::core::option::Option { - match value { - "PENDING" => Some(Self::Pending), - "SUCCEEDED" => Some(Self::Succeeded), - "FAILED" => Some(Self::Failed), - _ => None, - } - } -} -/// Indicates whether the balance is derived from a cooperative close, a force-close (for holder or counterparty), -/// or whether it is for an HTLC. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] -#[repr(i32)] -pub enum BalanceSource { - /// The channel was force closed by the holder. - HolderForceClosed = 0, - /// The channel was force closed by the counterparty. - CounterpartyForceClosed = 1, - /// The channel was cooperatively closed. - CoopClose = 2, - /// This balance is the result of an HTLC. - Htlc = 3, -} -impl BalanceSource { - /// String value of the enum field names used in the ProtoBuf definition. - /// - /// The values are not transformed in any way and thus are considered stable - /// (if the ProtoBuf definition does not change) and safe for programmatic use. - pub fn as_str_name(&self) -> &'static str { - match self { - BalanceSource::HolderForceClosed => "HOLDER_FORCE_CLOSED", - BalanceSource::CounterpartyForceClosed => "COUNTERPARTY_FORCE_CLOSED", - BalanceSource::CoopClose => "COOP_CLOSE", - BalanceSource::Htlc => "HTLC", - } - } - /// Creates an enum from field names used in the ProtoBuf definition. - pub fn from_str_name(value: &str) -> ::core::option::Option { - match value { - "HOLDER_FORCE_CLOSED" => Some(Self::HolderForceClosed), - "COUNTERPARTY_FORCE_CLOSED" => Some(Self::CounterpartyForceClosed), - "COOP_CLOSE" => Some(Self::CoopClose), - "HTLC" => Some(Self::Htlc), - _ => None, - } - } -} diff --git a/ldk-server/Cargo.toml b/ldk-server/Cargo.toml index 4156a649..7fddbc6e 100644 --- a/ldk-server/Cargo.toml +++ b/ldk-server/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] ldk-node = { git = "https://github.com/lightningdevkit/ldk-node", rev = "9e0a8124cbe2c00a06fc5c880113213d4b36d8aa" } serde = { version = "1.0.203", default-features = false, features = ["derive"] } +serde_json = "1.0" hyper = { version = "1", default-features = false, features = ["server", "http1"] } http-body-util = { version = "0.1", default-features = false } hyper-util = { version = "0.1", default-features = false, features = ["server-graceful"] } @@ -13,9 +14,7 @@ tokio = { version = "1.38.0", default-features = false, features = ["time", "sig tokio-rustls = { version = "0.26", default-features = false, features = ["ring"] } ring = { version = "0.17", default-features = false } getrandom = { version = "0.2", default-features = false } -prost = { version = "0.11.6", default-features = false, features = ["std"] } -ldk-server-protos = { path = "../ldk-server-protos" } -bytes = { version = "1.4.0", default-features = false } +ldk-server-json-models = { path = "../ldk-server-json-models" } hex = { package = "hex-conservative", version = "0.2.1", default-features = false } rusqlite = { version = "0.31.0", features = ["bundled"] } async-trait = { version = "0.1.85", default-features = false } diff --git a/ldk-server/src/api/bolt11_claim_for_hash.rs b/ldk-server/src/api/bolt11_claim_for_hash.rs index e1e62288..a66247c9 100644 --- a/ldk-server/src/api/bolt11_claim_for_hash.rs +++ b/ldk-server/src/api/bolt11_claim_for_hash.rs @@ -7,33 +7,19 @@ // You may not use this file except in accordance with one or both of these // licenses. -use hex::FromHex; use ldk_node::bitcoin::hashes::{sha256, Hash}; use ldk_node::lightning_types::payment::{PaymentHash, PaymentPreimage}; -use ldk_server_protos::api::{Bolt11ClaimForHashRequest, Bolt11ClaimForHashResponse}; +use ldk_server_json_models::api::{Bolt11ClaimForHashRequest, Bolt11ClaimForHashResponse}; use crate::api::error::LdkServerError; -use crate::api::error::LdkServerErrorCode::InvalidRequestError; use crate::service::Context; pub(crate) fn handle_bolt11_claim_for_hash_request( context: Context, request: Bolt11ClaimForHashRequest, ) -> Result { - let preimage_bytes = <[u8; 32]>::from_hex(&request.preimage).map_err(|_| { - LdkServerError::new( - InvalidRequestError, - "Invalid preimage, must be a 32-byte hex string.".to_string(), - ) - })?; - let preimage = PaymentPreimage(preimage_bytes); + let preimage = PaymentPreimage(request.preimage); - let payment_hash = if let Some(hash_hex) = &request.payment_hash { - let hash_bytes = <[u8; 32]>::from_hex(hash_hex).map_err(|_| { - LdkServerError::new( - InvalidRequestError, - "Invalid payment_hash, must be a 32-byte hex string.".to_string(), - ) - })?; + let payment_hash = if let Some(hash_bytes) = request.payment_hash { PaymentHash(hash_bytes) } else { PaymentHash(sha256::Hash::hash(&preimage.0).to_byte_array()) diff --git a/ldk-server/src/api/bolt11_fail_for_hash.rs b/ldk-server/src/api/bolt11_fail_for_hash.rs index 9f349164..b269d919 100644 --- a/ldk-server/src/api/bolt11_fail_for_hash.rs +++ b/ldk-server/src/api/bolt11_fail_for_hash.rs @@ -7,24 +7,16 @@ // You may not use this file except in accordance with one or both of these // licenses. -use hex::FromHex; use ldk_node::lightning_types::payment::PaymentHash; -use ldk_server_protos::api::{Bolt11FailForHashRequest, Bolt11FailForHashResponse}; +use ldk_server_json_models::api::{Bolt11FailForHashRequest, Bolt11FailForHashResponse}; use crate::api::error::LdkServerError; -use crate::api::error::LdkServerErrorCode::InvalidRequestError; use crate::service::Context; pub(crate) fn handle_bolt11_fail_for_hash_request( context: Context, request: Bolt11FailForHashRequest, ) -> Result { - let hash_bytes = <[u8; 32]>::from_hex(&request.payment_hash).map_err(|_| { - LdkServerError::new( - InvalidRequestError, - "Invalid payment_hash, must be a 32-byte hex string.".to_string(), - ) - })?; - let payment_hash = PaymentHash(hash_bytes); + let payment_hash = PaymentHash(request.payment_hash); context.node.bolt11_payment().fail_for_hash(payment_hash)?; diff --git a/ldk-server/src/api/bolt11_receive.rs b/ldk-server/src/api/bolt11_receive.rs index 793a0928..d96cef6b 100644 --- a/ldk-server/src/api/bolt11_receive.rs +++ b/ldk-server/src/api/bolt11_receive.rs @@ -7,17 +7,16 @@ // You may not use this file except in accordance with one or both of these // licenses. -use hex::DisplayHex; -use ldk_server_protos::api::{Bolt11ReceiveRequest, Bolt11ReceiveResponse}; +use ldk_server_json_models::api::{Bolt11ReceiveRequest, Bolt11ReceiveResponse}; use crate::api::error::LdkServerError; use crate::service::Context; -use crate::util::proto_adapter::proto_to_bolt11_description; +use crate::util::adapter::bolt11_description_from_model; pub(crate) fn handle_bolt11_receive_request( context: Context, request: Bolt11ReceiveRequest, ) -> Result { - let description = proto_to_bolt11_description(request.description)?; + let description = bolt11_description_from_model(request.description)?; let invoice = match request.amount_msat { Some(amount_msat) => { context.node.bolt11_payment().receive(amount_msat, &description, request.expiry_secs)? @@ -28,8 +27,8 @@ pub(crate) fn handle_bolt11_receive_request( .receive_variable_amount(&description, request.expiry_secs)?, }; - let payment_hash = invoice.payment_hash().0.to_lower_hex_string(); - let payment_secret = invoice.payment_secret().0.to_lower_hex_string(); + let payment_hash = invoice.payment_hash().0; + let payment_secret = invoice.payment_secret().0; let response = Bolt11ReceiveResponse { invoice: invoice.to_string(), payment_hash, payment_secret }; Ok(response) diff --git a/ldk-server/src/api/bolt11_receive_for_hash.rs b/ldk-server/src/api/bolt11_receive_for_hash.rs index 8f687067..9207c07d 100644 --- a/ldk-server/src/api/bolt11_receive_for_hash.rs +++ b/ldk-server/src/api/bolt11_receive_for_hash.rs @@ -7,26 +7,18 @@ // You may not use this file except in accordance with one or both of these // licenses. -use hex::FromHex; use ldk_node::lightning_types::payment::PaymentHash; -use ldk_server_protos::api::{Bolt11ReceiveForHashRequest, Bolt11ReceiveForHashResponse}; +use ldk_server_json_models::api::{Bolt11ReceiveForHashRequest, Bolt11ReceiveForHashResponse}; use crate::api::error::LdkServerError; -use crate::api::error::LdkServerErrorCode::InvalidRequestError; use crate::service::Context; -use crate::util::proto_adapter::proto_to_bolt11_description; +use crate::util::adapter::bolt11_description_from_model; pub(crate) fn handle_bolt11_receive_for_hash_request( context: Context, request: Bolt11ReceiveForHashRequest, ) -> Result { - let description = proto_to_bolt11_description(request.description)?; - let hash_bytes = <[u8; 32]>::from_hex(&request.payment_hash).map_err(|_| { - LdkServerError::new( - InvalidRequestError, - "Invalid payment_hash, must be a 32-byte hex string.".to_string(), - ) - })?; - let payment_hash = PaymentHash(hash_bytes); + let description = bolt11_description_from_model(request.description)?; + let payment_hash = PaymentHash(request.payment_hash); let invoice = match request.amount_msat { Some(amount_msat) => context.node.bolt11_payment().receive_for_hash( diff --git a/ldk-server/src/api/bolt11_receive_via_jit_channel.rs b/ldk-server/src/api/bolt11_receive_via_jit_channel.rs index 552a1823..87c427e9 100644 --- a/ldk-server/src/api/bolt11_receive_via_jit_channel.rs +++ b/ldk-server/src/api/bolt11_receive_via_jit_channel.rs @@ -7,7 +7,7 @@ // You may not use this file except in accordance with one or both of these // licenses. -use ldk_server_protos::api::{ +use ldk_server_json_models::api::{ Bolt11ReceiveVariableAmountViaJitChannelRequest, Bolt11ReceiveVariableAmountViaJitChannelResponse, Bolt11ReceiveViaJitChannelRequest, Bolt11ReceiveViaJitChannelResponse, @@ -15,12 +15,12 @@ use ldk_server_protos::api::{ use crate::api::error::LdkServerError; use crate::service::Context; -use crate::util::proto_adapter::proto_to_bolt11_description; +use crate::util::adapter::bolt11_description_from_model; pub(crate) fn handle_bolt11_receive_via_jit_channel_request( context: Context, request: Bolt11ReceiveViaJitChannelRequest, ) -> Result { - let description = proto_to_bolt11_description(request.description)?; + let description = bolt11_description_from_model(request.description)?; let invoice = context.node.bolt11_payment().receive_via_jit_channel( request.amount_msat, &description, @@ -34,7 +34,7 @@ pub(crate) fn handle_bolt11_receive_via_jit_channel_request( pub(crate) fn handle_bolt11_receive_variable_amount_via_jit_channel_request( context: Context, request: Bolt11ReceiveVariableAmountViaJitChannelRequest, ) -> Result { - let description = proto_to_bolt11_description(request.description)?; + let description = bolt11_description_from_model(request.description)?; let invoice = context.node.bolt11_payment().receive_variable_amount_via_jit_channel( &description, request.expiry_secs, diff --git a/ldk-server/src/api/bolt11_send.rs b/ldk-server/src/api/bolt11_send.rs index 1298896e..ce32362a 100644 --- a/ldk-server/src/api/bolt11_send.rs +++ b/ldk-server/src/api/bolt11_send.rs @@ -10,9 +10,9 @@ use std::str::FromStr; use ldk_node::lightning_invoice::Bolt11Invoice; -use ldk_server_protos::api::{Bolt11SendRequest, Bolt11SendResponse}; +use ldk_server_json_models::api::{Bolt11SendRequest, Bolt11SendResponse}; -use crate::api::build_route_parameters_config_from_proto; +use crate::api::build_route_parameters_config_from_model; use crate::api::error::LdkServerError; use crate::service::Context; @@ -22,7 +22,7 @@ pub(crate) fn handle_bolt11_send_request( let invoice = Bolt11Invoice::from_str(request.invoice.as_str()) .map_err(|_| ldk_node::NodeError::InvalidInvoice)?; - let route_parameters = build_route_parameters_config_from_proto(request.route_parameters)?; + let route_parameters = build_route_parameters_config_from_model(request.route_parameters)?; let payment_id = match request.amount_msat { None => context.node.bolt11_payment().send(&invoice, route_parameters), @@ -31,6 +31,6 @@ pub(crate) fn handle_bolt11_send_request( }, }?; - let response = Bolt11SendResponse { payment_id: payment_id.to_string() }; + let response = Bolt11SendResponse { payment_id: payment_id.0 }; Ok(response) } diff --git a/ldk-server/src/api/bolt12_receive.rs b/ldk-server/src/api/bolt12_receive.rs index 42f4b489..badb0fae 100644 --- a/ldk-server/src/api/bolt12_receive.rs +++ b/ldk-server/src/api/bolt12_receive.rs @@ -7,8 +7,7 @@ // You may not use this file except in accordance with one or both of these // licenses. -use hex::DisplayHex; -use ldk_server_protos::api::{Bolt12ReceiveRequest, Bolt12ReceiveResponse}; +use ldk_server_json_models::api::{Bolt12ReceiveRequest, Bolt12ReceiveResponse}; use crate::api::error::LdkServerError; use crate::service::Context; @@ -29,7 +28,7 @@ pub(crate) fn handle_bolt12_receive_request( .receive_variable_amount(&request.description, request.expiry_secs)?, }; - let offer_id = offer.id().0.to_lower_hex_string(); + let offer_id = offer.id().0; let response = Bolt12ReceiveResponse { offer: offer.to_string(), offer_id }; Ok(response) } diff --git a/ldk-server/src/api/bolt12_send.rs b/ldk-server/src/api/bolt12_send.rs index 30df2dd8..4acdda06 100644 --- a/ldk-server/src/api/bolt12_send.rs +++ b/ldk-server/src/api/bolt12_send.rs @@ -10,9 +10,9 @@ use std::str::FromStr; use ldk_node::lightning::offers::offer::Offer; -use ldk_server_protos::api::{Bolt12SendRequest, Bolt12SendResponse}; +use ldk_server_json_models::api::{Bolt12SendRequest, Bolt12SendResponse}; -use crate::api::build_route_parameters_config_from_proto; +use crate::api::build_route_parameters_config_from_model; use crate::api::error::LdkServerError; use crate::service::Context; @@ -22,7 +22,7 @@ pub(crate) fn handle_bolt12_send_request( let offer = Offer::from_str(request.offer.as_str()).map_err(|_| ldk_node::NodeError::InvalidOffer)?; - let route_parameters = build_route_parameters_config_from_proto(request.route_parameters)?; + let route_parameters = build_route_parameters_config_from_model(request.route_parameters)?; let payment_id = match request.amount_msat { None => context.node.bolt12_payment().send( @@ -40,6 +40,6 @@ pub(crate) fn handle_bolt12_send_request( ), }?; - let response = Bolt12SendResponse { payment_id: payment_id.to_string() }; + let response = Bolt12SendResponse { payment_id: payment_id.0 }; Ok(response) } diff --git a/ldk-server/src/api/close_channel.rs b/ldk-server/src/api/close_channel.rs index 5ae4070b..51ce66ed 100644 --- a/ldk-server/src/api/close_channel.rs +++ b/ldk-server/src/api/close_channel.rs @@ -7,11 +7,9 @@ // You may not use this file except in accordance with one or both of these // licenses. -use std::str::FromStr; - use ldk_node::bitcoin::secp256k1::PublicKey; use ldk_node::UserChannelId; -use ldk_server_protos::api::{ +use ldk_server_json_models::api::{ CloseChannelRequest, CloseChannelResponse, ForceCloseChannelRequest, ForceCloseChannelResponse, }; @@ -52,8 +50,8 @@ fn parse_user_channel_id(id: &str) -> Result { Ok(UserChannelId(parsed)) } -fn parse_counterparty_node_id(id: &str) -> Result { - PublicKey::from_str(id).map_err(|e| { +fn parse_counterparty_node_id(id: &[u8]) -> Result { + PublicKey::from_slice(id).map_err(|e| { LdkServerError::new( InvalidRequestError, format!("Invalid counterparty node ID, error: {}", e), diff --git a/ldk-server/src/api/connect_peer.rs b/ldk-server/src/api/connect_peer.rs index d3fac3a0..1039b67b 100644 --- a/ldk-server/src/api/connect_peer.rs +++ b/ldk-server/src/api/connect_peer.rs @@ -11,7 +11,7 @@ use std::str::FromStr; use ldk_node::bitcoin::secp256k1::PublicKey; use ldk_node::lightning::ln::msgs::SocketAddress; -use ldk_server_protos::api::{ConnectPeerRequest, ConnectPeerResponse}; +use ldk_server_json_models::api::{ConnectPeerRequest, ConnectPeerResponse}; use crate::api::error::LdkServerError; use crate::service::Context; @@ -19,7 +19,7 @@ use crate::service::Context; pub(crate) fn handle_connect_peer( context: Context, request: ConnectPeerRequest, ) -> Result { - let node_id = PublicKey::from_str(&request.node_pubkey) + let node_id = PublicKey::from_slice(&request.node_pubkey) .map_err(|_| ldk_node::NodeError::InvalidPublicKey)?; let address = SocketAddress::from_str(&request.address) .map_err(|_| ldk_node::NodeError::InvalidSocketAddress)?; diff --git a/ldk-server/src/api/disconnect_peer.rs b/ldk-server/src/api/disconnect_peer.rs index 76f2ecce..8903e978 100644 --- a/ldk-server/src/api/disconnect_peer.rs +++ b/ldk-server/src/api/disconnect_peer.rs @@ -7,10 +7,8 @@ // You may not use this file except in accordance with one or both of these // licenses. -use std::str::FromStr; - use ldk_node::bitcoin::secp256k1::PublicKey; -use ldk_server_protos::api::{DisconnectPeerRequest, DisconnectPeerResponse}; +use ldk_server_json_models::api::{DisconnectPeerRequest, DisconnectPeerResponse}; use crate::api::error::LdkServerError; use crate::service::Context; @@ -18,7 +16,7 @@ use crate::service::Context; pub(crate) fn handle_disconnect_peer( context: Context, request: DisconnectPeerRequest, ) -> Result { - let node_id = PublicKey::from_str(&request.node_pubkey) + let node_id = PublicKey::from_slice(&request.node_pubkey) .map_err(|_| ldk_node::NodeError::InvalidPublicKey)?; context.node.disconnect(node_id)?; diff --git a/ldk-server/src/api/error.rs b/ldk-server/src/api/error.rs index ad1c152f..b1b99999 100644 --- a/ldk-server/src/api/error.rs +++ b/ldk-server/src/api/error.rs @@ -41,16 +41,16 @@ impl fmt::Display for LdkServerError { #[derive(Clone, Debug, PartialEq, Eq)] #[allow(clippy::enum_variant_names)] pub(crate) enum LdkServerErrorCode { - /// Please refer to [`protos::error::ErrorCode::InvalidRequestError`]. + /// See [`ldk_server_json_models::error::ErrorCode::InvalidRequestError`]. InvalidRequestError, - /// Please refer to [`protos::error::ErrorCode::AuthError`]. + /// See [`ldk_server_json_models::error::ErrorCode::AuthError`]. AuthError, - /// Please refer to [`protos::error::ErrorCode::LightningError`]. + /// See [`ldk_server_json_models::error::ErrorCode::LightningError`]. LightningError, - /// Please refer to [`protos::error::ErrorCode::InternalServerError`]. + /// See [`ldk_server_json_models::error::ErrorCode::InternalServerError`]. InternalServerError, } diff --git a/ldk-server/src/api/export_pathfinding_scores.rs b/ldk-server/src/api/export_pathfinding_scores.rs index a6924a22..095ebf4d 100644 --- a/ldk-server/src/api/export_pathfinding_scores.rs +++ b/ldk-server/src/api/export_pathfinding_scores.rs @@ -7,7 +7,9 @@ // You may not use this file except in accordance with one or both of these // licenses. -use ldk_server_protos::api::{ExportPathfindingScoresRequest, ExportPathfindingScoresResponse}; +use ldk_server_json_models::api::{ + ExportPathfindingScoresRequest, ExportPathfindingScoresResponse, +}; use crate::api::error::LdkServerError; use crate::service::Context; @@ -17,6 +19,6 @@ pub(crate) fn handle_export_pathfinding_scores_request( ) -> Result { let scores = context.node.export_pathfinding_scores()?; - let response = ExportPathfindingScoresResponse { scores: scores.into() }; + let response = ExportPathfindingScoresResponse { scores }; Ok(response) } diff --git a/ldk-server/src/api/get_balances.rs b/ldk-server/src/api/get_balances.rs index 82ab69b7..90a7af27 100644 --- a/ldk-server/src/api/get_balances.rs +++ b/ldk-server/src/api/get_balances.rs @@ -7,11 +7,11 @@ // You may not use this file except in accordance with one or both of these // licenses. -use ldk_server_protos::api::{GetBalancesRequest, GetBalancesResponse}; +use ldk_server_json_models::api::{GetBalancesRequest, GetBalancesResponse}; use crate::api::error::LdkServerError; use crate::service::Context; -use crate::util::proto_adapter::{lightning_balance_to_proto, pending_sweep_balance_to_proto}; +use crate::util::adapter::{lightning_balance_to_model, pending_sweep_balance_to_model}; pub(crate) fn handle_get_balances_request( context: Context, _request: GetBalancesRequest, @@ -26,12 +26,12 @@ pub(crate) fn handle_get_balances_request( lightning_balances: balance_details .lightning_balances .into_iter() - .map(lightning_balance_to_proto) + .map(lightning_balance_to_model) .collect(), pending_balances_from_channel_closures: balance_details .pending_balances_from_channel_closures .into_iter() - .map(pending_sweep_balance_to_proto) + .map(pending_sweep_balance_to_model) .collect(), }; Ok(response) diff --git a/ldk-server/src/api/get_node_info.rs b/ldk-server/src/api/get_node_info.rs index c1b81279..f1943275 100644 --- a/ldk-server/src/api/get_node_info.rs +++ b/ldk-server/src/api/get_node_info.rs @@ -7,8 +7,9 @@ // You may not use this file except in accordance with one or both of these // licenses. -use ldk_server_protos::api::{GetNodeInfoRequest, GetNodeInfoResponse}; -use ldk_server_protos::types::BestBlock; +use ldk_node::bitcoin::hashes::Hash as _; +use ldk_server_json_models::api::{GetNodeInfoRequest, GetNodeInfoResponse}; +use ldk_server_json_models::types::BestBlock; use crate::api::error::LdkServerError; use crate::service::Context; @@ -19,7 +20,7 @@ pub(crate) fn handle_get_node_info_request( let node_status = context.node.status(); let best_block = BestBlock { - block_hash: node_status.current_best_block.block_hash.to_string(), + block_hash: node_status.current_best_block.block_hash.to_byte_array(), height: node_status.current_best_block.height, }; @@ -37,7 +38,7 @@ pub(crate) fn handle_get_node_info_request( let node_alias = context.node.node_alias().map(|alias| alias.to_string()); - let node_id = context.node.node_id().to_string(); + let node_id = context.node.node_id(); let node_uris = { let addrs = if announcement_addresses.is_empty() { @@ -48,8 +49,8 @@ pub(crate) fn handle_get_node_info_request( addrs.into_iter().map(|a| format!("{node_id}@{a}")).collect() }; let response = GetNodeInfoResponse { - node_id, - current_best_block: Some(best_block), + node_id: node_id.serialize(), + current_best_block: best_block, latest_lightning_wallet_sync_timestamp: node_status.latest_lightning_wallet_sync_timestamp, latest_onchain_wallet_sync_timestamp: node_status.latest_onchain_wallet_sync_timestamp, latest_fee_rate_cache_update_timestamp: node_status.latest_fee_rate_cache_update_timestamp, diff --git a/ldk-server/src/api/get_payment_details.rs b/ldk-server/src/api/get_payment_details.rs index 1d7b266a..140f4142 100644 --- a/ldk-server/src/api/get_payment_details.rs +++ b/ldk-server/src/api/get_payment_details.rs @@ -7,29 +7,19 @@ // You may not use this file except in accordance with one or both of these // licenses. -use hex::FromHex; use ldk_node::lightning::ln::channelmanager::PaymentId; -use ldk_server_protos::api::{GetPaymentDetailsRequest, GetPaymentDetailsResponse}; +use ldk_server_json_models::api::{GetPaymentDetailsRequest, GetPaymentDetailsResponse}; use crate::api::error::LdkServerError; -use crate::api::error::LdkServerErrorCode::InvalidRequestError; use crate::service::Context; -use crate::util::proto_adapter::payment_to_proto; +use crate::util::adapter::payment_to_model; pub(crate) fn handle_get_payment_details_request( context: Context, request: GetPaymentDetailsRequest, ) -> Result { - let payment_id_bytes = - <[u8; PaymentId::LENGTH]>::from_hex(&request.payment_id).map_err(|_| { - LdkServerError::new( - InvalidRequestError, - format!("Invalid payment_id, must be a {}-byte hex-string.", PaymentId::LENGTH), - ) - })?; + let payment_details = context.node.payment(&PaymentId(request.payment_id)); - let payment_details = context.node.payment(&PaymentId(payment_id_bytes)); - - let response = GetPaymentDetailsResponse { payment: payment_details.map(payment_to_proto) }; + let response = GetPaymentDetailsResponse { payment: payment_details.map(payment_to_model) }; Ok(response) } diff --git a/ldk-server/src/api/graph_get_channel.rs b/ldk-server/src/api/graph_get_channel.rs index 3e20a2a0..4b896f14 100644 --- a/ldk-server/src/api/graph_get_channel.rs +++ b/ldk-server/src/api/graph_get_channel.rs @@ -7,12 +7,12 @@ // You may not use this file except in accordance with one or both of these // licenses. -use ldk_server_protos::api::{GraphGetChannelRequest, GraphGetChannelResponse}; +use ldk_server_json_models::api::{GraphGetChannelRequest, GraphGetChannelResponse}; use crate::api::error::LdkServerError; use crate::api::error::LdkServerErrorCode::InvalidRequestError; use crate::service::Context; -use crate::util::proto_adapter::graph_channel_to_proto; +use crate::util::adapter::graph_channel_to_model; pub(crate) fn handle_graph_get_channel_request( context: Context, request: GraphGetChannelRequest, @@ -28,6 +28,6 @@ pub(crate) fn handle_graph_get_channel_request( ) })?; - let response = GraphGetChannelResponse { channel: Some(graph_channel_to_proto(channel_info)) }; + let response = GraphGetChannelResponse { channel: Some(graph_channel_to_model(channel_info)) }; Ok(response) } diff --git a/ldk-server/src/api/graph_get_node.rs b/ldk-server/src/api/graph_get_node.rs index 9ddb0eac..3b210ef5 100644 --- a/ldk-server/src/api/graph_get_node.rs +++ b/ldk-server/src/api/graph_get_node.rs @@ -8,30 +8,27 @@ // licenses. use ldk_node::lightning::routing::gossip::NodeId; -use ldk_server_protos::api::{GraphGetNodeRequest, GraphGetNodeResponse}; +use ldk_server_json_models::api::{GraphGetNodeRequest, GraphGetNodeResponse}; use crate::api::error::LdkServerError; use crate::api::error::LdkServerErrorCode::InvalidRequestError; use crate::service::Context; -use crate::util::proto_adapter::graph_node_to_proto; +use crate::util::adapter::graph_node_to_model; pub(crate) fn handle_graph_get_node_request( context: Context, request: GraphGetNodeRequest, ) -> Result { - let node_id: NodeId = request.node_id.parse().map_err(|_| { + let node_id = NodeId::from_slice(&request.node_id).map_err(|_| { LdkServerError::new( InvalidRequestError, - format!("Invalid node_id: {}. Expected a hex-encoded public key.", request.node_id), + "Invalid node_id: expected a valid 33-byte public key.".to_string(), ) })?; let node_info = context.node.network_graph().node(&node_id).ok_or_else(|| { - LdkServerError::new( - InvalidRequestError, - format!("Node with ID {} not found in the network graph.", request.node_id), - ) + LdkServerError::new(InvalidRequestError, "Node not found in the network graph.".to_string()) })?; - let response = GraphGetNodeResponse { node: Some(graph_node_to_proto(node_info)) }; + let response = GraphGetNodeResponse { node: Some(graph_node_to_model(node_info)) }; Ok(response) } diff --git a/ldk-server/src/api/graph_list_channels.rs b/ldk-server/src/api/graph_list_channels.rs index 60543292..2cee05cb 100644 --- a/ldk-server/src/api/graph_list_channels.rs +++ b/ldk-server/src/api/graph_list_channels.rs @@ -7,7 +7,7 @@ // You may not use this file except in accordance with one or both of these // licenses. -use ldk_server_protos::api::{GraphListChannelsRequest, GraphListChannelsResponse}; +use ldk_server_json_models::api::{GraphListChannelsRequest, GraphListChannelsResponse}; use crate::api::error::LdkServerError; use crate::service::Context; diff --git a/ldk-server/src/api/graph_list_nodes.rs b/ldk-server/src/api/graph_list_nodes.rs index 64ccd295..8a3c0283 100644 --- a/ldk-server/src/api/graph_list_nodes.rs +++ b/ldk-server/src/api/graph_list_nodes.rs @@ -7,7 +7,7 @@ // You may not use this file except in accordance with one or both of these // licenses. -use ldk_server_protos::api::{GraphListNodesRequest, GraphListNodesResponse}; +use ldk_server_json_models::api::{GraphListNodesRequest, GraphListNodesResponse}; use crate::api::error::LdkServerError; use crate::service::Context; diff --git a/ldk-server/src/api/list_channels.rs b/ldk-server/src/api/list_channels.rs index 84d1584b..f133c9bd 100644 --- a/ldk-server/src/api/list_channels.rs +++ b/ldk-server/src/api/list_channels.rs @@ -7,16 +7,16 @@ // You may not use this file except in accordance with one or both of these // licenses. -use ldk_server_protos::api::{ListChannelsRequest, ListChannelsResponse}; +use ldk_server_json_models::api::{ListChannelsRequest, ListChannelsResponse}; use crate::api::error::LdkServerError; use crate::service::Context; -use crate::util::proto_adapter::channel_to_proto; +use crate::util::adapter::channel_to_model; pub(crate) fn handle_list_channels_request( context: Context, _request: ListChannelsRequest, ) -> Result { - let channels = context.node.list_channels().into_iter().map(channel_to_proto).collect(); + let channels = context.node.list_channels().into_iter().map(channel_to_model).collect(); let response = ListChannelsResponse { channels }; Ok(response) diff --git a/ldk-server/src/api/list_forwarded_payments.rs b/ldk-server/src/api/list_forwarded_payments.rs index 78ce3dc4..6fb70f42 100644 --- a/ldk-server/src/api/list_forwarded_payments.rs +++ b/ldk-server/src/api/list_forwarded_payments.rs @@ -7,10 +7,8 @@ // You may not use this file except in accordance with one or both of these // licenses. -use bytes::Bytes; -use ldk_server_protos::api::{ListForwardedPaymentsRequest, ListForwardedPaymentsResponse}; -use ldk_server_protos::types::{ForwardedPayment, PageToken}; -use prost::Message; +use ldk_server_json_models::api::{ListForwardedPaymentsRequest, ListForwardedPaymentsResponse}; +use ldk_server_json_models::types::{ForwardedPayment, PageToken}; use crate::api::error::LdkServerError; use crate::api::error::LdkServerErrorCode::InternalServerError; @@ -54,8 +52,8 @@ pub(crate) fn handle_list_forwarded_payments_request( format!("Failed to read forwarded payment data: {}", e), ) })?; - let forwarded_payment = ForwardedPayment::decode(Bytes::from(forwarded_payment_bytes)) - .map_err(|e| { + let forwarded_payment = + serde_json::from_slice::(&forwarded_payment_bytes).map_err(|e| { LdkServerError::new( InternalServerError, format!("Failed to decode forwarded payment: {}", e), diff --git a/ldk-server/src/api/list_payments.rs b/ldk-server/src/api/list_payments.rs index fbaf7c55..9cd645e5 100644 --- a/ldk-server/src/api/list_payments.rs +++ b/ldk-server/src/api/list_payments.rs @@ -7,10 +7,8 @@ // You may not use this file except in accordance with one or both of these // licenses. -use bytes::Bytes; -use ldk_server_protos::api::{ListPaymentsRequest, ListPaymentsResponse}; -use ldk_server_protos::types::{PageToken, Payment}; -use prost::Message; +use ldk_server_json_models::api::{ListPaymentsRequest, ListPaymentsResponse}; +use ldk_server_json_models::types::{PageToken, Payment}; use crate::api::error::LdkServerError; use crate::api::error::LdkServerErrorCode::InternalServerError; @@ -49,7 +47,7 @@ pub(crate) fn handle_list_payments_request( format!("Failed to read payment data: {}", e), ) })?; - let payment = Payment::decode(Bytes::from(payment_bytes)).map_err(|e| { + let payment = serde_json::from_slice::(&payment_bytes).map_err(|e| { LdkServerError::new(InternalServerError, format!("Failed to decode payment: {}", e)) })?; payments.push(payment); diff --git a/ldk-server/src/api/list_peers.rs b/ldk-server/src/api/list_peers.rs index c75e119e..54203c5f 100644 --- a/ldk-server/src/api/list_peers.rs +++ b/ldk-server/src/api/list_peers.rs @@ -7,16 +7,16 @@ // You may not use this file except in accordance with one or both of these // licenses. -use ldk_server_protos::api::{ListPeersRequest, ListPeersResponse}; +use ldk_server_json_models::api::{ListPeersRequest, ListPeersResponse}; use crate::api::error::LdkServerError; use crate::service::Context; -use crate::util::proto_adapter::peer_to_proto; +use crate::util::adapter::peer_to_model; pub(crate) fn handle_list_peers_request( context: Context, _request: ListPeersRequest, ) -> Result { - let peers = context.node.list_peers().into_iter().map(peer_to_proto).collect(); + let peers = context.node.list_peers().into_iter().map(peer_to_model).collect(); let response = ListPeersResponse { peers }; Ok(response) diff --git a/ldk-server/src/api/mod.rs b/ldk-server/src/api/mod.rs index ab8d7d53..38712a1b 100644 --- a/ldk-server/src/api/mod.rs +++ b/ldk-server/src/api/mod.rs @@ -9,7 +9,7 @@ use ldk_node::config::{ChannelConfig, MaxDustHTLCExposure}; use ldk_node::lightning::routing::router::RouteParametersConfig; -use ldk_server_protos::types::channel_config::MaxDustHtlcExposure; +use ldk_server_json_models::types::MaxDustHtlcExposure; use crate::api::error::LdkServerError; use crate::api::error::LdkServerErrorCode::InvalidRequestError; @@ -48,10 +48,11 @@ pub(crate) mod unified_send; pub(crate) mod update_channel_config; pub(crate) mod verify_signature; -pub(crate) fn build_channel_config_from_proto( - default_config: ChannelConfig, proto_channel_config: ldk_server_protos::types::ChannelConfig, +pub(crate) fn build_channel_config_from_model( + default_config: ChannelConfig, + channel_config_model: ldk_server_json_models::types::ChannelConfig, ) -> Result { - let max_dust_htlc_exposure = proto_channel_config + let max_dust_htlc_exposure = channel_config_model .max_dust_htlc_exposure .map(|max_dust_htlc_exposure| match max_dust_htlc_exposure { MaxDustHtlcExposure::FixedLimitMsat(limit_msat) => { @@ -63,7 +64,7 @@ pub(crate) fn build_channel_config_from_proto( }) .unwrap_or(default_config.max_dust_htlc_exposure); - let cltv_expiry_delta = match proto_channel_config.cltv_expiry_delta { + let cltv_expiry_delta = match channel_config_model.cltv_expiry_delta { Some(c) => Some(u16::try_from(c).map_err(|_| { LdkServerError::new( InvalidRequestError, @@ -75,27 +76,27 @@ pub(crate) fn build_channel_config_from_proto( .unwrap_or(default_config.cltv_expiry_delta); Ok(ChannelConfig { - forwarding_fee_proportional_millionths: proto_channel_config + forwarding_fee_proportional_millionths: channel_config_model .forwarding_fee_proportional_millionths .unwrap_or(default_config.forwarding_fee_proportional_millionths), - forwarding_fee_base_msat: proto_channel_config + forwarding_fee_base_msat: channel_config_model .forwarding_fee_base_msat .unwrap_or(default_config.forwarding_fee_base_msat), cltv_expiry_delta, max_dust_htlc_exposure, - force_close_avoidance_max_fee_satoshis: proto_channel_config + force_close_avoidance_max_fee_satoshis: channel_config_model .force_close_avoidance_max_fee_satoshis .unwrap_or(default_config.force_close_avoidance_max_fee_satoshis), - accept_underpaying_htlcs: proto_channel_config + accept_underpaying_htlcs: channel_config_model .accept_underpaying_htlcs .unwrap_or(default_config.accept_underpaying_htlcs), }) } -pub(crate) fn build_route_parameters_config_from_proto( - proto_route_params: Option, +pub(crate) fn build_route_parameters_config_from_model( + route_params_model: Option, ) -> Result, LdkServerError> { - match proto_route_params { + match route_params_model { Some(params) => { let max_path_count = params.max_path_count.try_into().map_err(|_| { LdkServerError::new( diff --git a/ldk-server/src/api/onchain_receive.rs b/ldk-server/src/api/onchain_receive.rs index cad2837e..389cec84 100644 --- a/ldk-server/src/api/onchain_receive.rs +++ b/ldk-server/src/api/onchain_receive.rs @@ -7,7 +7,7 @@ // You may not use this file except in accordance with one or both of these // licenses. -use ldk_server_protos::api::{OnchainReceiveRequest, OnchainReceiveResponse}; +use ldk_server_json_models::api::{OnchainReceiveRequest, OnchainReceiveResponse}; use crate::api::error::LdkServerError; use crate::service::Context; diff --git a/ldk-server/src/api/onchain_send.rs b/ldk-server/src/api/onchain_send.rs index 6eb1d63a..2b116b3a 100644 --- a/ldk-server/src/api/onchain_send.rs +++ b/ldk-server/src/api/onchain_send.rs @@ -9,8 +9,9 @@ use std::str::FromStr; +use ldk_node::bitcoin::hashes::Hash as _; use ldk_node::bitcoin::{Address, FeeRate}; -use ldk_server_protos::api::{OnchainSendRequest, OnchainSendResponse}; +use ldk_server_json_models::api::{OnchainSendRequest, OnchainSendResponse}; use crate::api::error::LdkServerError; use crate::api::error::LdkServerErrorCode::InvalidRequestError; @@ -45,6 +46,6 @@ pub(crate) fn handle_onchain_send_request( )) }, }; - let response = OnchainSendResponse { txid: txid.to_string() }; + let response = OnchainSendResponse { txid: txid.to_byte_array() }; Ok(response) } diff --git a/ldk-server/src/api/open_channel.rs b/ldk-server/src/api/open_channel.rs index 6c470b71..a765ff6d 100644 --- a/ldk-server/src/api/open_channel.rs +++ b/ldk-server/src/api/open_channel.rs @@ -12,23 +12,23 @@ use std::str::FromStr; use ldk_node::bitcoin::secp256k1::PublicKey; use ldk_node::config::ChannelConfig; use ldk_node::lightning::ln::msgs::SocketAddress; -use ldk_server_protos::api::{OpenChannelRequest, OpenChannelResponse}; +use ldk_server_json_models::api::{OpenChannelRequest, OpenChannelResponse}; -use crate::api::build_channel_config_from_proto; +use crate::api::build_channel_config_from_model; use crate::api::error::LdkServerError; use crate::service::Context; pub(crate) fn handle_open_channel( context: Context, request: OpenChannelRequest, ) -> Result { - let node_id = PublicKey::from_str(&request.node_pubkey) + let node_id = PublicKey::from_slice(&request.node_pubkey) .map_err(|_| ldk_node::NodeError::InvalidPublicKey)?; let address = SocketAddress::from_str(&request.address) .map_err(|_| ldk_node::NodeError::InvalidSocketAddress)?; let channel_config = request .channel_config - .map(|proto_config| build_channel_config_from_proto(ChannelConfig::default(), proto_config)) + .map(|config_model| build_channel_config_from_model(ChannelConfig::default(), config_model)) .transpose()?; let user_channel_id = if request.announce_channel { diff --git a/ldk-server/src/api/sign_message.rs b/ldk-server/src/api/sign_message.rs index 8ef0e015..a4be8b18 100644 --- a/ldk-server/src/api/sign_message.rs +++ b/ldk-server/src/api/sign_message.rs @@ -7,7 +7,7 @@ // You may not use this file except in accordance with one or both of these // licenses. -use ldk_server_protos::api::{SignMessageRequest, SignMessageResponse}; +use ldk_server_json_models::api::{SignMessageRequest, SignMessageResponse}; use crate::api::error::LdkServerError; use crate::service::Context; diff --git a/ldk-server/src/api/splice_channel.rs b/ldk-server/src/api/splice_channel.rs index 5d6edfec..1f6f2518 100644 --- a/ldk-server/src/api/splice_channel.rs +++ b/ldk-server/src/api/splice_channel.rs @@ -12,7 +12,7 @@ use std::str::FromStr; use ldk_node::bitcoin::secp256k1::PublicKey; use ldk_node::bitcoin::Address; use ldk_node::UserChannelId; -use ldk_server_protos::api::{ +use ldk_server_json_models::api::{ SpliceInRequest, SpliceInResponse, SpliceOutRequest, SpliceOutResponse, }; @@ -69,8 +69,8 @@ fn parse_user_channel_id(id: &str) -> Result { Ok(UserChannelId(parsed)) } -fn parse_counterparty_node_id(id: &str) -> Result { - PublicKey::from_str(id).map_err(|e| { +fn parse_counterparty_node_id(id: &[u8]) -> Result { + PublicKey::from_slice(id).map_err(|e| { LdkServerError::new( InvalidRequestError, format!("Invalid counterparty node ID, error: {}", e), diff --git a/ldk-server/src/api/spontaneous_send.rs b/ldk-server/src/api/spontaneous_send.rs index 77fe3b8a..b1058b96 100644 --- a/ldk-server/src/api/spontaneous_send.rs +++ b/ldk-server/src/api/spontaneous_send.rs @@ -7,12 +7,10 @@ // You may not use this file except in accordance with one or both of these // licenses. -use std::str::FromStr; - use ldk_node::bitcoin::secp256k1::PublicKey; -use ldk_server_protos::api::{SpontaneousSendRequest, SpontaneousSendResponse}; +use ldk_server_json_models::api::{SpontaneousSendRequest, SpontaneousSendResponse}; -use crate::api::build_route_parameters_config_from_proto; +use crate::api::build_route_parameters_config_from_model; use crate::api::error::LdkServerError; use crate::api::error::LdkServerErrorCode::InvalidRequestError; use crate::service::Context; @@ -20,15 +18,15 @@ use crate::service::Context; pub(crate) fn handle_spontaneous_send_request( context: Context, request: SpontaneousSendRequest, ) -> Result { - let node_id = PublicKey::from_str(&request.node_id).map_err(|_| { + let node_id = PublicKey::from_slice(&request.node_id).map_err(|_| { LdkServerError::new(InvalidRequestError, "Invalid node_id provided.".to_string()) })?; - let route_parameters = build_route_parameters_config_from_proto(request.route_parameters)?; + let route_parameters = build_route_parameters_config_from_model(request.route_parameters)?; let payment_id = context.node.spontaneous_payment().send(request.amount_msat, node_id, route_parameters)?; - let response = SpontaneousSendResponse { payment_id: payment_id.to_string() }; + let response = SpontaneousSendResponse { payment_id: payment_id.0 }; Ok(response) } diff --git a/ldk-server/src/api/unified_send.rs b/ldk-server/src/api/unified_send.rs index 3f7807b4..21d09e13 100644 --- a/ldk-server/src/api/unified_send.rs +++ b/ldk-server/src/api/unified_send.rs @@ -8,18 +8,19 @@ // licenses. use ldk_node::payment::UnifiedPaymentResult; -use ldk_server_protos::api::unified_send_response::PaymentResult; -use ldk_server_protos::api::{UnifiedSendRequest, UnifiedSendResponse}; +use ldk_server_json_models::api::{ + UnifiedSendPaymentResult, UnifiedSendRequest, UnifiedSendResponse, +}; use tokio::runtime::Handle; -use crate::api::build_route_parameters_config_from_proto; +use crate::api::build_route_parameters_config_from_model; use crate::api::error::LdkServerError; use crate::service::Context; pub(crate) fn handle_unified_send_request( context: Context, request: UnifiedSendRequest, ) -> Result { - let route_parameters = build_route_parameters_config_from_proto(request.route_parameters)?; + let route_parameters = build_route_parameters_config_from_model(request.route_parameters)?; let result = tokio::task::block_in_place(|| { Handle::current().block_on(context.node.unified_payment().send( @@ -30,12 +31,12 @@ pub(crate) fn handle_unified_send_request( })?; let payment_result = match result { - UnifiedPaymentResult::Onchain { txid } => PaymentResult::Txid(txid.to_string()), + UnifiedPaymentResult::Onchain { txid } => UnifiedSendPaymentResult::Txid(txid.to_string()), UnifiedPaymentResult::Bolt11 { payment_id } => { - PaymentResult::Bolt11PaymentId(payment_id.to_string()) + UnifiedSendPaymentResult::Bolt11PaymentId(payment_id.to_string()) }, UnifiedPaymentResult::Bolt12 { payment_id } => { - PaymentResult::Bolt12PaymentId(payment_id.to_string()) + UnifiedSendPaymentResult::Bolt12PaymentId(payment_id.to_string()) }, }; diff --git a/ldk-server/src/api/update_channel_config.rs b/ldk-server/src/api/update_channel_config.rs index 780374c5..14f13a26 100644 --- a/ldk-server/src/api/update_channel_config.rs +++ b/ldk-server/src/api/update_channel_config.rs @@ -7,13 +7,11 @@ // You may not use this file except in accordance with one or both of these // licenses. -use std::str::FromStr; - use ldk_node::bitcoin::secp256k1::PublicKey; use ldk_node::UserChannelId; -use ldk_server_protos::api::{UpdateChannelConfigRequest, UpdateChannelConfigResponse}; +use ldk_server_json_models::api::{UpdateChannelConfigRequest, UpdateChannelConfigResponse}; -use crate::api::build_channel_config_from_proto; +use crate::api::build_channel_config_from_model; use crate::api::error::LdkServerError; use crate::api::error::LdkServerErrorCode::{InvalidRequestError, LightningError}; use crate::service::Context; @@ -37,19 +35,20 @@ pub(crate) fn handle_update_channel_config_request( })? .config; - let updated_channel_config = build_channel_config_from_proto( + let updated_channel_config = build_channel_config_from_model( current_config, request.channel_config.ok_or_else(|| { LdkServerError::new(InvalidRequestError, "Channel config must be provided.") })?, )?; - let counterparty_node_id = PublicKey::from_str(&request.counterparty_node_id).map_err(|e| { - LdkServerError::new( - InvalidRequestError, - format!("Invalid counterparty node id, error {}", e), - ) - })?; + let counterparty_node_id = + PublicKey::from_slice(&request.counterparty_node_id).map_err(|e| { + LdkServerError::new( + InvalidRequestError, + format!("Invalid counterparty node id, error {}", e), + ) + })?; context .node diff --git a/ldk-server/src/api/verify_signature.rs b/ldk-server/src/api/verify_signature.rs index 9b7551ea..dcbbd4b6 100644 --- a/ldk-server/src/api/verify_signature.rs +++ b/ldk-server/src/api/verify_signature.rs @@ -7,10 +7,8 @@ // You may not use this file except in accordance with one or both of these // licenses. -use std::str::FromStr; - use ldk_node::bitcoin::secp256k1::PublicKey; -use ldk_server_protos::api::{VerifySignatureRequest, VerifySignatureResponse}; +use ldk_server_json_models::api::{VerifySignatureRequest, VerifySignatureResponse}; use crate::api::error::LdkServerError; use crate::api::error::LdkServerErrorCode::InvalidRequestError; @@ -19,7 +17,7 @@ use crate::service::Context; pub(crate) fn handle_verify_signature_request( context: Context, request: VerifySignatureRequest, ) -> Result { - let public_key = PublicKey::from_str(&request.public_key).map_err(|_| { + let public_key = PublicKey::from_slice(&request.public_key).map_err(|_| { LdkServerError::new(InvalidRequestError, "Invalid public_key provided.".to_string()) })?; diff --git a/ldk-server/src/io/events/event_publisher.rs b/ldk-server/src/io/events/event_publisher.rs index 308bde25..f6091ac4 100644 --- a/ldk-server/src/io/events/event_publisher.rs +++ b/ldk-server/src/io/events/event_publisher.rs @@ -8,7 +8,7 @@ // licenses. use async_trait::async_trait; -use ldk_server_protos::events::EventEnvelope; +use ldk_server_json_models::events::Event; use crate::api::error::LdkServerError; @@ -20,10 +20,9 @@ use crate::api::error::LdkServerError; /// the `ldk-server.config` file. A no-op implementation is included by default, /// with specific implementations enabled via feature flags. /// -/// Events are represented as [`EventEnvelope`] messages, which are Protocol Buffers -/// ([protobuf](https://protobuf.dev/)) objects defined in [`ldk_server_protos::events`]. -/// These events are serialized to bytes by the publisher before transmission, and consumers can -/// deserialize them using the protobuf definitions. +/// Events are represented as [`Event`] messages defined in [`ldk_server_json_models::events`]. +/// These events are serialized to JSON by the publisher before transmission, and consumers can +/// deserialize them using the type definitions in that module. /// /// The underlying messaging system is expected to support durably buffered events, /// enabling easy decoupling between the LDK Server and event consumers. @@ -32,9 +31,9 @@ pub trait EventPublisher: Send + Sync { /// Publishes an event to the underlying messaging system. /// /// # Arguments - /// * `event` - The event message to publish, provided as an [`EventEnvelope`] - /// defined in [`ldk_server_protos::events`]. Implementors must serialize - /// the whole [`EventEnvelope`] to bytes before publishing. + /// * `event` - The event message to publish, provided as an [`Event`] + /// defined in [`ldk_server_json_models::events`]. Implementors must serialize + /// the [`Event`] to bytes before publishing. /// /// In order to ensure no events are lost, implementors of this trait must publish events /// durably to underlying messaging system. An event is considered published when @@ -48,7 +47,7 @@ pub trait EventPublisher: Send + Sync { /// may degrade performance until the underlying messaging system is operational again. /// /// [`LdkServerErrorCode::InternalServerError`]: crate::api::error::LdkServerErrorCode - async fn publish(&self, event: EventEnvelope) -> Result<(), LdkServerError>; + async fn publish(&self, event: Event) -> Result<(), LdkServerError>; } /// A no-op implementation of the [`EventPublisher`] trait. @@ -62,7 +61,7 @@ impl EventPublisher for NoopEventPublisher { /// /// This implementation does nothing and always returns `Ok(())`, serving as a /// default when no messaging system is configured. - async fn publish(&self, _event: EventEnvelope) -> Result<(), LdkServerError> { + async fn publish(&self, _event: Event) -> Result<(), LdkServerError> { Ok(()) } } diff --git a/ldk-server/src/io/events/mod.rs b/ldk-server/src/io/events/mod.rs index 4ec9a65a..2c584237 100644 --- a/ldk-server/src/io/events/mod.rs +++ b/ldk-server/src/io/events/mod.rs @@ -12,15 +12,15 @@ pub(crate) mod event_publisher; #[cfg(feature = "events-rabbitmq")] pub(crate) mod rabbitmq; -use ldk_server_protos::events::event_envelope; +use ldk_server_json_models::events::Event; /// Event variant to event name mapping. -pub(crate) fn get_event_name(event: &event_envelope::Event) -> &'static str { +pub(crate) fn get_event_name(event: &Event) -> &'static str { match event { - event_envelope::Event::PaymentReceived(_) => "PaymentReceived", - event_envelope::Event::PaymentSuccessful(_) => "PaymentSuccessful", - event_envelope::Event::PaymentFailed(_) => "PaymentFailed", - event_envelope::Event::PaymentForwarded(_) => "PaymentForwarded", - event_envelope::Event::PaymentClaimable(_) => "PaymentClaimable", + Event::PaymentReceived(_) => "PaymentReceived", + Event::PaymentSuccessful(_) => "PaymentSuccessful", + Event::PaymentFailed(_) => "PaymentFailed", + Event::PaymentForwarded(_) => "PaymentForwarded", + Event::PaymentClaimable(_) => "PaymentClaimable", } } diff --git a/ldk-server/src/io/events/rabbitmq/mod.rs b/ldk-server/src/io/events/rabbitmq/mod.rs index a20b5bd9..f92a2b7a 100644 --- a/ldk-server/src/io/events/rabbitmq/mod.rs +++ b/ldk-server/src/io/events/rabbitmq/mod.rs @@ -9,14 +9,13 @@ use std::sync::Arc; -use ::prost::Message; use async_trait::async_trait; use lapin::options::{BasicPublishOptions, ConfirmSelectOptions, ExchangeDeclareOptions}; use lapin::types::FieldTable; use lapin::{ BasicProperties, Channel, Connection, ConnectionProperties, ConnectionState, ExchangeKind, }; -use ldk_server_protos::events::EventEnvelope; +use ldk_server_json_models::events::Event; use tokio::sync::Mutex; use crate::api::error::LdkServerError; @@ -111,7 +110,7 @@ impl EventPublisher for RabbitMqEventPublisher { /// /// The event is published to a fanout exchange with persistent delivery mode, /// and the method waits for confirmation from RabbitMQ to ensure durability. - async fn publish(&self, event: EventEnvelope) -> Result<(), LdkServerError> { + async fn publish(&self, event: Event) -> Result<(), LdkServerError> { // Ensure connection is alive before proceeding self.ensure_connected().await?; @@ -126,7 +125,7 @@ impl EventPublisher for RabbitMqEventPublisher { &self.config.exchange_name, "", // Empty routing key should be used for fanout exchange, since it is ignored. BasicPublishOptions::default(), - &event.encode_to_vec(), + &serde_json::to_vec(&event).unwrap(), BasicProperties::default().with_delivery_mode(DELIVERY_MODE_PERSISTENT), ) .await @@ -166,8 +165,7 @@ mod integration_tests_events_rabbitmq { }; use lapin::types::FieldTable; use lapin::{Channel, Connection}; - use ldk_server_protos::events::event_envelope::Event; - use ldk_server_protos::events::PaymentForwarded; + use ldk_server_json_models::events::PaymentForwarded; use tokio; use super::*; @@ -188,8 +186,20 @@ mod integration_tests_events_rabbitmq { let queue_name = "test_queue"; setup_queue(&queue_name, &channel, &config).await; - let event = - EventEnvelope { event: Some(Event::PaymentForwarded(PaymentForwarded::default())) }; + let event = Event::PaymentForwarded(PaymentForwarded { + forwarded_payment: ldk_server_json_models::types::ForwardedPayment { + prev_channel_id: [0u8; 32], + next_channel_id: [0u8; 32], + prev_user_channel_id: String::new(), + prev_node_id: [0u8; 33], + next_node_id: [0u8; 33], + next_user_channel_id: None, + total_fee_earned_msat: None, + skimmed_fee_msat: None, + claim_from_onchain_tx: false, + outbound_amount_forwarded_msat: None, + }, + }); publisher.publish(event.clone()).await.expect("Failed to publish event"); consume_event(&queue_name, &channel, &event).await.expect("Failed to consume event"); @@ -223,7 +233,7 @@ mod integration_tests_events_rabbitmq { } async fn consume_event( - queue_name: &str, channel: &Channel, expected_event: &EventEnvelope, + queue_name: &str, channel: &Channel, expected_event: &Event, ) -> io::Result<()> { let mut consumer = channel .basic_consume( @@ -236,7 +246,8 @@ mod integration_tests_events_rabbitmq { .unwrap(); let delivery = tokio::time::timeout(Duration::from_secs(10), consumer.next()).await?.unwrap().unwrap(); - let received_event = EventEnvelope::decode(&*delivery.data)?; + let received_event: Event = serde_json::from_slice(&delivery.data) + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; assert_eq!(received_event, *expected_event, "Event mismatch"); channel.basic_ack(delivery.delivery_tag, BasicAckOptions::default()).await.unwrap(); Ok(()) diff --git a/ldk-server/src/io/persist/mod.rs b/ldk-server/src/io/persist/mod.rs index 6c01795b..53f0c198 100644 --- a/ldk-server/src/io/persist/mod.rs +++ b/ldk-server/src/io/persist/mod.rs @@ -11,9 +11,9 @@ pub(crate) mod paginated_kv_store; pub(crate) mod sqlite_store; /// The forwarded payments will be persisted under this prefix. -pub(crate) const FORWARDED_PAYMENTS_PERSISTENCE_PRIMARY_NAMESPACE: &str = "forwarded_payments"; -pub(crate) const FORWARDED_PAYMENTS_PERSISTENCE_SECONDARY_NAMESPACE: &str = ""; +pub(crate) const FORWARDED_PAYMENTS_PERSISTENCE_PRIMARY_NAMESPACE: &str = "ldk-server"; +pub(crate) const FORWARDED_PAYMENTS_PERSISTENCE_SECONDARY_NAMESPACE: &str = "forwarded_payments"; /// The payments will be persisted under this prefix. -pub(crate) const PAYMENTS_PERSISTENCE_PRIMARY_NAMESPACE: &str = "payments"; -pub(crate) const PAYMENTS_PERSISTENCE_SECONDARY_NAMESPACE: &str = ""; +pub(crate) const PAYMENTS_PERSISTENCE_PRIMARY_NAMESPACE: &str = "ldk-server"; +pub(crate) const PAYMENTS_PERSISTENCE_SECONDARY_NAMESPACE: &str = "payments"; diff --git a/ldk-server/src/main.rs b/ldk-server/src/main.rs index 3de2a408..a83e311d 100644 --- a/ldk-server/src/main.rs +++ b/ldk-server/src/main.rs @@ -27,11 +27,9 @@ use ldk_node::config::Config; use ldk_node::entropy::NodeEntropy; use ldk_node::lightning::ln::channelmanager::PaymentId; use ldk_node::{Builder, Event, Node}; -use ldk_server_protos::events; -use ldk_server_protos::events::{event_envelope, EventEnvelope}; -use ldk_server_protos::types::Payment; +use ldk_server_json_models::events; +use ldk_server_json_models::types::Payment; use log::{debug, error, info}; -use prost::Message; use tokio::net::TcpListener; use tokio::select; use tokio::signal::unix::SignalKind; @@ -48,9 +46,9 @@ use crate::io::persist::{ PAYMENTS_PERSISTENCE_SECONDARY_NAMESPACE, }; use crate::service::NodeService; +use crate::util::adapter::{forwarded_payment_to_model, payment_to_model}; use crate::util::config::{load_config, ArgsConfig, ChainSource}; use crate::util::logger::ServerLogger; -use crate::util::proto_adapter::{forwarded_payment_to_proto, payment_to_proto}; use crate::util::systemd; use crate::util::tls::get_or_generate_tls_config; @@ -322,8 +320,8 @@ fn main() { let payment_id = payment_id.expect("PaymentId expected for ldk-server >=0.1"); publish_event_and_upsert_payment(&payment_id, - |payment_ref| event_envelope::Event::PaymentReceived(events::PaymentReceived { - payment: Some(payment_ref.clone()), + |payment_ref| events::Event::PaymentReceived(events::PaymentReceived { + payment: payment_ref.clone(), }), &event_node, Arc::clone(&event_publisher), @@ -333,8 +331,8 @@ fn main() { let payment_id = payment_id.expect("PaymentId expected for ldk-server >=0.1"); publish_event_and_upsert_payment(&payment_id, - |payment_ref| event_envelope::Event::PaymentSuccessful(events::PaymentSuccessful { - payment: Some(payment_ref.clone()), + |payment_ref| events::Event::PaymentSuccessful(events::PaymentSuccessful { + payment: payment_ref.clone(), }), &event_node, Arc::clone(&event_publisher), @@ -344,8 +342,8 @@ fn main() { let payment_id = payment_id.expect("PaymentId expected for ldk-server >=0.1"); publish_event_and_upsert_payment(&payment_id, - |payment_ref| event_envelope::Event::PaymentFailed(events::PaymentFailed { - payment: Some(payment_ref.clone()), + |payment_ref| events::Event::PaymentFailed(events::PaymentFailed { + payment: payment_ref.clone(), }), &event_node, Arc::clone(&event_publisher), @@ -353,8 +351,8 @@ fn main() { }, Event::PaymentClaimable {payment_id, ..} => { publish_event_and_upsert_payment(&payment_id, - |payment_ref| event_envelope::Event::PaymentClaimable(events::PaymentClaimable { - payment: Some(payment_ref.clone()), + |payment_ref| events::Event::PaymentClaimable(events::PaymentClaimable { + payment: payment_ref.clone(), }), &event_node, Arc::clone(&event_publisher), @@ -377,7 +375,7 @@ fn main() { outbound_amount_forwarded_msat.unwrap_or(0), total_fee_earned_msat.unwrap_or(0), prev_channel_id, next_channel_id ); - let forwarded_payment = forwarded_payment_to_proto( + let forwarded_payment = forwarded_payment_to_model( prev_channel_id, next_channel_id, prev_user_channel_id, @@ -398,11 +396,11 @@ fn main() { let forwarded_payment_creation_time = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time must be > 1970").as_secs() as i64; - match event_publisher.publish(EventEnvelope { - event: Some(event_envelope::Event::PaymentForwarded(events::PaymentForwarded { - forwarded_payment: Some(forwarded_payment.clone()), - })), - }).await { + match event_publisher.publish( + events::Event::PaymentForwarded(events::PaymentForwarded { + forwarded_payment: forwarded_payment.clone(), + }), + ).await { Ok(_) => {}, Err(e) => { error!("Failed to publish 'PaymentForwarded' event: {}", e); @@ -413,7 +411,7 @@ fn main() { match paginated_store.write(FORWARDED_PAYMENTS_PERSISTENCE_PRIMARY_NAMESPACE,FORWARDED_PAYMENTS_PERSISTENCE_SECONDARY_NAMESPACE, &forwarded_payment_id.to_lower_hex_string(), forwarded_payment_creation_time, - &forwarded_payment.encode_to_vec(), + &serde_json::to_vec(&forwarded_payment).unwrap(), ) { Ok(_) => { if let Err(e) = event_node.event_handled() { @@ -475,16 +473,15 @@ fn main() { } async fn publish_event_and_upsert_payment( - payment_id: &PaymentId, payment_to_event: fn(&Payment) -> event_envelope::Event, - event_node: &Node, event_publisher: Arc, - paginated_store: Arc, + payment_id: &PaymentId, payment_to_event: fn(&Payment) -> events::Event, event_node: &Node, + event_publisher: Arc, paginated_store: Arc, ) { if let Some(payment_details) = event_node.payment(payment_id) { - let payment = payment_to_proto(payment_details); + let payment = payment_to_model(payment_details); let event = payment_to_event(&payment); let event_name = get_event_name(&event); - match event_publisher.publish(EventEnvelope { event: Some(event) }).await { + match event_publisher.publish(event).await { Ok(_) => {}, Err(e) => { error!("Failed to publish '{event_name}' event, : {e}"); @@ -507,9 +504,9 @@ fn upsert_payment_details( match paginated_store.write( PAYMENTS_PERSISTENCE_PRIMARY_NAMESPACE, PAYMENTS_PERSISTENCE_SECONDARY_NAMESPACE, - &payment.id, + &payment.id.to_lower_hex_string(), time, - &payment.encode_to_vec(), + &serde_json::to_vec(&payment).unwrap(), ) { Ok(_) => { if let Err(e) = event_node.event_handled() { diff --git a/ldk-server/src/service.rs b/ldk-server/src/service.rs index 05004ae6..c5188ace 100644 --- a/ldk-server/src/service.rs +++ b/ldk-server/src/service.rs @@ -18,7 +18,7 @@ use hyper::{Request, Response, StatusCode}; use ldk_node::bitcoin::hashes::hmac::{Hmac, HmacEngine}; use ldk_node::bitcoin::hashes::{sha256, Hash, HashEngine}; use ldk_node::Node; -use ldk_server_protos::endpoints::{ +use ldk_server_json_models::endpoints::{ BOLT11_CLAIM_FOR_HASH_PATH, BOLT11_FAIL_FOR_HASH_PATH, BOLT11_RECEIVE_FOR_HASH_PATH, BOLT11_RECEIVE_PATH, BOLT11_RECEIVE_VARIABLE_AMOUNT_VIA_JIT_CHANNEL_PATH, BOLT11_RECEIVE_VIA_JIT_CHANNEL_PATH, BOLT11_SEND_PATH, BOLT12_RECEIVE_PATH, BOLT12_SEND_PATH, @@ -30,7 +30,8 @@ use ldk_server_protos::endpoints::{ SPLICE_OUT_PATH, SPONTANEOUS_SEND_PATH, UNIFIED_SEND_PATH, UPDATE_CHANNEL_CONFIG_PATH, VERIFY_SIGNATURE_PATH, }; -use prost::Message; +use serde::de::DeserializeOwned; +use serde::Serialize; use crate::api::bolt11_claim_for_hash::handle_bolt11_claim_for_hash_request; use crate::api::bolt11_fail_for_hash::handle_bolt11_fail_for_hash_request; @@ -70,7 +71,7 @@ use crate::api::unified_send::handle_unified_send_request; use crate::api::update_channel_config::handle_update_channel_config_request; use crate::api::verify_signature::handle_verify_signature_request; use crate::io::persist::paginated_kv_store::PaginatedKVStore; -use crate::util::proto_adapter::to_error_response; +use crate::util::adapter::to_error_response; // Maximum request body size: 10 MB // This prevents memory exhaustion from large requests @@ -179,7 +180,8 @@ impl Service> for NodeService { return Box::pin(async move { Ok(Response::builder() .status(status_code) - .body(Full::new(Bytes::from(error_response.encode_to_vec()))) + .header("Content-Type", "application/json") + .body(Full::new(Bytes::from(serde_json::to_vec(&error_response).unwrap()))) // unwrap safety: body only errors when previous chained calls failed. .unwrap()) }); @@ -442,8 +444,8 @@ impl Service> for NodeService { } async fn handle_request< - T: Message + Default, - R: Message, + T: DeserializeOwned, + R: Serialize, F: Fn(Context, T) -> Result, >( context: Context, request: Request, auth_params: AuthParams, api_key: String, @@ -460,7 +462,8 @@ async fn handle_request< )); return Ok(Response::builder() .status(status_code) - .body(Full::new(Bytes::from(error_response.encode_to_vec()))) + .header("Content-Type", "application/json") + .body(Full::new(Bytes::from(serde_json::to_vec(&error_response).unwrap()))) // unwrap safety: body only errors when previous chained calls failed. .unwrap()); }, @@ -473,22 +476,26 @@ async fn handle_request< let (error_response, status_code) = to_error_response(e); return Ok(Response::builder() .status(status_code) - .body(Full::new(Bytes::from(error_response.encode_to_vec()))) + .header("Content-Type", "application/json") + .body(Full::new(Bytes::from(serde_json::to_vec(&error_response).unwrap()))) // unwrap safety: body only errors when previous chained calls failed. .unwrap()); } - match T::decode(bytes) { + let decode_bytes = if bytes.is_empty() { &b"{}"[..] } else { &bytes[..] }; + match serde_json::from_slice::(decode_bytes) { Ok(request) => match handler(context, request) { Ok(response) => Ok(Response::builder() - .body(Full::new(Bytes::from(response.encode_to_vec()))) + .header("Content-Type", "application/json") + .body(Full::new(Bytes::from(serde_json::to_vec(&response).unwrap()))) // unwrap safety: body only errors when previous chained calls failed. .unwrap()), Err(e) => { let (error_response, status_code) = to_error_response(e); Ok(Response::builder() .status(status_code) - .body(Full::new(Bytes::from(error_response.encode_to_vec()))) + .header("Content-Type", "application/json") + .body(Full::new(Bytes::from(serde_json::to_vec(&error_response).unwrap()))) // unwrap safety: body only errors when previous chained calls failed. .unwrap()) }, @@ -498,7 +505,8 @@ async fn handle_request< to_error_response(LdkServerError::new(InvalidRequestError, "Malformed request.")); Ok(Response::builder() .status(status_code) - .body(Full::new(Bytes::from(error_response.encode_to_vec()))) + .header("Content-Type", "application/json") + .body(Full::new(Bytes::from(serde_json::to_vec(&error_response).unwrap()))) // unwrap safety: body only errors when previous chained calls failed. .unwrap()) }, diff --git a/ldk-server/src/util/proto_adapter.rs b/ldk-server/src/util/adapter.rs similarity index 50% rename from ldk-server/src/util/proto_adapter.rs rename to ldk-server/src/util/adapter.rs index 77441dfd..e10d5c75 100644 --- a/ldk-server/src/util/proto_adapter.rs +++ b/ldk-server/src/util/adapter.rs @@ -7,10 +7,10 @@ // You may not use this file except in accordance with one or both of these // licenses. -use bytes::Bytes; use hex::prelude::*; use hyper::StatusCode; use ldk_node::bitcoin::hashes::sha256; +use ldk_node::bitcoin::hashes::Hash as _; use ldk_node::bitcoin::secp256k1::PublicKey; use ldk_node::config::{ChannelConfig, MaxDustHTLCExposure}; use ldk_node::lightning::chain::channelmonitor::BalanceSource; @@ -23,20 +23,9 @@ use ldk_node::payment::{ ConfirmationStatus, PaymentDetails, PaymentDirection, PaymentKind, PaymentStatus, }; use ldk_node::{ChannelDetails, LightningBalance, PeerDetails, PendingSweepBalance, UserChannelId}; -use ldk_server_protos::error::{ErrorCode, ErrorResponse}; -use ldk_server_protos::types::confirmation_status::Status::{Confirmed, Unconfirmed}; -use ldk_server_protos::types::lightning_balance::BalanceType::{ - ClaimableAwaitingConfirmations, ClaimableOnChannelClose, ContentiousClaimable, - CounterpartyRevokedOutputClaimable, MaybePreimageClaimableHtlc, MaybeTimeoutClaimableHtlc, -}; -use ldk_server_protos::types::payment_kind::Kind::{ - Bolt11, Bolt11Jit, Bolt12Offer, Bolt12Refund, Onchain, Spontaneous, -}; -use ldk_server_protos::types::pending_sweep_balance::BalanceType::{ - AwaitingThresholdConfirmations, BroadcastAwaitingConfirmation, PendingBroadcast, -}; -use ldk_server_protos::types::{ - bolt11_invoice_description, Channel, ForwardedPayment, LspFeeLimits, OutPoint, Payment, Peer, +use ldk_server_json_models::error::{ErrorCode, ErrorResponse}; +use ldk_server_json_models::types::{ + Channel, ForwardedPayment, LspFeeLimits, OutPoint, Payment, Peer, }; use crate::api::error::LdkServerError; @@ -44,22 +33,22 @@ use crate::api::error::LdkServerErrorCode::{ AuthError, InternalServerError, InvalidRequestError, LightningError, }; -pub(crate) fn peer_to_proto(peer: PeerDetails) -> Peer { +pub(crate) fn peer_to_model(peer: PeerDetails) -> Peer { Peer { - node_id: peer.node_id.to_string(), + node_id: peer.node_id.serialize(), address: peer.address.to_string(), is_persisted: peer.is_persisted, is_connected: peer.is_connected, } } -pub(crate) fn channel_to_proto(channel: ChannelDetails) -> Channel { +pub(crate) fn channel_to_model(channel: ChannelDetails) -> Channel { Channel { - channel_id: channel.channel_id.0.to_lower_hex_string(), - counterparty_node_id: channel.counterparty_node_id.to_string(), + channel_id: channel.channel_id.0, + counterparty_node_id: channel.counterparty_node_id.serialize(), funding_txo: channel .funding_txo - .map(|o| OutPoint { txid: o.txid.to_string(), vout: o.vout }), + .map(|o| OutPoint { txid: o.txid.to_byte_array(), vout: o.vout }), user_channel_id: channel.user_channel_id.0.to_string(), unspendable_punishment_reserve: channel.unspendable_punishment_reserve, channel_value_sats: channel.channel_value_sats, @@ -72,7 +61,7 @@ pub(crate) fn channel_to_proto(channel: ChannelDetails) -> Channel { is_channel_ready: channel.is_channel_ready, is_usable: channel.is_usable, is_announced: channel.is_announced, - channel_config: Some(channel_config_to_proto(channel.config)), + channel_config: Some(channel_config_to_model(channel.config)), next_outbound_htlc_limit_msat: channel.next_outbound_htlc_limit_msat, next_outbound_htlc_minimum_msat: channel.next_outbound_htlc_minimum_msat, force_close_spend_delay: channel.force_close_spend_delay.map(|x| x as u32), @@ -90,10 +79,10 @@ pub(crate) fn channel_to_proto(channel: ChannelDetails) -> Channel { } } -pub(crate) fn channel_config_to_proto( +pub(crate) fn channel_config_to_model( channel_config: ChannelConfig, -) -> ldk_server_protos::types::ChannelConfig { - ldk_server_protos::types::ChannelConfig { +) -> ldk_server_json_models::types::ChannelConfig { + ldk_server_json_models::types::ChannelConfig { forwarding_fee_proportional_millionths: Some( channel_config.forwarding_fee_proportional_millionths, ), @@ -105,20 +94,16 @@ pub(crate) fn channel_config_to_proto( accept_underpaying_htlcs: Some(channel_config.accept_underpaying_htlcs), max_dust_htlc_exposure: match channel_config.max_dust_htlc_exposure { MaxDustHTLCExposure::FixedLimit { limit_msat } => { - Some(ldk_server_protos::types::channel_config::MaxDustHtlcExposure::FixedLimitMsat( - limit_msat, - )) + Some(ldk_server_json_models::types::MaxDustHtlcExposure::FixedLimitMsat(limit_msat)) }, MaxDustHTLCExposure::FeeRateMultiplier { multiplier } => Some( - ldk_server_protos::types::channel_config::MaxDustHtlcExposure::FeeRateMultiplier( - multiplier, - ), + ldk_server_json_models::types::MaxDustHtlcExposure::FeeRateMultiplier(multiplier), ), }, } } -pub(crate) fn payment_to_proto(payment: PaymentDetails) -> Payment { +pub(crate) fn payment_to_model(payment: PaymentDetails) -> Payment { let PaymentDetails { id, kind, @@ -130,41 +115,43 @@ pub(crate) fn payment_to_proto(payment: PaymentDetails) -> Payment { } = payment; Payment { - id: id.to_string(), - kind: Some(payment_kind_to_proto(kind)), + id: id.0, + kind: payment_kind_to_model(kind), amount_msat, fee_paid_msat, direction: match direction { - PaymentDirection::Inbound => ldk_server_protos::types::PaymentDirection::Inbound.into(), - PaymentDirection::Outbound => { - ldk_server_protos::types::PaymentDirection::Outbound.into() - }, + PaymentDirection::Inbound => ldk_server_json_models::types::PaymentDirection::Inbound, + PaymentDirection::Outbound => ldk_server_json_models::types::PaymentDirection::Outbound, }, status: match status { - PaymentStatus::Pending => ldk_server_protos::types::PaymentStatus::Pending.into(), - PaymentStatus::Succeeded => ldk_server_protos::types::PaymentStatus::Succeeded.into(), - PaymentStatus::Failed => ldk_server_protos::types::PaymentStatus::Failed.into(), + PaymentStatus::Pending => ldk_server_json_models::types::PaymentStatus::Pending, + PaymentStatus::Succeeded => ldk_server_json_models::types::PaymentStatus::Succeeded, + PaymentStatus::Failed => ldk_server_json_models::types::PaymentStatus::Failed, }, latest_update_timestamp, } } -pub(crate) fn payment_kind_to_proto( +pub(crate) fn payment_kind_to_model( payment_kind: PaymentKind, -) -> ldk_server_protos::types::PaymentKind { +) -> ldk_server_json_models::types::PaymentKind { match payment_kind { - PaymentKind::Onchain { txid, status } => ldk_server_protos::types::PaymentKind { - kind: Some(Onchain(ldk_server_protos::types::Onchain { - txid: txid.to_string(), - status: Some(confirmation_status_to_proto(status)), - })), + PaymentKind::Onchain { txid, status } => { + ldk_server_json_models::types::PaymentKind::Onchain( + ldk_server_json_models::types::Onchain { + txid: txid.to_byte_array(), + status: confirmation_status_to_model(status), + }, + ) }, - PaymentKind::Bolt11 { hash, preimage, secret } => ldk_server_protos::types::PaymentKind { - kind: Some(Bolt11(ldk_server_protos::types::Bolt11 { - hash: hash.to_string(), - preimage: preimage.map(|p| p.to_string()), - secret: secret.map(|s| Bytes::copy_from_slice(&s.0)), - })), + PaymentKind::Bolt11 { hash, preimage, secret } => { + ldk_server_json_models::types::PaymentKind::Bolt11( + ldk_server_json_models::types::Bolt11 { + hash: hash.0, + preimage: preimage.map(|p| p.0), + secret: secret.map(|s| s.0), + }, + ) }, PaymentKind::Bolt11Jit { hash, @@ -172,73 +159,77 @@ pub(crate) fn payment_kind_to_proto( secret, lsp_fee_limits, counterparty_skimmed_fee_msat, - } => ldk_server_protos::types::PaymentKind { - kind: Some(Bolt11Jit(ldk_server_protos::types::Bolt11Jit { - hash: hash.to_string(), - preimage: preimage.map(|p| p.to_string()), - secret: secret.map(|s| Bytes::copy_from_slice(&s.0)), + } => ldk_server_json_models::types::PaymentKind::Bolt11Jit( + ldk_server_json_models::types::Bolt11Jit { + hash: hash.0, + preimage: preimage.map(|p| p.0), + secret: secret.map(|s| s.0), lsp_fee_limits: Some(LspFeeLimits { max_total_opening_fee_msat: lsp_fee_limits.max_total_opening_fee_msat, max_proportional_opening_fee_ppm_msat: lsp_fee_limits .max_proportional_opening_fee_ppm_msat, }), counterparty_skimmed_fee_msat, - })), - }, + }, + ), PaymentKind::Bolt12Offer { hash, preimage, secret, offer_id, payer_note, quantity } => { - ldk_server_protos::types::PaymentKind { - kind: Some(Bolt12Offer(ldk_server_protos::types::Bolt12Offer { - hash: hash.map(|h| h.to_string()), - preimage: preimage.map(|p| p.to_string()), - secret: secret.map(|s| Bytes::copy_from_slice(&s.0)), - offer_id: offer_id.0.to_lower_hex_string(), + ldk_server_json_models::types::PaymentKind::Bolt12Offer( + ldk_server_json_models::types::Bolt12Offer { + hash: hash.map(|h| h.0), + preimage: preimage.map(|p| p.0), + secret: secret.map(|s| s.0), + offer_id: offer_id.0, payer_note: payer_note.map(|s| s.to_string()), quantity, - })), - } + }, + ) }, PaymentKind::Bolt12Refund { hash, preimage, secret, payer_note, quantity } => { - ldk_server_protos::types::PaymentKind { - kind: Some(Bolt12Refund(ldk_server_protos::types::Bolt12Refund { - hash: hash.map(|h| h.to_string()), - preimage: preimage.map(|p| p.to_string()), - secret: secret.map(|s| Bytes::copy_from_slice(&s.0)), + ldk_server_json_models::types::PaymentKind::Bolt12Refund( + ldk_server_json_models::types::Bolt12Refund { + hash: hash.map(|h| h.0), + preimage: preimage.map(|p| p.0), + secret: secret.map(|s| s.0), payer_note: payer_note.map(|s| s.to_string()), quantity, - })), - } + }, + ) }, - PaymentKind::Spontaneous { hash, preimage } => ldk_server_protos::types::PaymentKind { - kind: Some(Spontaneous(ldk_server_protos::types::Spontaneous { - hash: hash.to_string(), - preimage: preimage.map(|p| p.to_string()), - })), + PaymentKind::Spontaneous { hash, preimage } => { + ldk_server_json_models::types::PaymentKind::Spontaneous( + ldk_server_json_models::types::Spontaneous { + hash: hash.0, + preimage: preimage.map(|p| p.0), + }, + ) }, } } -pub(crate) fn confirmation_status_to_proto( +pub(crate) fn confirmation_status_to_model( confirmation_status: ConfirmationStatus, -) -> ldk_server_protos::types::ConfirmationStatus { +) -> ldk_server_json_models::types::ConfirmationStatus { match confirmation_status { ConfirmationStatus::Confirmed { block_hash, height, timestamp } => { - ldk_server_protos::types::ConfirmationStatus { - status: Some(Confirmed(ldk_server_protos::types::Confirmed { - block_hash: block_hash.to_string(), + ldk_server_json_models::types::ConfirmationStatus::Confirmed( + ldk_server_json_models::types::Confirmed { + block_hash: block_hash.to_byte_array(), height, timestamp, - })), - } + }, + ) }, - ConfirmationStatus::Unconfirmed => ldk_server_protos::types::ConfirmationStatus { - status: Some(Unconfirmed(ldk_server_protos::types::Unconfirmed {})), + ConfirmationStatus::Unconfirmed => { + ldk_server_json_models::types::ConfirmationStatus::Unconfirmed( + ldk_server_json_models::types::Unconfirmed {}, + ) }, } } -pub(crate) fn lightning_balance_to_proto( +pub(crate) fn lightning_balance_to_model( lightning_balance: LightningBalance, -) -> ldk_server_protos::types::LightningBalance { +) -> ldk_server_json_models::types::LightningBalance { match lightning_balance { LightningBalance::ClaimableOnChannelClose { channel_id, @@ -249,48 +240,44 @@ pub(crate) fn lightning_balance_to_proto( outbound_forwarded_htlc_rounded_msat, inbound_claiming_htlc_rounded_msat, inbound_htlc_rounded_msat, - } => ldk_server_protos::types::LightningBalance { - balance_type: Some(ClaimableOnChannelClose( - ldk_server_protos::types::ClaimableOnChannelClose { - channel_id: channel_id.0.to_lower_hex_string(), - counterparty_node_id: counterparty_node_id.to_string(), - amount_satoshis, - transaction_fee_satoshis, - outbound_payment_htlc_rounded_msat, - outbound_forwarded_htlc_rounded_msat, - inbound_claiming_htlc_rounded_msat, - inbound_htlc_rounded_msat, - }, - )), - }, + } => ldk_server_json_models::types::LightningBalance::ClaimableOnChannelClose( + ldk_server_json_models::types::ClaimableOnChannelClose { + channel_id: channel_id.0, + counterparty_node_id: counterparty_node_id.serialize(), + amount_satoshis, + transaction_fee_satoshis, + outbound_payment_htlc_rounded_msat, + outbound_forwarded_htlc_rounded_msat, + inbound_claiming_htlc_rounded_msat, + inbound_htlc_rounded_msat, + }, + ), LightningBalance::ClaimableAwaitingConfirmations { channel_id, counterparty_node_id, amount_satoshis, confirmation_height, source, - } => ldk_server_protos::types::LightningBalance { - balance_type: Some(ClaimableAwaitingConfirmations( - ldk_server_protos::types::ClaimableAwaitingConfirmations { - channel_id: channel_id.0.to_lower_hex_string(), - counterparty_node_id: counterparty_node_id.to_string(), - amount_satoshis, - confirmation_height, - source: match source { - BalanceSource::HolderForceClosed => { - ldk_server_protos::types::BalanceSource::HolderForceClosed.into() - }, - BalanceSource::CounterpartyForceClosed => { - ldk_server_protos::types::BalanceSource::CounterpartyForceClosed.into() - }, - BalanceSource::CoopClose => { - ldk_server_protos::types::BalanceSource::CoopClose.into() - }, - BalanceSource::Htlc => ldk_server_protos::types::BalanceSource::Htlc.into(), + } => ldk_server_json_models::types::LightningBalance::ClaimableAwaitingConfirmations( + ldk_server_json_models::types::ClaimableAwaitingConfirmations { + channel_id: channel_id.0, + counterparty_node_id: counterparty_node_id.serialize(), + amount_satoshis, + confirmation_height, + source: match source { + BalanceSource::HolderForceClosed => { + ldk_server_json_models::types::BalanceSource::HolderForceClosed + }, + BalanceSource::CounterpartyForceClosed => { + ldk_server_json_models::types::BalanceSource::CounterpartyForceClosed + }, + BalanceSource::CoopClose => { + ldk_server_json_models::types::BalanceSource::CoopClose }, + BalanceSource::Htlc => ldk_server_json_models::types::BalanceSource::Htlc, }, - )), - }, + }, + ), LightningBalance::ContentiousClaimable { channel_id, counterparty_node_id, @@ -298,18 +285,16 @@ pub(crate) fn lightning_balance_to_proto( timeout_height, payment_hash, payment_preimage, - } => ldk_server_protos::types::LightningBalance { - balance_type: Some(ContentiousClaimable( - ldk_server_protos::types::ContentiousClaimable { - channel_id: channel_id.0.to_lower_hex_string(), - counterparty_node_id: counterparty_node_id.to_string(), - amount_satoshis, - timeout_height, - payment_hash: payment_hash.to_string(), - payment_preimage: payment_preimage.to_string(), - }, - )), - }, + } => ldk_server_json_models::types::LightningBalance::ContentiousClaimable( + ldk_server_json_models::types::ContentiousClaimable { + channel_id: channel_id.0, + counterparty_node_id: counterparty_node_id.serialize(), + amount_satoshis, + timeout_height, + payment_hash: payment_hash.0, + payment_preimage: payment_preimage.0, + }, + ), LightningBalance::MaybeTimeoutClaimableHTLC { channel_id, counterparty_node_id, @@ -317,100 +302,90 @@ pub(crate) fn lightning_balance_to_proto( claimable_height, payment_hash, outbound_payment, - } => ldk_server_protos::types::LightningBalance { - balance_type: Some(MaybeTimeoutClaimableHtlc( - ldk_server_protos::types::MaybeTimeoutClaimableHtlc { - channel_id: channel_id.0.to_lower_hex_string(), - counterparty_node_id: counterparty_node_id.to_string(), - amount_satoshis, - claimable_height, - payment_hash: payment_hash.to_string(), - outbound_payment, - }, - )), - }, + } => ldk_server_json_models::types::LightningBalance::MaybeTimeoutClaimableHtlc( + ldk_server_json_models::types::MaybeTimeoutClaimableHtlc { + channel_id: channel_id.0, + counterparty_node_id: counterparty_node_id.serialize(), + amount_satoshis, + claimable_height, + payment_hash: payment_hash.0, + outbound_payment, + }, + ), LightningBalance::MaybePreimageClaimableHTLC { channel_id, counterparty_node_id, amount_satoshis, expiry_height, payment_hash, - } => ldk_server_protos::types::LightningBalance { - balance_type: Some(MaybePreimageClaimableHtlc( - ldk_server_protos::types::MaybePreimageClaimableHtlc { - channel_id: channel_id.0.to_lower_hex_string(), - counterparty_node_id: counterparty_node_id.to_string(), - amount_satoshis, - expiry_height, - payment_hash: payment_hash.to_string(), - }, - )), - }, + } => ldk_server_json_models::types::LightningBalance::MaybePreimageClaimableHtlc( + ldk_server_json_models::types::MaybePreimageClaimableHtlc { + channel_id: channel_id.0, + counterparty_node_id: counterparty_node_id.serialize(), + amount_satoshis, + expiry_height, + payment_hash: payment_hash.0, + }, + ), LightningBalance::CounterpartyRevokedOutputClaimable { channel_id, counterparty_node_id, amount_satoshis, - } => ldk_server_protos::types::LightningBalance { - balance_type: Some(CounterpartyRevokedOutputClaimable( - ldk_server_protos::types::CounterpartyRevokedOutputClaimable { - channel_id: channel_id.0.to_lower_hex_string(), - counterparty_node_id: counterparty_node_id.to_string(), - amount_satoshis, - }, - )), - }, + } => ldk_server_json_models::types::LightningBalance::CounterpartyRevokedOutputClaimable( + ldk_server_json_models::types::CounterpartyRevokedOutputClaimable { + channel_id: channel_id.0, + counterparty_node_id: counterparty_node_id.serialize(), + amount_satoshis, + }, + ), } } -pub(crate) fn pending_sweep_balance_to_proto( +pub(crate) fn pending_sweep_balance_to_model( pending_sweep_balance: PendingSweepBalance, -) -> ldk_server_protos::types::PendingSweepBalance { +) -> ldk_server_json_models::types::PendingSweepBalance { match pending_sweep_balance { PendingSweepBalance::PendingBroadcast { channel_id, amount_satoshis } => { - ldk_server_protos::types::PendingSweepBalance { - balance_type: Some(PendingBroadcast(ldk_server_protos::types::PendingBroadcast { - channel_id: channel_id.map(|c| c.0.to_lower_hex_string()), + ldk_server_json_models::types::PendingSweepBalance::PendingBroadcast( + ldk_server_json_models::types::PendingBroadcast { + channel_id: channel_id.map(|c| c.0), amount_satoshis, - })), - } + }, + ) }, PendingSweepBalance::BroadcastAwaitingConfirmation { channel_id, latest_broadcast_height, latest_spending_txid, amount_satoshis, - } => ldk_server_protos::types::PendingSweepBalance { - balance_type: Some(BroadcastAwaitingConfirmation( - ldk_server_protos::types::BroadcastAwaitingConfirmation { - channel_id: channel_id.map(|c| c.0.to_lower_hex_string()), - latest_broadcast_height, - latest_spending_txid: latest_spending_txid.to_string(), - amount_satoshis, - }, - )), - }, + } => ldk_server_json_models::types::PendingSweepBalance::BroadcastAwaitingConfirmation( + ldk_server_json_models::types::BroadcastAwaitingConfirmation { + channel_id: channel_id.map(|c| c.0), + latest_broadcast_height, + latest_spending_txid: latest_spending_txid.to_byte_array(), + amount_satoshis, + }, + ), PendingSweepBalance::AwaitingThresholdConfirmations { channel_id, latest_spending_txid, confirmation_hash, confirmation_height, amount_satoshis, - } => ldk_server_protos::types::PendingSweepBalance { - balance_type: Some(AwaitingThresholdConfirmations( - ldk_server_protos::types::AwaitingThresholdConfirmations { - channel_id: channel_id.map(|c| c.0.to_lower_hex_string()), - latest_spending_txid: latest_spending_txid.to_string(), - confirmation_hash: confirmation_hash.to_string(), - confirmation_height, - amount_satoshis, - }, - )), - }, + } => ldk_server_json_models::types::PendingSweepBalance::AwaitingThresholdConfirmations( + ldk_server_json_models::types::AwaitingThresholdConfirmations { + channel_id: channel_id.map(|c| c.0), + latest_spending_txid: latest_spending_txid.to_byte_array(), + confirmation_hash: confirmation_hash.to_byte_array(), + confirmation_height, + amount_satoshis, + }, + ), } } #[allow(clippy::too_many_arguments)] -pub(crate) fn forwarded_payment_to_proto( +pub(crate) fn forwarded_payment_to_model( prev_channel_id: ChannelId, next_channel_id: ChannelId, prev_user_channel_id: Option, next_user_channel_id: Option, prev_node_id: Option, next_node_id: Option, @@ -418,15 +393,15 @@ pub(crate) fn forwarded_payment_to_proto( outbound_amount_forwarded_msat: Option, ) -> ForwardedPayment { ForwardedPayment { - prev_channel_id: prev_channel_id.to_string(), - next_channel_id: next_channel_id.to_string(), + prev_channel_id: prev_channel_id.0, + next_channel_id: next_channel_id.0, prev_user_channel_id: prev_user_channel_id .expect("prev_user_channel_id expected for ldk-server >=0.1") .0 .to_string(), next_user_channel_id: next_user_channel_id.map(|u| u.0.to_string()), - prev_node_id: prev_node_id.expect("prev_node_id expected for ldk-server >=0.1").to_string(), - next_node_id: next_node_id.expect("next_node_id expected for ldk-node >=0.1").to_string(), + prev_node_id: prev_node_id.expect("prev_node_id expected for ldk-server >=0.1").serialize(), + next_node_id: next_node_id.expect("next_node_id expected for ldk-node >=0.1").serialize(), total_fee_earned_msat, skimmed_fee_msat, claim_from_onchain_tx, @@ -434,11 +409,11 @@ pub(crate) fn forwarded_payment_to_proto( } } -pub(crate) fn proto_to_bolt11_description( - description: Option, +pub(crate) fn bolt11_description_from_model( + description: Option, ) -> Result { - Ok(match description.and_then(|d| d.kind) { - Some(bolt11_invoice_description::Kind::Direct(s)) => { + Ok(match description { + Some(ldk_server_json_models::types::Bolt11InvoiceDescription::Direct(s)) => { Bolt11InvoiceDescription::Direct(Description::new(s).map_err(|e| { LdkServerError::new( InvalidRequestError, @@ -446,7 +421,7 @@ pub(crate) fn proto_to_bolt11_description( ) })?) }, - Some(bolt11_invoice_description::Kind::Hash(h)) => { + Some(ldk_server_json_models::types::Bolt11InvoiceDescription::Hash(h)) => { let hash_bytes = <[u8; 32]>::from_hex(&h).map_err(|_| { LdkServerError::new( InvalidRequestError, @@ -466,45 +441,45 @@ pub(crate) fn proto_to_bolt11_description( }) } -pub(crate) fn graph_routing_fees_to_proto( +pub(crate) fn graph_routing_fees_to_model( fees: RoutingFees, -) -> ldk_server_protos::types::GraphRoutingFees { - ldk_server_protos::types::GraphRoutingFees { +) -> ldk_server_json_models::types::GraphRoutingFees { + ldk_server_json_models::types::GraphRoutingFees { base_msat: fees.base_msat, proportional_millionths: fees.proportional_millionths, } } -pub(crate) fn graph_channel_update_to_proto( +pub(crate) fn graph_channel_update_to_model( update: ChannelUpdateInfo, -) -> ldk_server_protos::types::GraphChannelUpdate { - ldk_server_protos::types::GraphChannelUpdate { +) -> ldk_server_json_models::types::GraphChannelUpdate { + ldk_server_json_models::types::GraphChannelUpdate { last_update: update.last_update, enabled: update.enabled, cltv_expiry_delta: update.cltv_expiry_delta as u32, htlc_minimum_msat: update.htlc_minimum_msat, htlc_maximum_msat: update.htlc_maximum_msat, - fees: Some(graph_routing_fees_to_proto(update.fees)), + fees: graph_routing_fees_to_model(update.fees), } } -pub(crate) fn graph_channel_to_proto( +pub(crate) fn graph_channel_to_model( channel: ChannelInfo, -) -> ldk_server_protos::types::GraphChannel { - ldk_server_protos::types::GraphChannel { - node_one: channel.node_one.to_string(), - node_two: channel.node_two.to_string(), +) -> ldk_server_json_models::types::GraphChannel { + ldk_server_json_models::types::GraphChannel { + node_one: channel.node_one.as_slice().try_into().expect("NodeId should be 33 bytes"), + node_two: channel.node_two.as_slice().try_into().expect("NodeId should be 33 bytes"), capacity_sats: channel.capacity_sats, - one_to_two: channel.one_to_two.map(graph_channel_update_to_proto), - two_to_one: channel.two_to_one.map(graph_channel_update_to_proto), + one_to_two: channel.one_to_two.map(graph_channel_update_to_model), + two_to_one: channel.two_to_one.map(graph_channel_update_to_model), } } -pub(crate) fn graph_node_announcement_to_proto( +pub(crate) fn graph_node_announcement_to_model( announcement: NodeAnnouncementInfo, -) -> ldk_server_protos::types::GraphNodeAnnouncement { +) -> ldk_server_json_models::types::GraphNodeAnnouncement { let rgb = announcement.rgb(); - ldk_server_protos::types::GraphNodeAnnouncement { + ldk_server_json_models::types::GraphNodeAnnouncement { last_update: announcement.last_update(), alias: announcement.alias().to_string(), rgb: format!("{:02x}{:02x}{:02x}", rgb[0], rgb[1], rgb[2]), @@ -512,10 +487,10 @@ pub(crate) fn graph_node_announcement_to_proto( } } -pub(crate) fn graph_node_to_proto(node: NodeInfo) -> ldk_server_protos::types::GraphNode { - ldk_server_protos::types::GraphNode { +pub(crate) fn graph_node_to_model(node: NodeInfo) -> ldk_server_json_models::types::GraphNode { + ldk_server_json_models::types::GraphNode { channels: node.channels, - announcement_info: node.announcement_info.map(graph_node_announcement_to_proto), + announcement_info: node.announcement_info.map(graph_node_announcement_to_model), } } @@ -525,7 +500,7 @@ pub(crate) fn to_error_response(ldk_error: LdkServerError) -> (ErrorResponse, St AuthError => ErrorCode::AuthError, LightningError => ErrorCode::LightningError, InternalServerError => ErrorCode::InternalServerError, - } as i32; + }; let status = match ldk_error.error_code { InvalidRequestError => StatusCode::BAD_REQUEST, diff --git a/ldk-server/src/util/mod.rs b/ldk-server/src/util/mod.rs index 5d74de43..52162b0a 100644 --- a/ldk-server/src/util/mod.rs +++ b/ldk-server/src/util/mod.rs @@ -7,8 +7,8 @@ // You may not use this file except in accordance with one or both of these // licenses. +pub(crate) mod adapter; pub(crate) mod config; pub(crate) mod logger; -pub(crate) mod proto_adapter; pub(crate) mod systemd; pub(crate) mod tls; From 38a33899241714337193afbd116a8ffcc2362a74 Mon Sep 17 00:00:00 2001 From: Joost Jager Date: Tue, 17 Mar 2026 11:35:28 +0100 Subject: [PATCH 2/4] Replace RabbitMQ event publishing with Server-Sent Events Remove the RabbitMQ-based EventPublisher trait and lapin dependency in favor of a built-in SSE streaming endpoint. Events are now delivered directly to clients over HTTP/TLS via a /Subscribe endpoint, eliminating the need for an external message broker. The server uses a tokio broadcast channel internally. The new SseBody type implements hyper::body::Body to stream events as JSON in SSE format. Event publishing becomes synchronous and fire-and-forget when no subscribers are connected. Add a subscribe command to the CLI that connects to the SSE endpoint and prints each event as a JSON line to stdout. The client library exposes a typed async event stream via LdkServerClient::subscribe(). E2E tests use CliEventConsumer which spawns the CLI subscribe command as a child process, replacing the previous raw TLS/SSE consumer and RabbitMQ consumer. AI tools were used in preparing this commit. --- ...nts-rabbitmq.yml => integration-tests.yml} | 19 - Cargo.lock | 1167 +---------------- e2e-tests/Cargo.lock | 1153 +--------------- e2e-tests/Cargo.toml | 2 - e2e-tests/build.rs | 2 +- e2e-tests/src/lib.rs | 139 +- e2e-tests/tests/e2e.rs | 22 +- ldk-server-cli/Cargo.toml | 1 + ldk-server-cli/src/main.rs | 15 + ldk-server-client/Cargo.toml | 4 +- ldk-server-client/src/client.rs | 77 +- ldk-server-json-models/src/endpoints.rs | 1 + ldk-server/Cargo.toml | 11 - ldk-server/ldk-server-config.toml | 5 - ldk-server/src/io/events/event_publisher.rs | 67 - ldk-server/src/io/events/mod.rs | 5 +- ldk-server/src/io/events/rabbitmq/mod.rs | 255 ---- ldk-server/src/io/events/sse.rs | 76 ++ ldk-server/src/main.rs | 60 +- ldk-server/src/service.rs | 98 +- ldk-server/src/util/config.rs | 109 +- 21 files changed, 398 insertions(+), 2890 deletions(-) rename .github/workflows/{integration-tests-events-rabbitmq.yml => integration-tests.yml} (67%) delete mode 100644 ldk-server/src/io/events/event_publisher.rs delete mode 100644 ldk-server/src/io/events/rabbitmq/mod.rs create mode 100644 ldk-server/src/io/events/sse.rs diff --git a/.github/workflows/integration-tests-events-rabbitmq.yml b/.github/workflows/integration-tests.yml similarity index 67% rename from .github/workflows/integration-tests-events-rabbitmq.yml rename to .github/workflows/integration-tests.yml index 6caeb4a0..6f8ae819 100644 --- a/.github/workflows/integration-tests-events-rabbitmq.yml +++ b/.github/workflows/integration-tests.yml @@ -10,20 +10,6 @@ jobs: integration-tests: runs-on: ubuntu-latest - services: - rabbitmq: - image: rabbitmq:3 - env: - RABBITMQ_DEFAULT_USER: guest - RABBITMQ_DEFAULT_PASS: guest - ports: - - 5672:5672 - options: >- - --health-cmd "rabbitmqctl node_health_check" - --health-interval 10s - --health-timeout 5s - --health-retries 5 - steps: - name: Checkout code uses: actions/checkout@v4 @@ -45,11 +31,6 @@ jobs: - name: Set bitcoind environment variable run: echo "BITCOIND_EXE=$( pwd )/bin/bitcoind-${{ runner.os }}-${{ runner.arch }}" >> "$GITHUB_ENV" - - name: Run RabbitMQ integration tests - run: cargo test --features integration-tests-events-rabbitmq --verbose --color=always -- --nocapture - env: - RUST_BACKTRACE: 1 - - name: Run end-to-end tests run: cargo test --manifest-path e2e-tests/Cargo.toml --verbose --color=always -- --test-threads=4 --nocapture env: diff --git a/Cargo.lock b/Cargo.lock index b3fe9283..8b6c3fe4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,17 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "aes" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", -] - [[package]] name = "ahash" version = "0.8.12" @@ -34,54 +23,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "amq-protocol" -version = "7.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "587d313f3a8b4a40f866cc84b6059fe83133bf172165ac3b583129dd211d8e1c" -dependencies = [ - "amq-protocol-tcp", - "amq-protocol-types", - "amq-protocol-uri", - "cookie-factory", - "nom", - "serde", -] - -[[package]] -name = "amq-protocol-tcp" -version = "7.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc707ab9aa964a85d9fc25908a3fdc486d2e619406883b3105b48bf304a8d606" -dependencies = [ - "amq-protocol-uri", - "tcp-stream", - "tracing", -] - -[[package]] -name = "amq-protocol-types" -version = "7.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf99351d92a161c61ec6ecb213bc7057f5b837dd4e64ba6cb6491358efd770c4" -dependencies = [ - "cookie-factory", - "nom", - "serde", - "serde_json", -] - -[[package]] -name = "amq-protocol-uri" -version = "7.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f89f8273826a676282208e5af38461a07fe939def57396af6ad5997fcf56577d" -dependencies = [ - "amq-protocol-types", - "percent-encoding", - "url", -] - [[package]] name = "android_system_properties" version = "0.1.5" @@ -110,171 +51,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] -name = "asn1-rs" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56624a96882bb8c26d61312ae18cb45868e5a9992ea73c58e45c3101e56a1e60" -dependencies = [ - "asn1-rs-derive", - "asn1-rs-impl", - "displaydoc", - "nom", - "num-traits", - "rusticata-macros", - "thiserror", - "time", -] - -[[package]] -name = "asn1-rs-derive" -version = "0.6.0" +name = "async-stream" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.108", - "synstructure", + "async-stream-impl", + "futures-core", + "pin-project-lite", ] [[package]] -name = "asn1-rs-impl" -version = "0.2.0" +name = "async-stream-impl" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", "syn 2.0.108", ] -[[package]] -name = "async-channel" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" -dependencies = [ - "concurrent-queue", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-executor" -version = "1.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497c00e0fd83a72a79a39fcbd8e3e2f055d6f6c7e025f3b3d91f4f8e76527fb8" -dependencies = [ - "async-task", - "concurrent-queue", - "fastrand 2.3.0", - "futures-lite 2.6.1", - "pin-project-lite", - "slab", -] - -[[package]] -name = "async-global-executor" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13f937e26114b93193065fd44f507aa2e9169ad0cdabbb996920b1fe1ddea7ba" -dependencies = [ - "async-channel", - "async-executor", - "async-io 2.6.0", - "async-lock 3.4.1", - "blocking", - "futures-lite 2.6.1", -] - -[[package]] -name = "async-global-executor-trait" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9af57045d58eeb1f7060e7025a1631cbc6399e0a1d10ad6735b3d0ea7f8346ce" -dependencies = [ - "async-global-executor", - "async-trait", - "executor-trait", -] - -[[package]] -name = "async-io" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" -dependencies = [ - "async-lock 2.8.0", - "autocfg", - "cfg-if", - "concurrent-queue", - "futures-lite 1.13.0", - "log", - "parking", - "polling 2.8.0", - "rustix 0.37.28", - "slab", - "socket2 0.4.10", - "waker-fn", -] - -[[package]] -name = "async-io" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" -dependencies = [ - "autocfg", - "cfg-if", - "concurrent-queue", - "futures-io", - "futures-lite 2.6.1", - "parking", - "polling 3.11.0", - "rustix 1.1.2", - "slab", - "windows-sys 0.61.2", -] - -[[package]] -name = "async-lock" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" -dependencies = [ - "event-listener 2.5.3", -] - -[[package]] -name = "async-lock" -version = "3.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc" -dependencies = [ - "event-listener 5.4.1", - "event-listener-strategy", - "pin-project-lite", -] - -[[package]] -name = "async-reactor-trait" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6012d170ad00de56c9ee354aef2e358359deb1ec504254e0e5a3774771de0e" -dependencies = [ - "async-io 1.13.0", - "async-trait", - "futures-core", - "reactor-trait", -] - -[[package]] -name = "async-task" -version = "4.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" - [[package]] name = "async-trait" version = "0.1.89" @@ -320,12 +117,6 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" -[[package]] -name = "base64ct" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" - [[package]] name = "bdk_chain" version = "0.23.2" @@ -525,37 +316,6 @@ dependencies = [ "webpki-roots 0.25.4", ] -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "block-padding" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" -dependencies = [ - "generic-array", -] - -[[package]] -name = "blocking" -version = "1.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" -dependencies = [ - "async-channel", - "async-task", - "futures-io", - "futures-lite 2.6.1", - "piper", -] - [[package]] name = "bumpalo" version = "3.19.0" @@ -574,15 +334,6 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" -[[package]] -name = "cbc" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" -dependencies = [ - "cipher", -] - [[package]] name = "cc" version = "1.2.43" @@ -623,16 +374,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", -] - [[package]] name = "clap" version = "4.5.51" @@ -681,39 +422,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" -[[package]] -name = "cms" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b77c319abfd5219629c45c34c89ba945ed3c5e49fcde9d16b6c3885f118a730" -dependencies = [ - "const-oid", - "der", - "spki", - "x509-cert", -] - -[[package]] -name = "concurrent-queue" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "const-oid" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" - -[[package]] -name = "cookie-factory" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9885fa71e26b8ab7855e2ec7cae6e9b380edff76cd052e07c683a0319d51b3a2" - [[package]] name = "core-foundation" version = "0.9.4" @@ -730,104 +438,6 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" -[[package]] -name = "cpufeatures" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" -dependencies = [ - "libc", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "data-encoding" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" - -[[package]] -name = "der" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" -dependencies = [ - "const-oid", - "der_derive", - "flagset", - "pem-rfc7468", - "zeroize", -] - -[[package]] -name = "der-parser" -version = "10.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6" -dependencies = [ - "asn1-rs", - "displaydoc", - "nom", - "num-bigint", - "num-traits", - "rusticata-macros", -] - -[[package]] -name = "der_derive" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.108", -] - -[[package]] -name = "deranged" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" -dependencies = [ - "powerfmt", -] - -[[package]] -name = "des" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdd80ce8ce993de27e9f063a444a4d53ce8e8db4c1f00cc03af5ad5a9867a1e" -dependencies = [ - "cipher", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", - "subtle", -] - [[package]] name = "displaydoc" version = "0.2.5" @@ -849,12 +459,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "doc-comment" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "780955b8b195a21ab8e4ac6b60dd1dbdcec1dc6c51c0617964b08c81785e12c9" - [[package]] name = "either" version = "1.15.0" @@ -917,42 +521,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - -[[package]] -name = "event-listener" -version = "5.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener-strategy" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" -dependencies = [ - "event-listener 5.4.1", - "pin-project-lite", -] - -[[package]] -name = "executor-trait" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c39dff9342e4e0e16ce96be751eb21a94e94a87bb2f6e63ad1961c2ce109bf" -dependencies = [ - "async-trait", -] - [[package]] name = "fallible-iterator" version = "0.3.0" @@ -965,15 +533,6 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" -[[package]] -name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] - [[package]] name = "fastrand" version = "2.3.0" @@ -992,23 +551,6 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" -[[package]] -name = "flagset" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7ac824320a75a52197e8f2d787f6a38b6718bb6897a35142d749af3c0e8f4fe" - -[[package]] -name = "flume" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" -dependencies = [ - "futures-core", - "futures-sink", - "spin", -] - [[package]] name = "fnv" version = "1.0.7" @@ -1072,34 +614,6 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" -[[package]] -name = "futures-lite" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" -dependencies = [ - "fastrand 1.9.0", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] - -[[package]] -name = "futures-lite" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" -dependencies = [ - "fastrand 2.3.0", - "futures-core", - "futures-io", - "parking", - "pin-project-lite", -] - [[package]] name = "futures-macro" version = "0.3.31" @@ -1141,16 +655,6 @@ dependencies = [ "slab", ] -[[package]] -name = "generic-array" -version = "0.14.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" -dependencies = [ - "typenum", - "version_check", -] - [[package]] name = "getrandom" version = "0.2.16" @@ -1240,24 +744,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - -[[package]] -name = "hermit-abi" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - [[package]] name = "hex-conservative" version = "0.1.2" @@ -1279,15 +765,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - [[package]] name = "home" version = "0.5.12" @@ -1602,36 +1079,6 @@ dependencies = [ "hashbrown 0.16.0", ] -[[package]] -name = "inout" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" -dependencies = [ - "block-padding", - "generic-array", -] - -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi 0.3.9", - "libc", - "windows-sys 0.48.0", -] - [[package]] name = "ipnet" version = "2.11.0" @@ -1673,28 +1120,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "lapin" -version = "2.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02d2aa4725b9607915fa1a73e940710a3be6af508ce700e56897cbe8847fbb07" -dependencies = [ - "amq-protocol", - "async-global-executor-trait", - "async-reactor-trait", - "async-trait", - "executor-trait", - "flume", - "futures-core", - "futures-io", - "parking_lot", - "pinky-swear", - "reactor-trait", - "serde", - "tracing", - "waker-fn", -] - [[package]] name = "lazy_static" version = "1.5.0" @@ -1748,17 +1173,14 @@ dependencies = [ name = "ldk-server" version = "0.1.0" dependencies = [ - "async-trait", "base64 0.21.7", "chrono", "clap", - "futures-util", "getrandom 0.2.16", "hex-conservative 0.2.1", "http-body-util", "hyper 1.7.0", "hyper-util", - "lapin", "ldk-node", "ldk-server-json-models", "log", @@ -1777,6 +1199,7 @@ version = "0.1.0" dependencies = [ "clap", "clap_complete", + "futures-util", "hex-conservative 0.2.1", "ldk-server-client", "serde", @@ -1789,7 +1212,9 @@ dependencies = [ name = "ldk-server-client" version = "0.1.0" dependencies = [ + "async-stream", "bitcoin_hashes 0.14.0", + "futures-util", "ldk-server-json-models", "reqwest 0.11.27", "serde", @@ -1958,12 +1383,6 @@ dependencies = [ "bitcoin", ] -[[package]] -name = "linux-raw-sys" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" - [[package]] name = "linux-raw-sys" version = "0.4.15" @@ -1982,15 +1401,6 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" -[[package]] -name = "lock_api" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" -dependencies = [ - "scopeguard", -] - [[package]] name = "log" version = "0.4.29" @@ -2015,12 +1425,6 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - [[package]] name = "miniscript" version = "12.3.5" @@ -2036,161 +1440,41 @@ dependencies = [ name = "mio" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" -dependencies = [ - "libc", - "wasi", - "windows-sys 0.61.2", -] - -[[package]] -name = "multimap" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" - -[[package]] -name = "musig2" -version = "0.1.0" -source = "git+https://github.com/arik-so/rust-musig2?rev=6f95a05718cbb44d8fe3fa6021aea8117aa38d50#6f95a05718cbb44d8fe3fa6021aea8117aa38d50" -dependencies = [ - "bitcoin", -] - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "num-bigint" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" -dependencies = [ - "num-integer", - "num-traits", -] - -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - -[[package]] -name = "oid-registry" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7" -dependencies = [ - "asn1-rs", -] - -[[package]] -name = "once_cell" -version = "1.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" - -[[package]] -name = "openssl-probe" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" - -[[package]] -name = "p12-keystore" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cae83056e7cb770211494a0ecf66d9fa7eba7d00977e5bb91f0e925b40b937f" -dependencies = [ - "cbc", - "cms", - "der", - "des", - "hex", - "hmac", - "pkcs12", - "pkcs5", - "rand 0.9.2", - "rc2", - "sha1", - "sha2", - "thiserror", - "x509-parser", -] - -[[package]] -name = "parking" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" - -[[package]] -name = "parking_lot" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" dependencies = [ - "lock_api", - "parking_lot_core", + "libc", + "wasi", + "windows-sys 0.61.2", ] [[package]] -name = "parking_lot_core" -version = "0.9.12" +name = "multimap" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" + +[[package]] +name = "musig2" +version = "0.1.0" +source = "git+https://github.com/arik-so/rust-musig2?rev=6f95a05718cbb44d8fe3fa6021aea8117aa38d50#6f95a05718cbb44d8fe3fa6021aea8117aa38d50" dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-link", + "bitcoin", ] [[package]] -name = "pbkdf2" -version = "0.12.2" +name = "num-traits" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ - "digest", - "hmac", + "autocfg", ] [[package]] -name = "pem-rfc7468" -version = "0.7.0" +name = "once_cell" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" -dependencies = [ - "base64ct", -] +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "percent-encoding" @@ -2226,95 +1510,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "pinky-swear" -version = "6.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1ea6e230dd3a64d61bcb8b79e597d3ab6b4c94ec7a234ce687dd718b4f2e657" -dependencies = [ - "doc-comment", - "flume", - "parking_lot", - "tracing", -] - -[[package]] -name = "piper" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" -dependencies = [ - "atomic-waker", - "fastrand 2.3.0", - "futures-io", -] - -[[package]] -name = "pkcs12" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "695b3df3d3cc1015f12d70235e35b6b79befc5fa7a9b95b951eab1dd07c9efc2" -dependencies = [ - "cms", - "const-oid", - "der", - "digest", - "spki", - "x509-cert", - "zeroize", -] - -[[package]] -name = "pkcs5" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e847e2c91a18bfa887dd028ec33f2fe6f25db77db3619024764914affe8b69a6" -dependencies = [ - "aes", - "cbc", - "der", - "pbkdf2", - "scrypt", - "sha2", - "spki", -] - [[package]] name = "pkg-config" version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" -[[package]] -name = "polling" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" -dependencies = [ - "autocfg", - "bitflags 1.3.2", - "cfg-if", - "concurrent-queue", - "libc", - "log", - "pin-project-lite", - "windows-sys 0.48.0", -] - -[[package]] -name = "polling" -version = "3.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" -dependencies = [ - "cfg-if", - "concurrent-queue", - "hermit-abi 0.5.2", - "pin-project-lite", - "rustix 1.1.2", - "windows-sys 0.61.2", -] - [[package]] name = "possiblyrandom" version = "0.2.0" @@ -2332,12 +1533,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - [[package]] name = "ppv-lite86" version = "0.2.21" @@ -2549,35 +1744,6 @@ dependencies = [ "getrandom 0.3.4", ] -[[package]] -name = "rc2" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62c64daa8e9438b84aaae55010a93f396f8e60e3911590fcba770d04643fc1dd" -dependencies = [ - "cipher", -] - -[[package]] -name = "reactor-trait" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "438a4293e4d097556730f4711998189416232f009c137389e0f961d2bc0ddc58" -dependencies = [ - "async-trait", - "futures-core", - "futures-io", -] - -[[package]] -name = "redox_syscall" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags 2.10.0", -] - [[package]] name = "regex" version = "1.12.2" @@ -2631,7 +1797,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "rustls 0.21.12", - "rustls-pemfile 1.0.4", + "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", @@ -2639,10 +1805,12 @@ dependencies = [ "system-configuration", "tokio", "tokio-rustls 0.24.1", + "tokio-util", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-streams", "web-sys", "webpki-roots 0.25.4", "winreg", @@ -2720,29 +1888,6 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" -[[package]] -name = "rusticata-macros" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" -dependencies = [ - "nom", -] - -[[package]] -name = "rustix" -version = "0.37.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "519165d378b97752ca44bbe15047d5d3409e875f39327546b42ac81d7e18c1b6" -dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys 0.3.8", - "windows-sys 0.48.0", -] - [[package]] name = "rustix" version = "0.38.44" @@ -2796,32 +1941,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rustls-connector" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70cc376c6ba1823ae229bacf8ad93c136d93524eab0e4e5e0e4f96b9c4e5b212" -dependencies = [ - "log", - "rustls 0.23.34", - "rustls-native-certs", - "rustls-pki-types", - "rustls-webpki 0.103.8", -] - -[[package]] -name = "rustls-native-certs" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" -dependencies = [ - "openssl-probe", - "rustls-pemfile 2.2.0", - "rustls-pki-types", - "schannel", - "security-framework", -] - [[package]] name = "rustls-pemfile" version = "1.0.4" @@ -2831,15 +1950,6 @@ dependencies = [ "base64 0.21.7", ] -[[package]] -name = "rustls-pemfile" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" -dependencies = [ - "rustls-pki-types", -] - [[package]] name = "rustls-pki-types" version = "1.13.0" @@ -2883,41 +1993,6 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" -[[package]] -name = "salsa20" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" -dependencies = [ - "cipher", -] - -[[package]] -name = "schannel" -version = "0.1.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "scrypt" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" -dependencies = [ - "pbkdf2", - "salsa20", - "sha2", -] - [[package]] name = "sct" version = "0.7.1" @@ -2949,29 +2024,6 @@ dependencies = [ "cc", ] -[[package]] -name = "security-framework" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" -dependencies = [ - "bitflags 2.10.0", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "serde" version = "1.0.228" @@ -3036,28 +2088,6 @@ dependencies = [ "serde", ] -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha2" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - [[package]] name = "shlex" version = "1.3.0" @@ -3085,16 +2115,6 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" -[[package]] -name = "socket2" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "socket2" version = "0.5.10" @@ -3115,25 +2135,6 @@ dependencies = [ "windows-sys 0.60.2", ] -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] - -[[package]] -name = "spki" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" -dependencies = [ - "base64ct", - "der", -] - [[package]] name = "stable_deref_trait" version = "1.2.1" @@ -3221,25 +2222,13 @@ dependencies = [ "libc", ] -[[package]] -name = "tcp-stream" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "495b0abdce3dc1f8fd27240651c9e68890c14e9d9c61527b1ce44d8a5a7bd3d5" -dependencies = [ - "cfg-if", - "p12-keystore", - "rustls-connector", - "rustls-pemfile 2.2.0", -] - [[package]] name = "tempfile" version = "3.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" dependencies = [ - "fastrand 2.3.0", + "fastrand", "getrandom 0.3.4", "once_cell", "rustix 1.1.2", @@ -3266,37 +2255,6 @@ dependencies = [ "syn 2.0.108", ] -[[package]] -name = "time" -version = "0.3.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" -dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" - -[[package]] -name = "time-macros" -version = "0.2.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" -dependencies = [ - "num-conv", - "time-core", -] - [[package]] name = "tinystr" version = "0.8.2" @@ -3486,12 +2444,6 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" -[[package]] -name = "typenum" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" - [[package]] name = "unicode-ident" version = "1.0.20" @@ -3564,12 +2516,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "waker-fn" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" - [[package]] name = "want" version = "0.3.1" @@ -3652,6 +2598,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-streams" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "web-sys" version = "0.3.82" @@ -4042,34 +3001,6 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" -[[package]] -name = "x509-cert" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1301e935010a701ae5f8655edc0ad17c44bad3ac5ce8c39185f75453b720ae94" -dependencies = [ - "const-oid", - "der", - "spki", -] - -[[package]] -name = "x509-parser" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4569f339c0c402346d4a75a9e39cf8dad310e287eef1ff56d4c68e5067f53460" -dependencies = [ - "asn1-rs", - "data-encoding", - "der-parser", - "lazy_static", - "nom", - "oid-registry", - "rusticata-macros", - "thiserror", - "time", -] - [[package]] name = "yoke" version = "0.8.1" diff --git a/e2e-tests/Cargo.lock b/e2e-tests/Cargo.lock index 11b6c65e..5e64bda9 100644 --- a/e2e-tests/Cargo.lock +++ b/e2e-tests/Cargo.lock @@ -8,17 +8,6 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" -[[package]] -name = "aes" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", -] - [[package]] name = "ahash" version = "0.8.12" @@ -40,54 +29,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "amq-protocol" -version = "7.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "587d313f3a8b4a40f866cc84b6059fe83133bf172165ac3b583129dd211d8e1c" -dependencies = [ - "amq-protocol-tcp", - "amq-protocol-types", - "amq-protocol-uri", - "cookie-factory", - "nom", - "serde", -] - -[[package]] -name = "amq-protocol-tcp" -version = "7.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc707ab9aa964a85d9fc25908a3fdc486d2e619406883b3105b48bf304a8d606" -dependencies = [ - "amq-protocol-uri", - "tcp-stream", - "tracing", -] - -[[package]] -name = "amq-protocol-types" -version = "7.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf99351d92a161c61ec6ecb213bc7057f5b837dd4e64ba6cb6491358efd770c4" -dependencies = [ - "cookie-factory", - "nom", - "serde", - "serde_json", -] - -[[package]] -name = "amq-protocol-uri" -version = "7.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f89f8273826a676282208e5af38461a07fe939def57396af6ad5997fcf56577d" -dependencies = [ - "amq-protocol-types", - "percent-encoding", - "url", -] - [[package]] name = "android_system_properties" version = "0.1.5" @@ -110,171 +51,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] -name = "asn1-rs" -version = "0.7.1" +name = "async-stream" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56624a96882bb8c26d61312ae18cb45868e5a9992ea73c58e45c3101e56a1e60" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" dependencies = [ - "asn1-rs-derive", - "asn1-rs-impl", - "displaydoc", - "nom", - "num-traits", - "rusticata-macros", - "thiserror", - "time", -] - -[[package]] -name = "asn1-rs-derive" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.116", - "synstructure", + "async-stream-impl", + "futures-core", + "pin-project-lite", ] [[package]] -name = "asn1-rs-impl" -version = "0.2.0" +name = "async-stream-impl" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", "syn 2.0.116", ] -[[package]] -name = "async-channel" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" -dependencies = [ - "concurrent-queue", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-executor" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" -dependencies = [ - "async-task", - "concurrent-queue", - "fastrand 2.3.0", - "futures-lite 2.6.1", - "pin-project-lite", - "slab", -] - -[[package]] -name = "async-global-executor" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13f937e26114b93193065fd44f507aa2e9169ad0cdabbb996920b1fe1ddea7ba" -dependencies = [ - "async-channel", - "async-executor", - "async-io 2.6.0", - "async-lock 3.4.2", - "blocking", - "futures-lite 2.6.1", -] - -[[package]] -name = "async-global-executor-trait" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9af57045d58eeb1f7060e7025a1631cbc6399e0a1d10ad6735b3d0ea7f8346ce" -dependencies = [ - "async-global-executor", - "async-trait", - "executor-trait", -] - -[[package]] -name = "async-io" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" -dependencies = [ - "async-lock 2.8.0", - "autocfg", - "cfg-if", - "concurrent-queue", - "futures-lite 1.13.0", - "log", - "parking", - "polling 2.8.0", - "rustix 0.37.28", - "slab", - "socket2 0.4.10", - "waker-fn", -] - -[[package]] -name = "async-io" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" -dependencies = [ - "autocfg", - "cfg-if", - "concurrent-queue", - "futures-io", - "futures-lite 2.6.1", - "parking", - "polling 3.11.0", - "rustix 1.1.3", - "slab", - "windows-sys 0.61.2", -] - -[[package]] -name = "async-lock" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" -dependencies = [ - "event-listener 2.5.3", -] - -[[package]] -name = "async-lock" -version = "3.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" -dependencies = [ - "event-listener 5.4.1", - "event-listener-strategy", - "pin-project-lite", -] - -[[package]] -name = "async-reactor-trait" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6012d170ad00de56c9ee354aef2e358359deb1ec504254e0e5a3774771de0e" -dependencies = [ - "async-io 1.13.0", - "async-trait", - "futures-core", - "reactor-trait", -] - -[[package]] -name = "async-task" -version = "4.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" - [[package]] name = "async-trait" version = "0.1.89" @@ -326,12 +123,6 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" -[[package]] -name = "base64ct" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" - [[package]] name = "bdk_chain" version = "0.23.2" @@ -500,37 +291,6 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "block-padding" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" -dependencies = [ - "generic-array", -] - -[[package]] -name = "blocking" -version = "1.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" -dependencies = [ - "async-channel", - "async-task", - "futures-io", - "futures-lite 2.6.1", - "piper", -] - [[package]] name = "bumpalo" version = "3.20.1" @@ -569,15 +329,6 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "cbc" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" -dependencies = [ - "cipher", -] - [[package]] name = "cc" version = "1.2.56" @@ -624,49 +375,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e4de3bc4ea267985becf712dc6d9eed8b04c953b3fcfb339ebc87acd9804901" -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", -] - -[[package]] -name = "cms" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b77c319abfd5219629c45c34c89ba945ed3c5e49fcde9d16b6c3885f118a730" -dependencies = [ - "const-oid", - "der", - "spki", - "x509-cert", -] - -[[package]] -name = "concurrent-queue" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "const-oid" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" - -[[package]] -name = "cookie-factory" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9885fa71e26b8ab7855e2ec7cae6e9b380edff76cd052e07c683a0319d51b3a2" - [[package]] name = "core-foundation" version = "0.9.4" @@ -727,15 +435,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "cpufeatures" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" -dependencies = [ - "libc", -] - [[package]] name = "crc32fast" version = "1.5.0" @@ -751,89 +450,6 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" -[[package]] -name = "crypto-common" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "data-encoding" -version = "2.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" - -[[package]] -name = "der" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" -dependencies = [ - "const-oid", - "der_derive", - "flagset", - "pem-rfc7468", - "zeroize", -] - -[[package]] -name = "der-parser" -version = "10.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6" -dependencies = [ - "asn1-rs", - "displaydoc", - "nom", - "num-bigint", - "num-traits", - "rusticata-macros", -] - -[[package]] -name = "der_derive" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.116", -] - -[[package]] -name = "deranged" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc3dc5ad92c2e2d1c193bbbbdf2ea477cb81331de4f3103f267ca18368b988c4" -dependencies = [ - "powerfmt", -] - -[[package]] -name = "des" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdd80ce8ce993de27e9f063a444a4d53ce8e8db4c1f00cc03af5ad5a9867a1e" -dependencies = [ - "cipher", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", - "subtle", -] - [[package]] name = "displaydoc" version = "0.2.5" @@ -855,20 +471,12 @@ dependencies = [ "tokio", ] -[[package]] -name = "doc-comment" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "780955b8b195a21ab8e4ac6b60dd1dbdcec1dc6c51c0617964b08c81785e12c9" - [[package]] name = "e2e-tests" version = "0.1.0" dependencies = [ "corepc-node", - "futures-util", "hex-conservative", - "lapin", "ldk-node", "ldk-server-client", "ldk-server-json-models", @@ -940,42 +548,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - -[[package]] -name = "event-listener" -version = "5.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener-strategy" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" -dependencies = [ - "event-listener 5.4.1", - "pin-project-lite", -] - -[[package]] -name = "executor-trait" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c39dff9342e4e0e16ce96be751eb21a94e94a87bb2f6e63ad1961c2ce109bf" -dependencies = [ - "async-trait", -] - [[package]] name = "fallible-iterator" version = "0.3.0" @@ -988,15 +560,6 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" -[[package]] -name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] - [[package]] name = "fastrand" version = "2.3.0" @@ -1026,12 +589,6 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" -[[package]] -name = "flagset" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7ac824320a75a52197e8f2d787f6a38b6718bb6897a35142d749af3c0e8f4fe" - [[package]] name = "flate2" version = "1.1.9" @@ -1042,17 +599,6 @@ dependencies = [ "miniz_oxide", ] -[[package]] -name = "flume" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" -dependencies = [ - "futures-core", - "futures-sink", - "spin", -] - [[package]] name = "fnv" version = "1.0.7" @@ -1122,34 +668,6 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" -[[package]] -name = "futures-lite" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" -dependencies = [ - "fastrand 1.9.0", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] - -[[package]] -name = "futures-lite" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" -dependencies = [ - "fastrand 2.3.0", - "futures-core", - "futures-io", - "parking", - "pin-project-lite", -] - [[package]] name = "futures-macro" version = "0.3.32" @@ -1190,16 +708,6 @@ dependencies = [ "slab", ] -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - [[package]] name = "getrandom" version = "0.2.17" @@ -1311,24 +819,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - -[[package]] -name = "hermit-abi" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - [[package]] name = "hex-conservative" version = "0.2.2" @@ -1344,15 +834,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - [[package]] name = "home" version = "0.5.12" @@ -1522,7 +1003,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.5.10", + "socket2 0.6.2", "tokio", "tower-service", "tracing", @@ -1672,36 +1153,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "inout" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" -dependencies = [ - "block-padding", - "generic-array", -] - -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi 0.3.9", - "libc", - "windows-sys 0.48.0", -] - [[package]] name = "ipnet" version = "2.11.0" @@ -1755,28 +1206,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "lapin" -version = "2.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02d2aa4725b9607915fa1a73e940710a3be6af508ce700e56897cbe8847fbb07" -dependencies = [ - "amq-protocol", - "async-global-executor-trait", - "async-reactor-trait", - "async-trait", - "executor-trait", - "flume", - "futures-core", - "futures-io", - "parking_lot", - "pinky-swear", - "reactor-trait", - "serde", - "tracing", - "waker-fn", -] - [[package]] name = "lazy_static" version = "1.5.0" @@ -1829,7 +1258,9 @@ dependencies = [ name = "ldk-server-client" version = "0.1.0" dependencies = [ + "async-stream", "bitcoin_hashes", + "futures-util", "ldk-server-json-models", "reqwest 0.11.27", "serde", @@ -1869,7 +1300,7 @@ checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" dependencies = [ "bitflags 2.11.0", "libc", - "redox_syscall 0.7.1", + "redox_syscall", ] [[package]] @@ -2015,12 +1446,6 @@ dependencies = [ "bitcoin", ] -[[package]] -name = "linux-raw-sys" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" - [[package]] name = "linux-raw-sys" version = "0.4.15" @@ -2039,15 +1464,6 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" -[[package]] -name = "lock_api" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" -dependencies = [ - "scopeguard", -] - [[package]] name = "log" version = "0.4.29" @@ -2072,12 +1488,6 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - [[package]] name = "miniscript" version = "12.3.5" @@ -2127,150 +1537,30 @@ dependencies = [ name = "multimap" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" - -[[package]] -name = "musig2" -version = "0.1.0" -source = "git+https://github.com/arik-so/rust-musig2?rev=6f95a05718cbb44d8fe3fa6021aea8117aa38d50#6f95a05718cbb44d8fe3fa6021aea8117aa38d50" -dependencies = [ - "bitcoin", -] - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "num-bigint" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" -dependencies = [ - "num-integer", - "num-traits", -] - -[[package]] -name = "num-conv" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - -[[package]] -name = "oid-registry" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7" -dependencies = [ - "asn1-rs", -] - -[[package]] -name = "once_cell" -version = "1.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" - -[[package]] -name = "openssl-probe" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" - -[[package]] -name = "p12-keystore" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cae83056e7cb770211494a0ecf66d9fa7eba7d00977e5bb91f0e925b40b937f" -dependencies = [ - "cbc", - "cms", - "der", - "des", - "hex", - "hmac", - "pkcs12", - "pkcs5", - "rand 0.9.2", - "rc2", - "sha1", - "sha2", - "thiserror", - "x509-parser", -] - -[[package]] -name = "parking" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" - -[[package]] -name = "parking_lot" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" -dependencies = [ - "lock_api", - "parking_lot_core", -] +checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" [[package]] -name = "parking_lot_core" -version = "0.9.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +name = "musig2" +version = "0.1.0" +source = "git+https://github.com/arik-so/rust-musig2?rev=6f95a05718cbb44d8fe3fa6021aea8117aa38d50#6f95a05718cbb44d8fe3fa6021aea8117aa38d50" dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.5.18", - "smallvec", - "windows-link", + "bitcoin", ] [[package]] -name = "pbkdf2" -version = "0.12.2" +name = "num-traits" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ - "digest", - "hmac", + "autocfg", ] [[package]] -name = "pem-rfc7468" -version = "0.7.0" +name = "once_cell" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" -dependencies = [ - "base64ct", -] +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "percent-encoding" @@ -2306,95 +1596,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "pinky-swear" -version = "6.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1ea6e230dd3a64d61bcb8b79e597d3ab6b4c94ec7a234ce687dd718b4f2e657" -dependencies = [ - "doc-comment", - "flume", - "parking_lot", - "tracing", -] - -[[package]] -name = "piper" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" -dependencies = [ - "atomic-waker", - "fastrand 2.3.0", - "futures-io", -] - -[[package]] -name = "pkcs12" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "695b3df3d3cc1015f12d70235e35b6b79befc5fa7a9b95b951eab1dd07c9efc2" -dependencies = [ - "cms", - "const-oid", - "der", - "digest", - "spki", - "x509-cert", - "zeroize", -] - -[[package]] -name = "pkcs5" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e847e2c91a18bfa887dd028ec33f2fe6f25db77db3619024764914affe8b69a6" -dependencies = [ - "aes", - "cbc", - "der", - "pbkdf2", - "scrypt", - "sha2", - "spki", -] - [[package]] name = "pkg-config" version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" -[[package]] -name = "polling" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" -dependencies = [ - "autocfg", - "bitflags 1.3.2", - "cfg-if", - "concurrent-queue", - "libc", - "log", - "pin-project-lite", - "windows-sys 0.48.0", -] - -[[package]] -name = "polling" -version = "3.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" -dependencies = [ - "cfg-if", - "concurrent-queue", - "hermit-abi 0.5.2", - "pin-project-lite", - "rustix 1.1.3", - "windows-sys 0.61.2", -] - [[package]] name = "possiblyrandom" version = "0.2.0" @@ -2412,12 +1619,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - [[package]] name = "ppv-lite86" version = "0.2.21" @@ -2523,7 +1724,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls 0.23.36", - "socket2 0.5.10", + "socket2 0.6.2", "thiserror", "tokio", "tracing", @@ -2560,9 +1761,9 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.5.10", + "socket2 0.6.2", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] @@ -2639,35 +1840,6 @@ dependencies = [ "getrandom 0.3.4", ] -[[package]] -name = "rc2" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62c64daa8e9438b84aaae55010a93f396f8e60e3911590fcba770d04643fc1dd" -dependencies = [ - "cipher", -] - -[[package]] -name = "reactor-trait" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "438a4293e4d097556730f4711998189416232f009c137389e0f961d2bc0ddc58" -dependencies = [ - "async-trait", - "futures-core", - "futures-io", -] - -[[package]] -name = "redox_syscall" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags 2.11.0", -] - [[package]] name = "redox_syscall" version = "0.7.1" @@ -2730,7 +1902,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "rustls 0.21.12", - "rustls-pemfile 1.0.4", + "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", @@ -2738,10 +1910,12 @@ dependencies = [ "system-configuration", "tokio", "tokio-rustls 0.24.1", + "tokio-util", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-streams", "web-sys", "webpki-roots 0.25.4", "winreg", @@ -2821,29 +1995,6 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" -[[package]] -name = "rusticata-macros" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" -dependencies = [ - "nom", -] - -[[package]] -name = "rustix" -version = "0.37.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "519165d378b97752ca44bbe15047d5d3409e875f39327546b42ac81d7e18c1b6" -dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys 0.3.8", - "windows-sys 0.48.0", -] - [[package]] name = "rustix" version = "0.38.44" @@ -2897,32 +2048,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rustls-connector" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70cc376c6ba1823ae229bacf8ad93c136d93524eab0e4e5e0e4f96b9c4e5b212" -dependencies = [ - "log", - "rustls 0.23.36", - "rustls-native-certs", - "rustls-pki-types", - "rustls-webpki 0.103.9", -] - -[[package]] -name = "rustls-native-certs" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" -dependencies = [ - "openssl-probe", - "rustls-pemfile 2.2.0", - "rustls-pki-types", - "schannel", - "security-framework", -] - [[package]] name = "rustls-pemfile" version = "1.0.4" @@ -2932,15 +2057,6 @@ dependencies = [ "base64 0.21.7", ] -[[package]] -name = "rustls-pemfile" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" -dependencies = [ - "rustls-pki-types", -] - [[package]] name = "rustls-pki-types" version = "1.14.0" @@ -2984,41 +2100,6 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" -[[package]] -name = "salsa20" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" -dependencies = [ - "cipher", -] - -[[package]] -name = "schannel" -version = "0.1.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "scrypt" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" -dependencies = [ - "pbkdf2", - "salsa20", - "sha2", -] - [[package]] name = "sct" version = "0.7.1" @@ -3050,29 +2131,6 @@ dependencies = [ "cc", ] -[[package]] -name = "security-framework" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" -dependencies = [ - "bitflags 2.11.0", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "321c8673b092a9a42605034a9879d73cb79101ed5fd117bc9a597b89b4e9e61a" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "semver" version = "1.0.27" @@ -3134,28 +2192,6 @@ dependencies = [ "serde", ] -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha2" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - [[package]] name = "shlex" version = "1.3.0" @@ -3180,16 +2216,6 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" -[[package]] -name = "socket2" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "socket2" version = "0.5.10" @@ -3210,25 +2236,6 @@ dependencies = [ "windows-sys 0.60.2", ] -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] - -[[package]] -name = "spki" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" -dependencies = [ - "base64ct", - "der", -] - [[package]] name = "stable_deref_trait" version = "1.2.1" @@ -3321,25 +2328,13 @@ dependencies = [ "xattr", ] -[[package]] -name = "tcp-stream" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "495b0abdce3dc1f8fd27240651c9e68890c14e9d9c61527b1ce44d8a5a7bd3d5" -dependencies = [ - "cfg-if", - "p12-keystore", - "rustls-connector", - "rustls-pemfile 2.2.0", -] - [[package]] name = "tempfile" version = "3.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0136791f7c95b1f6dd99f9cc786b91bb81c3800b639b3478e561ddb7be95e5f1" dependencies = [ - "fastrand 2.3.0", + "fastrand", "getrandom 0.4.1", "once_cell", "rustix 1.1.3", @@ -3366,37 +2361,6 @@ dependencies = [ "syn 2.0.116", ] -[[package]] -name = "time" -version = "0.3.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" -dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde_core", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" - -[[package]] -name = "time-macros" -version = "0.2.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" -dependencies = [ - "num-conv", - "time-core", -] - [[package]] name = "tinystr" version = "0.8.2" @@ -3551,12 +2515,6 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" -[[package]] -name = "typenum" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" - [[package]] name = "unicode-ident" version = "1.0.24" @@ -3636,12 +2594,6 @@ dependencies = [ "url", ] -[[package]] -name = "waker-fn" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" - [[package]] name = "want" version = "0.3.1" @@ -3756,6 +2708,19 @@ dependencies = [ "wasmparser", ] +[[package]] +name = "wasm-streams" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "wasmparser" version = "0.244.0" @@ -4240,34 +3205,6 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" -[[package]] -name = "x509-cert" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1301e935010a701ae5f8655edc0ad17c44bad3ac5ce8c39185f75453b720ae94" -dependencies = [ - "const-oid", - "der", - "spki", -] - -[[package]] -name = "x509-parser" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4569f339c0c402346d4a75a9e39cf8dad310e287eef1ff56d4c68e5067f53460" -dependencies = [ - "asn1-rs", - "data-encoding", - "der-parser", - "lazy_static", - "nom", - "oid-registry", - "rusticata-macros", - "thiserror", - "time", -] - [[package]] name = "xattr" version = "1.6.1" diff --git a/e2e-tests/Cargo.toml b/e2e-tests/Cargo.toml index e0f42f61..81755335 100644 --- a/e2e-tests/Cargo.toml +++ b/e2e-tests/Cargo.toml @@ -11,6 +11,4 @@ ldk-server-client = { path = "../ldk-server-client" } ldk-server-json-models = { path = "../ldk-server-json-models" } serde_json = "1.0" hex-conservative = { version = "0.2", features = ["std"] } -lapin = { version = "2.4.0", features = ["rustls"], default-features = false } -futures-util = "0.3" ldk-node = { git = "https://github.com/lightningdevkit/ldk-node", rev = "d1bbf978c8b7abe87ae2e40793556c1fe4e7ea49" } diff --git a/e2e-tests/build.rs b/e2e-tests/build.rs index 0741cbe8..3902586d 100644 --- a/e2e-tests/build.rs +++ b/e2e-tests/build.rs @@ -21,7 +21,7 @@ fn main() { "-p", "ldk-server", "--features", - "events-rabbitmq,experimental-lsps2-support", + "experimental-lsps2-support", "-p", "ldk-server-cli", ]) diff --git a/e2e-tests/src/lib.rs b/e2e-tests/src/lib.rs index 41f4c49f..44ae7202 100644 --- a/e2e-tests/src/lib.rs +++ b/e2e-tests/src/lib.rs @@ -93,7 +93,6 @@ pub struct LdkServerHandle { pub api_key: String, pub tls_cert_path: PathBuf, pub node_id: [u8; 33], - pub exchange_name: String, client: LdkServerClient, } @@ -109,8 +108,6 @@ impl LdkServerHandle { let (rpc_host, rpc_port_num, rpc_user, rpc_password) = bitcoind.rpc_details(); let rpc_address = format!("{rpc_host}:{rpc_port_num}"); - let exchange_name = format!("e2e_test_exchange_{rest_port}"); - let config_content = format!( r#"[node] network = "regtest" @@ -126,10 +123,6 @@ rpc_address = "{rpc_address}" rpc_user = "{rpc_user}" rpc_password = "{rpc_password}" -[rabbitmq] -connection_string = "amqp://guest:guest@localhost:5672/%2f" -exchange_name = "{exchange_name}" - [liquidity.lsps2_service] advertise_service = false channel_opening_fee_ppm = 10000 @@ -202,7 +195,6 @@ client_trusts_lsp = true api_key, tls_cert_path, node_id: [0u8; 33], - exchange_name, client, }; @@ -426,99 +418,72 @@ pub async fn setup_funded_channel( open_resp.user_channel_id } -/// RabbitMQ event consumer for verifying events published by ldk-server. -pub struct RabbitMqEventConsumer { - _connection: lapin::Connection, - channel: lapin::Channel, - consumer: lapin::Consumer, +/// Event consumer that spawns `ldk-server-cli subscribe` and reads JSON events from stdout. +pub struct CliEventConsumer { + receiver: tokio::sync::mpsc::Receiver, + child: Option, } -impl RabbitMqEventConsumer { - /// Connect to RabbitMQ and create an exclusive queue bound to the given exchange. - pub async fn new(exchange_name: &str) -> Self { - use lapin::options::{ - BasicConsumeOptions, ExchangeDeclareOptions, QueueBindOptions, QueueDeclareOptions, - }; - use lapin::types::FieldTable; - use lapin::{ConnectionProperties, ExchangeKind}; +impl CliEventConsumer { + /// Start the CLI subscribe command and begin receiving events in the background. + pub fn new(server: &LdkServerHandle) -> Self { + let cli_path = cli_binary_path(); + let mut child = Command::new(&cli_path) + .arg("--base-url") + .arg(server.base_url()) + .arg("--api-key") + .arg(&server.api_key) + .arg("--tls-cert") + .arg(server.tls_cert_path.to_str().unwrap()) + .arg("subscribe") + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .unwrap_or_else(|e| panic!("Failed to start CLI subscribe at {:?}: {}", cli_path, e)); - let connection = lapin::Connection::connect( - "amqp://guest:guest@localhost:5672/%2f", - ConnectionProperties::default(), - ) - .await - .expect("Failed to connect to RabbitMQ"); - - let channel = connection.create_channel().await.expect("Failed to create channel"); - - // Declare exchange (idempotent — may already exist from the server) - channel - .exchange_declare( - exchange_name, - ExchangeKind::Fanout, - ExchangeDeclareOptions { durable: true, ..Default::default() }, - FieldTable::default(), - ) - .await - .expect("Failed to declare exchange"); - - // Create exclusive auto-delete queue with server-generated name - let queue = channel - .queue_declare( - "", - QueueDeclareOptions { exclusive: true, auto_delete: true, ..Default::default() }, - FieldTable::default(), - ) - .await - .expect("Failed to declare queue"); - let queue_name = queue.name().to_string(); - - channel - .queue_bind( - &queue_name, - exchange_name, - "", - QueueBindOptions::default(), - FieldTable::default(), - ) - .await - .expect("Failed to bind queue"); - - let consumer = channel - .basic_consume( - &queue_name, - &format!("consumer_{}", queue_name), - BasicConsumeOptions::default(), - FieldTable::default(), - ) - .await - .expect("Failed to start consumer"); - - Self { _connection: connection, channel, consumer } + let stdout = child.stdout.take().unwrap(); + let (tx, rx) = tokio::sync::mpsc::channel(64); + + std::thread::spawn(move || { + let reader = BufReader::new(stdout); + for line in reader.lines().map_while(Result::ok) { + let trimmed = line.trim(); + if trimmed.is_empty() { + continue; + } + if let Ok(event) = + serde_json::from_str::(trimmed) + { + if tx.blocking_send(event).is_err() { + break; + } + } + } + }); + + Self { receiver: rx, child: Some(child) } } /// Consume up to `count` events, waiting up to `timeout` for each. pub async fn consume_events( &mut self, count: usize, timeout: Duration, ) -> Vec { - use futures_util::StreamExt; - use lapin::options::BasicAckOptions; - let mut events = Vec::new(); for _ in 0..count { - match tokio::time::timeout(timeout, self.consumer.next()).await { - Ok(Some(Ok(delivery))) => { - let event: ldk_server_json_models::events::Event = - serde_json::from_slice(&delivery.data).expect("Failed to decode event"); - self.channel - .basic_ack(delivery.delivery_tag, BasicAckOptions::default()) - .await - .expect("Failed to ack"); - events.push(event); - }, + match tokio::time::timeout(timeout, self.receiver.recv()).await { + Ok(Some(event)) => events.push(event), _ => break, } } events } } + +impl Drop for CliEventConsumer { + fn drop(&mut self) { + if let Some(mut child) = self.child.take() { + let _ = child.kill(); + let _ = child.wait(); + } + } +} diff --git a/e2e-tests/tests/e2e.rs b/e2e-tests/tests/e2e.rs index 4d9a1270..6409c40e 100644 --- a/e2e-tests/tests/e2e.rs +++ b/e2e-tests/tests/e2e.rs @@ -12,7 +12,7 @@ use std::time::Duration; use e2e_tests::{ find_available_port, mine_and_sync, run_cli, run_cli_raw, setup_funded_channel, - wait_for_onchain_balance, LdkServerHandle, RabbitMqEventConsumer, TestBitcoind, + wait_for_onchain_balance, LdkServerHandle, CliEventConsumer, TestBitcoind, }; use hex_conservative::{DisplayHex, FromHex}; use ldk_node::bitcoin::hashes::{sha256, Hash}; @@ -328,8 +328,8 @@ async fn test_cli_bolt11_send() { let server_b = LdkServerHandle::start(&bitcoind).await; // Set up event consumers before any payments - let mut consumer_a = RabbitMqEventConsumer::new(&server_a.exchange_name).await; - let mut consumer_b = RabbitMqEventConsumer::new(&server_b.exchange_name).await; + let mut consumer_a = CliEventConsumer::new(&server_a); + let mut consumer_b = CliEventConsumer::new(&server_b); setup_funded_channel(&bitcoind, &server_a, &server_b, 100_000).await; @@ -429,8 +429,8 @@ async fn test_cli_spontaneous_send() { let server_a = LdkServerHandle::start(&bitcoind).await; let server_b = LdkServerHandle::start(&bitcoind).await; - let mut consumer_a = RabbitMqEventConsumer::new(&server_a.exchange_name).await; - let mut consumer_b = RabbitMqEventConsumer::new(&server_b.exchange_name).await; + let mut consumer_a = CliEventConsumer::new(&server_a); + let mut consumer_b = CliEventConsumer::new(&server_b); setup_funded_channel(&bitcoind, &server_a, &server_b, 100_000).await; @@ -667,8 +667,8 @@ async fn test_forwarded_payment_event() { // B: LSP node (all e2e servers include LSPS2 service config) let server_b = LdkServerHandle::start(&bitcoind).await; - // Set up RabbitMQ consumer on B before any payments - let mut consumer_b = RabbitMqEventConsumer::new(&server_b.exchange_name).await; + // Set up SSE consumer on B before any payments + let mut consumer_b = CliEventConsumer::new(&server_b); // Open channel A -> B (1M sats, larger for JIT forwarding) setup_funded_channel(&bitcoind, &server_a, &server_b, 1_000_000).await; @@ -746,8 +746,8 @@ async fn test_hodl_invoice_claim() { let server_a = LdkServerHandle::start(&bitcoind).await; let server_b = LdkServerHandle::start(&bitcoind).await; - let mut consumer_a = RabbitMqEventConsumer::new(&server_a.exchange_name).await; - let mut consumer_b = RabbitMqEventConsumer::new(&server_b.exchange_name).await; + let mut consumer_a = CliEventConsumer::new(&server_a); + let mut consumer_b = CliEventConsumer::new(&server_b); setup_funded_channel(&bitcoind, &server_a, &server_b, 100_000).await; @@ -825,8 +825,8 @@ async fn test_hodl_invoice_fail() { let server_b = LdkServerHandle::start(&bitcoind).await; // Set up event consumers before any payments - let mut consumer_a = RabbitMqEventConsumer::new(&server_a.exchange_name).await; - let mut consumer_b = RabbitMqEventConsumer::new(&server_b.exchange_name).await; + let mut consumer_a = CliEventConsumer::new(&server_a); + let mut consumer_b = CliEventConsumer::new(&server_b); setup_funded_channel(&bitcoind, &server_a, &server_b, 100_000).await; diff --git a/ldk-server-cli/Cargo.toml b/ldk-server-cli/Cargo.toml index 4b839146..e9cccebb 100644 --- a/ldk-server-cli/Cargo.toml +++ b/ldk-server-cli/Cargo.toml @@ -11,4 +11,5 @@ hex-conservative = { version = "0.2", default-features = false, features = ["std tokio = { version = "1.38.0", default-features = false, features = ["rt-multi-thread", "macros"] } serde = "1.0" serde_json = "1.0" +futures-util = { version = "0.3", default-features = false } toml = { version = "0.8", default-features = false, features = ["parse"] } diff --git a/ldk-server-cli/src/main.rs b/ldk-server-cli/src/main.rs index b60e9d2d..c53fc9fe 100644 --- a/ldk-server-cli/src/main.rs +++ b/ldk-server-cli/src/main.rs @@ -522,6 +522,8 @@ enum Commands { #[arg(help = "The hex-encoded node ID to look up")] node_id: String, }, + #[command(about = "Subscribe to server-sent events and print each event as a JSON line")] + Subscribe, #[command(about = "Generate shell completions for the CLI")] Completions { #[arg( @@ -1114,6 +1116,19 @@ async fn main() { .await, ); }, + Commands::Subscribe => { + use futures_util::StreamExt; + let stream = client.subscribe().await.unwrap_or_else(|e| handle_error(e)); + tokio::pin!(stream); + while let Some(event) = stream.next().await { + match serde_json::to_string(&event) { + Ok(json) => println!("{json}"), + Err(e) => { + eprintln!("Error serializing event: {e}"); + }, + } + } + }, Commands::Completions { .. } => unreachable!("Handled above"), } } diff --git a/ldk-server-client/Cargo.toml b/ldk-server-client/Cargo.toml index c1560fad..f30d5851 100644 --- a/ldk-server-client/Cargo.toml +++ b/ldk-server-client/Cargo.toml @@ -5,7 +5,9 @@ edition = "2021" [dependencies] ldk-server-json-models = { path = "../ldk-server-json-models" } -reqwest = { version = "0.11.13", default-features = false, features = ["rustls-tls"] } +reqwest = { version = "0.11.13", default-features = false, features = ["rustls-tls", "stream"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" bitcoin_hashes = "0.14" +futures-util = { version = "0.3", default-features = false } +async-stream = "0.3" diff --git a/ldk-server-client/src/client.rs b/ldk-server-client/src/client.rs index a087950f..0859f53e 100644 --- a/ldk-server-client/src/client.rs +++ b/ldk-server-client/src/client.rs @@ -42,8 +42,8 @@ use ldk_server_json_models::endpoints::{ GRAPH_GET_CHANNEL_PATH, GRAPH_GET_NODE_PATH, GRAPH_LIST_CHANNELS_PATH, GRAPH_LIST_NODES_PATH, LIST_CHANNELS_PATH, LIST_FORWARDED_PAYMENTS_PATH, LIST_PAYMENTS_PATH, LIST_PEERS_PATH, ONCHAIN_RECEIVE_PATH, ONCHAIN_SEND_PATH, OPEN_CHANNEL_PATH, SIGN_MESSAGE_PATH, SPLICE_IN_PATH, - SPLICE_OUT_PATH, SPONTANEOUS_SEND_PATH, UNIFIED_SEND_PATH, UPDATE_CHANNEL_CONFIG_PATH, - VERIFY_SIGNATURE_PATH, + SPLICE_OUT_PATH, SPONTANEOUS_SEND_PATH, SUBSCRIBE_PATH, UNIFIED_SEND_PATH, + UPDATE_CHANNEL_CONFIG_PATH, VERIFY_SIGNATURE_PATH, }; use ldk_server_json_models::error::{ErrorCode, ErrorResponse}; use reqwest::header::CONTENT_TYPE; @@ -429,6 +429,79 @@ impl LdkServerClient { self.post_request(&request, &url).await } + /// Subscribe to server-sent events. Returns an async stream of [`Event`] values. + /// + /// The stream yields one item per SSE event. It ends when the server closes the connection. + /// + /// [`Event`]: ldk_server_json_models::events::Event + pub async fn subscribe( + &self, + ) -> Result< + impl futures_util::Stream, + LdkServerError, + > { + use futures_util::StreamExt; + let url = format!("https://{}/{SUBSCRIBE_PATH}", self.base_url); + let auth_header = self.compute_auth_header(&[]); + let response = self + .client + .get(&url) + .header("X-Auth", auth_header) + .header("Accept", "text/event-stream") + .send() + .await + .map_err(|e| { + LdkServerError::new(InternalError, format!("HTTP request failed: {}", e)) + })?; + + if !response.status().is_success() { + let status = response.status(); + let payload = response.bytes().await.map_err(|e| { + LdkServerError::new(InternalError, format!("Failed to read response body: {}", e)) + })?; + let error_response = + serde_json::from_slice::(&payload).map_err(|e| { + LdkServerError::new( + JsonParseError, + format!("Failed to decode error response (status {}): {}", status, e), + ) + })?; + let error_code = match error_response.error_code { + ErrorCode::InvalidRequestError => InvalidRequestError, + ErrorCode::AuthError => AuthError, + ErrorCode::LightningError => LightningError, + ErrorCode::InternalServerError => InternalServerError, + ErrorCode::UnknownError => InternalError, + }; + return Err(LdkServerError::new(error_code, error_response.message)); + } + + let stream = async_stream::stream! { + let mut byte_stream = response.bytes_stream(); + let mut buffer = String::new(); + while let Some(chunk) = byte_stream.next().await { + let chunk = match chunk { + Ok(c) => c, + Err(_) => break, + }; + buffer.push_str(&String::from_utf8_lossy(&chunk)); + while let Some(pos) = buffer.find("\n\n") { + let event_block = buffer[..pos].to_string(); + buffer = buffer[pos + 2..].to_string(); + for line in event_block.lines() { + if let Some(data) = line.strip_prefix("data: ") { + if let Ok(event) = serde_json::from_str::(data) { + yield event; + } + } + } + } + } + }; + + Ok(stream) + } + async fn post_request( &self, request: &Rq, url: &str, ) -> Result { diff --git a/ldk-server-json-models/src/endpoints.rs b/ldk-server-json-models/src/endpoints.rs index c6818dee..204216d7 100644 --- a/ldk-server-json-models/src/endpoints.rs +++ b/ldk-server-json-models/src/endpoints.rs @@ -43,3 +43,4 @@ pub const GRAPH_LIST_CHANNELS_PATH: &str = "GraphListChannels"; pub const GRAPH_GET_CHANNEL_PATH: &str = "GraphGetChannel"; pub const GRAPH_LIST_NODES_PATH: &str = "GraphListNodes"; pub const GRAPH_GET_NODE_PATH: &str = "GraphGetNode"; +pub const SUBSCRIBE_PATH: &str = "Subscribe"; diff --git a/ldk-server/Cargo.toml b/ldk-server/Cargo.toml index 7fddbc6e..152fe908 100644 --- a/ldk-server/Cargo.toml +++ b/ldk-server/Cargo.toml @@ -17,25 +17,14 @@ getrandom = { version = "0.2", default-features = false } ldk-server-json-models = { path = "../ldk-server-json-models" } hex = { package = "hex-conservative", version = "0.2.1", default-features = false } rusqlite = { version = "0.31.0", features = ["bundled"] } -async-trait = { version = "0.1.85", default-features = false } toml = { version = "0.8.9", default-features = false, features = ["parse"] } chrono = { version = "0.4", default-features = false, features = ["clock"] } log = "0.4.28" base64 = { version = "0.21", default-features = false, features = ["std"] } clap = { version = "4.0.5", default-features = false, features = ["derive", "std", "error-context", "suggestions", "help", "env"] } -# Required for RabittMQ based EventPublisher. Only enabled for `events-rabbitmq` feature. -lapin = { version = "2.4.0", features = ["rustls"], default-features = false, optional = true } - [features] default = [] -events-rabbitmq = ["dep:lapin"] # Experimental Features. experimental-lsps2-support = [] - -# Feature-flags related to integration tests. -integration-tests-events-rabbitmq = ["events-rabbitmq"] - -[dev-dependencies] -futures-util = "0.3.31" diff --git a/ldk-server/ldk-server-config.toml b/ldk-server/ldk-server-config.toml index d4345bc3..cf1cac37 100644 --- a/ldk-server/ldk-server-config.toml +++ b/ldk-server/ldk-server-config.toml @@ -38,11 +38,6 @@ server_url = "ssl://electrum.blockstream.info:50002" # Electrum endpoint [esplora] server_url = "https://mempool.space/api" # Esplora endpoint -# RabbitMQ settings (only required if using events-rabbitmq feature) -[rabbitmq] -connection_string = "" # RabbitMQ connection string -exchange_name = "" - # LSPS2 Client Support [liquidity.lsps2_client] # The public key of the LSPS2 LSP we source just-in-time liquidity from. diff --git a/ldk-server/src/io/events/event_publisher.rs b/ldk-server/src/io/events/event_publisher.rs deleted file mode 100644 index f6091ac4..00000000 --- a/ldk-server/src/io/events/event_publisher.rs +++ /dev/null @@ -1,67 +0,0 @@ -// This file is Copyright its original authors, visible in version control -// history. -// -// This file is licensed under the Apache License, Version 2.0 or the MIT license -// , at your option. -// You may not use this file except in accordance with one or both of these -// licenses. - -use async_trait::async_trait; -use ldk_server_json_models::events::Event; - -use crate::api::error::LdkServerError; - -/// A trait for publishing events or notifications from the LDK Server. -/// -/// Implementors of this trait define how events are sent to various messaging -/// systems. It provides a consistent, asynchronous interface for event publishing, while allowing -/// each implementation to manage its own initialization and configuration, typically sourced from -/// the `ldk-server.config` file. A no-op implementation is included by default, -/// with specific implementations enabled via feature flags. -/// -/// Events are represented as [`Event`] messages defined in [`ldk_server_json_models::events`]. -/// These events are serialized to JSON by the publisher before transmission, and consumers can -/// deserialize them using the type definitions in that module. -/// -/// The underlying messaging system is expected to support durably buffered events, -/// enabling easy decoupling between the LDK Server and event consumers. -#[async_trait] -pub trait EventPublisher: Send + Sync { - /// Publishes an event to the underlying messaging system. - /// - /// # Arguments - /// * `event` - The event message to publish, provided as an [`Event`] - /// defined in [`ldk_server_json_models::events`]. Implementors must serialize - /// the [`Event`] to bytes before publishing. - /// - /// In order to ensure no events are lost, implementors of this trait must publish events - /// durably to underlying messaging system. An event is considered published when - /// [`EventPublisher::publish`] returns `Ok(())`, thus implementors MUST durably persist/publish events *before* - /// returning `Ok(())`. - /// - /// # Errors - /// May return an [`LdkServerErrorCode::InternalServerError`] if the event cannot be published, - /// such as due to network failures, misconfiguration, or transport-specific issues. - /// If event publishing fails, the LDK Server will retry publishing the event indefinitely, which - /// may degrade performance until the underlying messaging system is operational again. - /// - /// [`LdkServerErrorCode::InternalServerError`]: crate::api::error::LdkServerErrorCode - async fn publish(&self, event: Event) -> Result<(), LdkServerError>; -} - -/// A no-op implementation of the [`EventPublisher`] trait. -#[cfg(not(feature = "events-rabbitmq"))] -pub(crate) struct NoopEventPublisher; - -#[async_trait] -#[cfg(not(feature = "events-rabbitmq"))] -impl EventPublisher for NoopEventPublisher { - /// Publishes an event to a no-op sink, effectively discarding it. - /// - /// This implementation does nothing and always returns `Ok(())`, serving as a - /// default when no messaging system is configured. - async fn publish(&self, _event: Event) -> Result<(), LdkServerError> { - Ok(()) - } -} diff --git a/ldk-server/src/io/events/mod.rs b/ldk-server/src/io/events/mod.rs index 2c584237..993a8478 100644 --- a/ldk-server/src/io/events/mod.rs +++ b/ldk-server/src/io/events/mod.rs @@ -7,10 +7,7 @@ // You may not use this file except in accordance with one or both of these // licenses. -pub(crate) mod event_publisher; - -#[cfg(feature = "events-rabbitmq")] -pub(crate) mod rabbitmq; +pub(crate) mod sse; use ldk_server_json_models::events::Event; diff --git a/ldk-server/src/io/events/rabbitmq/mod.rs b/ldk-server/src/io/events/rabbitmq/mod.rs deleted file mode 100644 index f92a2b7a..00000000 --- a/ldk-server/src/io/events/rabbitmq/mod.rs +++ /dev/null @@ -1,255 +0,0 @@ -// This file is Copyright its original authors, visible in version control -// history. -// -// This file is licensed under the Apache License, Version 2.0 or the MIT license -// , at your option. -// You may not use this file except in accordance with one or both of these -// licenses. - -use std::sync::Arc; - -use async_trait::async_trait; -use lapin::options::{BasicPublishOptions, ConfirmSelectOptions, ExchangeDeclareOptions}; -use lapin::types::FieldTable; -use lapin::{ - BasicProperties, Channel, Connection, ConnectionProperties, ConnectionState, ExchangeKind, -}; -use ldk_server_json_models::events::Event; -use tokio::sync::Mutex; - -use crate::api::error::LdkServerError; -use crate::api::error::LdkServerErrorCode::InternalServerError; -use crate::io::events::event_publisher::EventPublisher; - -/// A RabbitMQ-based implementation of the EventPublisher trait. -pub struct RabbitMqEventPublisher { - /// The RabbitMQ connection, used for reconnection logic. - connection: Arc>>, - /// The RabbitMQ channel used for publishing events. - channel: Arc>>, - /// Configuration details, including connection string and exchange name. - config: RabbitMqConfig, -} - -/// Configuration for the RabbitMQ event publisher. -#[derive(Debug, Clone)] -pub struct RabbitMqConfig { - pub connection_string: String, - pub exchange_name: String, -} - -/// Delivery mode for persistent messages (written to disk). -const DELIVERY_MODE_PERSISTENT: u8 = 2; - -impl RabbitMqEventPublisher { - /// Creates a new RabbitMqEventPublisher instance. - pub fn new(config: RabbitMqConfig) -> Self { - Self { connection: Arc::new(Mutex::new(None)), channel: Arc::new(Mutex::new(None)), config } - } - - async fn connect(config: &RabbitMqConfig) -> Result<(Connection, Channel), LdkServerError> { - let conn = Connection::connect(&config.connection_string, ConnectionProperties::default()) - .await - .map_err(|e| { - LdkServerError::new( - InternalServerError, - format!("Failed to connect to RabbitMQ: {}", e), - ) - })?; - - let channel = conn.create_channel().await.map_err(|e| { - LdkServerError::new(InternalServerError, format!("Failed to create channel: {}", e)) - })?; - - channel.confirm_select(ConfirmSelectOptions::default()).await.map_err(|e| { - LdkServerError::new(InternalServerError, format!("Failed to enable confirms: {}", e)) - })?; - - channel - .exchange_declare( - &config.exchange_name, - ExchangeKind::Fanout, - ExchangeDeclareOptions { durable: true, ..Default::default() }, - FieldTable::default(), - ) - .await - .map_err(|e| { - LdkServerError::new( - InternalServerError, - format!("Failed to declare exchange: {}", e), - ) - })?; - - Ok((conn, channel)) - } - - async fn ensure_connected(&self) -> Result<(), LdkServerError> { - { - let connection = self.connection.lock().await; - if let Some(connection) = &*connection { - if connection.status().state() == ConnectionState::Connected { - return Ok(()); - } - } - } - - // Connection is not alive, attempt reconnecting. - let (connection, channel) = Self::connect(&self.config) - .await - .map_err(|e| LdkServerError::new(InternalServerError, e.to_string()))?; - *self.connection.lock().await = Some(connection); - *self.channel.lock().await = Some(channel); - Ok(()) - } -} - -#[async_trait] -impl EventPublisher for RabbitMqEventPublisher { - /// Publishes an event to RabbitMQ. - /// - /// The event is published to a fanout exchange with persistent delivery mode, - /// and the method waits for confirmation from RabbitMQ to ensure durability. - async fn publish(&self, event: Event) -> Result<(), LdkServerError> { - // Ensure connection is alive before proceeding - self.ensure_connected().await?; - - let channel_guard = self.channel.lock().await; - let channel = channel_guard.as_ref().ok_or_else(|| { - LdkServerError::new(InternalServerError, "Channel not initialized".to_string()) - })?; - - // Publish the event with persistent delivery mode - let confirm = channel - .basic_publish( - &self.config.exchange_name, - "", // Empty routing key should be used for fanout exchange, since it is ignored. - BasicPublishOptions::default(), - &serde_json::to_vec(&event).unwrap(), - BasicProperties::default().with_delivery_mode(DELIVERY_MODE_PERSISTENT), - ) - .await - .map_err(|e| { - LdkServerError::new( - InternalServerError, - format!("Failed to publish event, error: {}", e), - ) - })?; - - let confirmation = confirm.await.map_err(|e| { - LdkServerError::new(InternalServerError, format!("Failed to get confirmation: {}", e)) - })?; - - match confirmation { - lapin::publisher_confirm::Confirmation::Ack(_) => Ok(()), - lapin::publisher_confirm::Confirmation::Nack(_) => Err(LdkServerError::new( - InternalServerError, - "Message not acknowledged".to_string(), - )), - _ => { - Err(LdkServerError::new(InternalServerError, "Unexpected confirmation".to_string())) - }, - } - } -} - -#[cfg(test)] -#[cfg(feature = "integration-tests-events-rabbitmq")] -mod integration_tests_events_rabbitmq { - use std::io; - use std::time::Duration; - - use futures_util::stream::StreamExt; - use lapin::options::{ - BasicAckOptions, BasicConsumeOptions, QueueBindOptions, QueueDeclareOptions, - }; - use lapin::types::FieldTable; - use lapin::{Channel, Connection}; - use ldk_server_json_models::events::PaymentForwarded; - use tokio; - - use super::*; - #[tokio::test] - async fn test_publish_and_consume_event() { - let config = RabbitMqConfig { - connection_string: "amqp://guest:guest@localhost:5672/%2f".to_string(), - exchange_name: "test_exchange".to_string(), - }; - - let publisher = RabbitMqEventPublisher::new(config.clone()); - - let conn = Connection::connect(&config.connection_string, ConnectionProperties::default()) - .await - .expect("Failed make rabbitmq connection"); - let channel = conn.create_channel().await.expect("Failed to create rabbitmq channel"); - - let queue_name = "test_queue"; - setup_queue(&queue_name, &channel, &config).await; - - let event = Event::PaymentForwarded(PaymentForwarded { - forwarded_payment: ldk_server_json_models::types::ForwardedPayment { - prev_channel_id: [0u8; 32], - next_channel_id: [0u8; 32], - prev_user_channel_id: String::new(), - prev_node_id: [0u8; 33], - next_node_id: [0u8; 33], - next_user_channel_id: None, - total_fee_earned_msat: None, - skimmed_fee_msat: None, - claim_from_onchain_tx: false, - outbound_amount_forwarded_msat: None, - }, - }); - publisher.publish(event.clone()).await.expect("Failed to publish event"); - - consume_event(&queue_name, &channel, &event).await.expect("Failed to consume event"); - } - - async fn setup_queue(queue_name: &str, channel: &Channel, config: &RabbitMqConfig) { - channel - .queue_declare(queue_name, QueueDeclareOptions::default(), FieldTable::default()) - .await - .unwrap(); - channel - .exchange_declare( - &config.exchange_name, - ExchangeKind::Fanout, - ExchangeDeclareOptions { durable: true, ..Default::default() }, - FieldTable::default(), - ) - .await - .unwrap(); - - channel - .queue_bind( - queue_name, - &config.exchange_name, - "", - QueueBindOptions::default(), - FieldTable::default(), - ) - .await - .unwrap(); - } - - async fn consume_event( - queue_name: &str, channel: &Channel, expected_event: &Event, - ) -> io::Result<()> { - let mut consumer = channel - .basic_consume( - queue_name, - "test_consumer", - BasicConsumeOptions::default(), - FieldTable::default(), - ) - .await - .unwrap(); - let delivery = - tokio::time::timeout(Duration::from_secs(10), consumer.next()).await?.unwrap().unwrap(); - let received_event: Event = serde_json::from_slice(&delivery.data) - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; - assert_eq!(received_event, *expected_event, "Event mismatch"); - channel.basic_ack(delivery.delivery_tag, BasicAckOptions::default()).await.unwrap(); - Ok(()) - } -} diff --git a/ldk-server/src/io/events/sse.rs b/ldk-server/src/io/events/sse.rs new file mode 100644 index 00000000..b23ea248 --- /dev/null +++ b/ldk-server/src/io/events/sse.rs @@ -0,0 +1,76 @@ +// This file is Copyright its original authors, visible in version control +// history. +// +// This file is licensed under the Apache License, Version 2.0 or the MIT license +// , at your option. +// You may not use this file except in accordance with one or both of these +// licenses. + +use std::pin::Pin; +use std::task::{Context, Poll}; + +use hyper::body::{Bytes, Frame}; +use ldk_server_json_models::events::Event; +use log::warn; +use tokio::sync::{broadcast, mpsc}; + +use super::get_event_name; + +/// An HTTP body that streams Server-Sent Events from a broadcast channel. +/// +/// Uses an internal mpsc channel bridged from the broadcast receiver via a +/// spawned task, so that `poll_frame` can poll a `Receiver` directly. +pub(crate) struct SseBody { + receiver: mpsc::Receiver, +} + +impl SseBody { + pub(crate) fn new(broadcast_rx: broadcast::Receiver) -> Self { + let (tx, rx) = mpsc::channel(64); + tokio::spawn(bridge_broadcast_to_mpsc(broadcast_rx, tx)); + Self { receiver: rx } + } +} + +async fn bridge_broadcast_to_mpsc( + mut broadcast_rx: broadcast::Receiver, tx: mpsc::Sender, +) { + loop { + match broadcast_rx.recv().await { + Ok(event) => { + if tx.send(event).await.is_err() { + break; + } + }, + Err(broadcast::error::RecvError::Lagged(n)) => { + warn!("SSE subscriber lagged, skipped {n} events"); + }, + Err(broadcast::error::RecvError::Closed) => break, + } + } +} + +impl hyper::body::Body for SseBody { + type Data = Bytes; + type Error = std::convert::Infallible; + + fn poll_frame( + mut self: Pin<&mut Self>, cx: &mut Context<'_>, + ) -> Poll, Self::Error>>> { + match self.receiver.poll_recv(cx) { + Poll::Ready(Some(event)) => { + let encoded = format_sse_event(&event); + Poll::Ready(Some(Ok(Frame::data(Bytes::from(encoded))))) + }, + Poll::Ready(None) => Poll::Ready(None), + Poll::Pending => Poll::Pending, + } + } +} + +fn format_sse_event(event: &Event) -> String { + let event_name = get_event_name(event); + let data = serde_json::to_string(event).unwrap(); + format!("event: {event_name}\ndata: {data}\n\n") +} diff --git a/ldk-server/src/main.rs b/ldk-server/src/main.rs index a83e311d..d50590f6 100644 --- a/ldk-server/src/main.rs +++ b/ldk-server/src/main.rs @@ -33,12 +33,11 @@ use log::{debug, error, info}; use tokio::net::TcpListener; use tokio::select; use tokio::signal::unix::SignalKind; +use tokio::sync::broadcast; -use crate::io::events::event_publisher::EventPublisher; use crate::io::events::get_event_name; -#[cfg(feature = "events-rabbitmq")] -use crate::io::events::rabbitmq::{RabbitMqConfig, RabbitMqEventPublisher}; use crate::io::persist::paginated_kv_store::PaginatedKVStore; + use crate::io::persist::sqlite_store::SqliteStore; use crate::io::persist::{ FORWARDED_PAYMENTS_PERSISTENCE_PRIMARY_NAMESPACE, @@ -220,18 +219,7 @@ fn main() { }, }); - #[cfg(not(feature = "events-rabbitmq"))] - let event_publisher: Arc = - Arc::new(crate::io::events::event_publisher::NoopEventPublisher); - - #[cfg(feature = "events-rabbitmq")] - let event_publisher: Arc = { - let rabbitmq_config = RabbitMqConfig { - connection_string: config_file.rabbitmq_connection_string, - exchange_name: config_file.rabbitmq_exchange_name, - }; - Arc::new(RabbitMqEventPublisher::new(rabbitmq_config)) - }; + let (event_sender, _) = broadcast::channel::(256); info!("Starting up..."); match node.start() { @@ -324,8 +312,8 @@ fn main() { payment: payment_ref.clone(), }), &event_node, - Arc::clone(&event_publisher), - Arc::clone(&paginated_store)).await; + &event_sender, + Arc::clone(&paginated_store)); }, Event::PaymentSuccessful {payment_id, ..} => { let payment_id = payment_id.expect("PaymentId expected for ldk-server >=0.1"); @@ -335,8 +323,8 @@ fn main() { payment: payment_ref.clone(), }), &event_node, - Arc::clone(&event_publisher), - Arc::clone(&paginated_store)).await; + &event_sender, + Arc::clone(&paginated_store)); }, Event::PaymentFailed {payment_id, ..} => { let payment_id = payment_id.expect("PaymentId expected for ldk-server >=0.1"); @@ -346,8 +334,8 @@ fn main() { payment: payment_ref.clone(), }), &event_node, - Arc::clone(&event_publisher), - Arc::clone(&paginated_store)).await; + &event_sender, + Arc::clone(&paginated_store)); }, Event::PaymentClaimable {payment_id, ..} => { publish_event_and_upsert_payment(&payment_id, @@ -355,8 +343,8 @@ fn main() { payment: payment_ref.clone(), }), &event_node, - Arc::clone(&event_publisher), - Arc::clone(&paginated_store)).await; + &event_sender, + Arc::clone(&paginated_store)); }, Event::PaymentForwarded { prev_channel_id, @@ -396,17 +384,11 @@ fn main() { let forwarded_payment_creation_time = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time must be > 1970").as_secs() as i64; - match event_publisher.publish( + let _ = event_sender.send( events::Event::PaymentForwarded(events::PaymentForwarded { forwarded_payment: forwarded_payment.clone(), }), - ).await { - Ok(_) => {}, - Err(e) => { - error!("Failed to publish 'PaymentForwarded' event: {}", e); - continue; - } - }; + ); match paginated_store.write(FORWARDED_PAYMENTS_PERSISTENCE_PRIMARY_NAMESPACE,FORWARDED_PAYMENTS_PERSISTENCE_SECONDARY_NAMESPACE, &forwarded_payment_id.to_lower_hex_string(), @@ -433,7 +415,7 @@ fn main() { res = rest_svc_listener.accept() => { match res { Ok((stream, _)) => { - let node_service = NodeService::new(Arc::clone(&node), Arc::clone(&paginated_store), api_key.clone()); + let node_service = NodeService::new(Arc::clone(&node), Arc::clone(&paginated_store), api_key.clone(), event_sender.clone()); let acceptor = tls_acceptor.clone(); runtime.spawn(async move { match acceptor.accept(stream).await { @@ -472,22 +454,18 @@ fn main() { info!("Shutdown complete.."); } -async fn publish_event_and_upsert_payment( +fn publish_event_and_upsert_payment( payment_id: &PaymentId, payment_to_event: fn(&Payment) -> events::Event, event_node: &Node, - event_publisher: Arc, paginated_store: Arc, + event_sender: &broadcast::Sender, paginated_store: Arc, ) { if let Some(payment_details) = event_node.payment(payment_id) { let payment = payment_to_model(payment_details); let event = payment_to_event(&payment); let event_name = get_event_name(&event); - match event_publisher.publish(event).await { - Ok(_) => {}, - Err(e) => { - error!("Failed to publish '{event_name}' event, : {e}"); - return; - }, - }; + if event_sender.send(event).is_err() { + debug!("No active subscribers for '{event_name}' event"); + } upsert_payment_details(event_node, Arc::clone(&paginated_store), &payment); } else { diff --git a/ldk-server/src/service.rs b/ldk-server/src/service.rs index c5188ace..4ecc0579 100644 --- a/ldk-server/src/service.rs +++ b/ldk-server/src/service.rs @@ -27,11 +27,13 @@ use ldk_server_json_models::endpoints::{ GRAPH_GET_CHANNEL_PATH, GRAPH_GET_NODE_PATH, GRAPH_LIST_CHANNELS_PATH, GRAPH_LIST_NODES_PATH, LIST_CHANNELS_PATH, LIST_FORWARDED_PAYMENTS_PATH, LIST_PAYMENTS_PATH, LIST_PEERS_PATH, ONCHAIN_RECEIVE_PATH, ONCHAIN_SEND_PATH, OPEN_CHANNEL_PATH, SIGN_MESSAGE_PATH, SPLICE_IN_PATH, - SPLICE_OUT_PATH, SPONTANEOUS_SEND_PATH, UNIFIED_SEND_PATH, UPDATE_CHANNEL_CONFIG_PATH, - VERIFY_SIGNATURE_PATH, + SPLICE_OUT_PATH, SPONTANEOUS_SEND_PATH, SUBSCRIBE_PATH, UNIFIED_SEND_PATH, + UPDATE_CHANNEL_CONFIG_PATH, VERIFY_SIGNATURE_PATH, }; +use ldk_server_json_models::events::Event; use serde::de::DeserializeOwned; use serde::Serialize; +use tokio::sync::broadcast; use crate::api::bolt11_claim_for_hash::handle_bolt11_claim_for_hash_request; use crate::api::bolt11_fail_for_hash::handle_bolt11_fail_for_hash_request; @@ -70,6 +72,7 @@ use crate::api::spontaneous_send::handle_spontaneous_send_request; use crate::api::unified_send::handle_unified_send_request; use crate::api::update_channel_config::handle_update_channel_config_request; use crate::api::verify_signature::handle_verify_signature_request; +use crate::io::events::sse::SseBody; use crate::io::persist::paginated_kv_store::PaginatedKVStore; use crate::util::adapter::to_error_response; @@ -78,17 +81,19 @@ use crate::util::adapter::to_error_response; const MAX_BODY_SIZE: usize = 10 * 1024 * 1024; #[derive(Clone)] -pub struct NodeService { +pub(crate) struct NodeService { node: Arc, paginated_kv_store: Arc, api_key: String, + event_sender: broadcast::Sender, } impl NodeService { pub(crate) fn new( node: Arc, paginated_kv_store: Arc, api_key: String, + event_sender: broadcast::Sender, ) -> Self { - Self { node, paginated_kv_store, api_key } + Self { node, paginated_kv_store, api_key, event_sender } } } @@ -166,8 +171,11 @@ pub(crate) struct Context { pub(crate) paginated_kv_store: Arc, } +type BoxBody = http_body_util::combinators::BoxBody; +type ServiceResponse = Response; + impl Service> for NodeService { - type Response = Response>; + type Response = ServiceResponse; type Error = hyper::Error; type Future = Pin> + Send>>; @@ -176,15 +184,7 @@ impl Service> for NodeService { let auth_params = match extract_auth_params(&req) { Ok(params) => params, Err(e) => { - let (error_response, status_code) = to_error_response(e); - return Box::pin(async move { - Ok(Response::builder() - .status(status_code) - .header("Content-Type", "application/json") - .body(Full::new(Bytes::from(serde_json::to_vec(&error_response).unwrap()))) - // unwrap safety: body only errors when previous chained calls failed. - .unwrap()) - }); + return Box::pin(async move { Ok(error_to_response(e)) }); }, }; @@ -193,6 +193,7 @@ impl Service> for NodeService { paginated_kv_store: Arc::clone(&self.paginated_kv_store), }; let api_key = self.api_key.clone(); + let event_sender = self.event_sender.clone(); // Exclude '/' from path pattern matching. match &req.uri().path()[1..] { @@ -429,13 +430,25 @@ impl Service> for NodeService { api_key, handle_graph_get_node_request, )), + SUBSCRIBE_PATH => Box::pin(async move { + if let Err(e) = + validate_hmac_auth(auth_params.timestamp, &auth_params.hmac_hex, &[], &api_key) + { + return Ok(error_to_response(e)); + } + let sse_body = SseBody::new(event_sender.subscribe()); + Ok(Response::builder() + .header("Content-Type", "text/event-stream") + .header("Cache-Control", "no-cache") + .body(sse_body.boxed()) + .unwrap()) + }), path => { let error = format!("Unknown request: {}", path).into_bytes(); Box::pin(async { Ok(Response::builder() .status(StatusCode::BAD_REQUEST) - .body(Full::new(Bytes::from(error))) - // unwrap safety: body only errors when previous chained calls failed. + .body(Full::new(Bytes::from(error)).boxed()) .unwrap()) }) }, @@ -443,6 +456,15 @@ impl Service> for NodeService { } } +fn error_to_response(e: LdkServerError) -> ServiceResponse { + let (error_response, status_code) = to_error_response(e); + Response::builder() + .status(status_code) + .header("Content-Type", "application/json") + .body(Full::new(Bytes::from(serde_json::to_vec(&error_response).unwrap())).boxed()) + .unwrap() +} + async fn handle_request< T: DeserializeOwned, R: Serialize, @@ -450,36 +472,22 @@ async fn handle_request< >( context: Context, request: Request, auth_params: AuthParams, api_key: String, handler: F, -) -> Result<>>::Response, hyper::Error> { - // Limit the size of the request body to prevent abuse +) -> Result { let limited_body = Limited::new(request.into_body(), MAX_BODY_SIZE); let bytes = match limited_body.collect().await { Ok(collected) => collected.to_bytes(), Err(_) => { - let (error_response, status_code) = to_error_response(LdkServerError::new( + return Ok(error_to_response(LdkServerError::new( InvalidRequestError, "Request body too large or failed to read.", - )); - return Ok(Response::builder() - .status(status_code) - .header("Content-Type", "application/json") - .body(Full::new(Bytes::from(serde_json::to_vec(&error_response).unwrap()))) - // unwrap safety: body only errors when previous chained calls failed. - .unwrap()); + ))); }, }; - // Validate HMAC authentication with the request body if let Err(e) = validate_hmac_auth(auth_params.timestamp, &auth_params.hmac_hex, &bytes, &api_key) { - let (error_response, status_code) = to_error_response(e); - return Ok(Response::builder() - .status(status_code) - .header("Content-Type", "application/json") - .body(Full::new(Bytes::from(serde_json::to_vec(&error_response).unwrap()))) - // unwrap safety: body only errors when previous chained calls failed. - .unwrap()); + return Ok(error_to_response(e)); } let decode_bytes = if bytes.is_empty() { &b"{}"[..] } else { &bytes[..] }; @@ -487,28 +495,12 @@ async fn handle_request< Ok(request) => match handler(context, request) { Ok(response) => Ok(Response::builder() .header("Content-Type", "application/json") - .body(Full::new(Bytes::from(serde_json::to_vec(&response).unwrap()))) - // unwrap safety: body only errors when previous chained calls failed. + .body(Full::new(Bytes::from(serde_json::to_vec(&response).unwrap())).boxed()) .unwrap()), - Err(e) => { - let (error_response, status_code) = to_error_response(e); - Ok(Response::builder() - .status(status_code) - .header("Content-Type", "application/json") - .body(Full::new(Bytes::from(serde_json::to_vec(&error_response).unwrap()))) - // unwrap safety: body only errors when previous chained calls failed. - .unwrap()) - }, + Err(e) => Ok(error_to_response(e)), }, Err(_) => { - let (error_response, status_code) = - to_error_response(LdkServerError::new(InvalidRequestError, "Malformed request.")); - Ok(Response::builder() - .status(status_code) - .header("Content-Type", "application/json") - .body(Full::new(Bytes::from(serde_json::to_vec(&error_response).unwrap()))) - // unwrap safety: body only errors when previous chained calls failed. - .unwrap()) + Ok(error_to_response(LdkServerError::new(InvalidRequestError, "Malformed request."))) }, } } diff --git a/ldk-server/src/util/config.rs b/ldk-server/src/util/config.rs index c950987c..18aa841c 100644 --- a/ldk-server/src/util/config.rs +++ b/ldk-server/src/util/config.rs @@ -48,10 +48,6 @@ pub struct Config { pub storage_dir_path: Option, pub chain_source: ChainSource, pub rgs_server_url: Option, - #[cfg_attr(not(feature = "events-rabbitmq"), allow(dead_code))] - pub rabbitmq_connection_string: String, - #[cfg_attr(not(feature = "events-rabbitmq"), allow(dead_code))] - pub rabbitmq_exchange_name: String, pub lsps2_client_config: Option, #[cfg_attr(not(feature = "experimental-lsps2-support"), allow(dead_code))] pub lsps2_service_config: Option, @@ -97,8 +93,6 @@ struct ConfigBuilder { bitcoind_rpc_user: Option, bitcoind_rpc_password: Option, rgs_server_url: Option, - rabbitmq_connection_string: Option, - rabbitmq_exchange_name: Option, lsps2: Option, log_level: Option, log_file_path: Option, @@ -146,11 +140,6 @@ impl ConfigBuilder { self.log_file_path = log.file.or(self.log_file_path.clone()); } - if let Some(rabbitmq) = toml.rabbitmq { - self.rabbitmq_connection_string = Some(rabbitmq.connection_string); - self.rabbitmq_exchange_name = Some(rabbitmq.exchange_name); - } - if let Some(liquidity) = toml.liquidity { self.lsps2 = Some(liquidity); } @@ -313,30 +302,6 @@ impl ConfigBuilder { .transpose()? .unwrap_or(LevelFilter::Debug); - #[cfg(feature = "events-rabbitmq")] - let (rabbitmq_connection_string, rabbitmq_exchange_name) = { - let connection_string = self.rabbitmq_connection_string.ok_or_else(|| io::Error::new( - io::ErrorKind::InvalidInput, - "Both `rabbitmq.connection_string` and `rabbitmq.exchange_name` must be configured if enabling `events-rabbitmq` feature." - ))?; - let exchange_name = self.rabbitmq_exchange_name.ok_or_else(|| io::Error::new( - io::ErrorKind::InvalidInput, - "Both `rabbitmq.connection_string` and `rabbitmq.exchange_name` must be configured if enabling `events-rabbitmq` feature." - ))?; - - if connection_string.is_empty() || exchange_name.is_empty() { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - "Both `rabbitmq.connection_string` and `rabbitmq.exchange_name` must be configured if enabling `events-rabbitmq` feature." - )); - } - - (connection_string, exchange_name) - }; - - #[cfg(not(feature = "events-rabbitmq"))] - let (rabbitmq_connection_string, rabbitmq_exchange_name) = (String::new(), String::new()); - let lsps2_client_config = self .lsps2 .as_ref() @@ -372,8 +337,6 @@ impl ConfigBuilder { storage_dir_path: self.storage_dir_path, chain_source, rgs_server_url: self.rgs_server_url, - rabbitmq_connection_string, - rabbitmq_exchange_name, lsps2_client_config, lsps2_service_config, log_level, @@ -391,7 +354,6 @@ pub struct TomlConfig { bitcoind: Option, electrum: Option, esplora: Option, - rabbitmq: Option, liquidity: Option, log: Option, tls: Option, @@ -441,12 +403,6 @@ struct LogConfig { file: Option, } -#[derive(Deserialize, Serialize)] -struct RabbitmqConfig { - connection_string: String, - exchange_name: String, -} - #[derive(Deserialize, Serialize)] struct TomlTlsConfig { cert_path: Option, @@ -711,9 +667,6 @@ mod tests { rpc_user = "bitcoind-testuser" rpc_password = "bitcoind-testpassword" - [rabbitmq] - connection_string = "rabbitmq_connection_string" - exchange_name = "rabbitmq_exchange_name" [liquidity.lsps2_client] node_pubkey = "0217890e3aad8d35bc054f43acc00084b25229ecff0ab68debd82883ad65ee8266" @@ -786,13 +739,6 @@ mod tests { let alias = "LDK Server"; - #[cfg(feature = "events-rabbitmq")] - let (expected_rabbit_conn, expected_rabbit_exchange) = - ("rabbitmq_connection_string".to_string(), "rabbitmq_exchange_name".to_string()); - - #[cfg(not(feature = "events-rabbitmq"))] - let (expected_rabbit_conn, expected_rabbit_exchange) = (String::new(), String::new()); - let expected = Config { listening_addrs: Some(vec![SocketAddress::from_str("localhost:3001").unwrap()]), announcement_addrs: Some(vec![SocketAddress::from_str("54.3.7.81:3001").unwrap()]), @@ -812,8 +758,6 @@ mod tests { rpc_password: "bitcoind-testpassword".to_string(), }, rgs_server_url: Some("https://rapidsync.lightningdevkit.org/snapshot/v2/".to_string()), - rabbitmq_connection_string: expected_rabbit_conn, - rabbitmq_exchange_name: expected_rabbit_exchange, lsps2_client_config: Some(LSPSClientConfig { node_id: PublicKey::from_str( "0217890e3aad8d35bc054f43acc00084b25229ecff0ab68debd82883ad65ee8266", @@ -847,8 +791,6 @@ mod tests { assert_eq!(config.storage_dir_path, expected.storage_dir_path); assert_eq!(config.chain_source, expected.chain_source); assert_eq!(config.rgs_server_url, expected.rgs_server_url); - assert_eq!(config.rabbitmq_connection_string, expected.rabbitmq_connection_string); - assert_eq!(config.rabbitmq_exchange_name, expected.rabbitmq_exchange_name); assert_eq!(config.lsps2_client_config, expected.lsps2_client_config); #[cfg(feature = "experimental-lsps2-support")] assert_eq!(config.lsps2_service_config.is_some(), expected.lsps2_service_config.is_some()); @@ -882,9 +824,7 @@ mod tests { [electrum] server_url = "ssl://electrum.blockstream.info:50002" - [rabbitmq] - connection_string = "rabbitmq_connection_string" - exchange_name = "rabbitmq_exchange_name" + [liquidity.lsps2_client] node_pubkey = "0217890e3aad8d35bc054f43acc00084b25229ecff0ab68debd82883ad65ee8266" @@ -939,9 +879,7 @@ mod tests { rpc_user = "bitcoind-testuser" rpc_password = "bitcoind-testpassword" - [rabbitmq] - connection_string = "rabbitmq_connection_string" - exchange_name = "rabbitmq_exchange_name" + [liquidity.lsps2_client] node_pubkey = "0217890e3aad8d35bc054f43acc00084b25229ecff0ab68debd82883ad65ee8266" @@ -1003,9 +941,7 @@ mod tests { [esplora] server_url = "https://mempool.space/api" - [rabbitmq] - connection_string = "rabbitmq_connection_string" - exchange_name = "rabbitmq_exchange_name" + [liquidity.lsps2_client] node_pubkey = "0217890e3aad8d35bc054f43acc00084b25229ecff0ab68debd82883ad65ee8266" @@ -1048,9 +984,7 @@ mod tests { rpc_user = "bitcoind-testuser" rpc_password = "bitcoind-testpassword" - [rabbitmq] - connection_string = "rabbitmq_connection_string" - exchange_name = "rabbitmq_exchange_name" + [liquidity.lsps2_service] advertise_service = false @@ -1098,14 +1032,6 @@ mod tests { ); } - #[cfg(feature = "events-rabbitmq")] - { - validate_missing!( - "[rabbitmq]", - "Both `rabbitmq.connection_string` and `rabbitmq.exchange_name` must be configured if enabling `events-rabbitmq` feature." - ); - } - validate_missing!("rpc_password", missing_field_msg("bitcoind_rpc_password")); validate_missing!("rpc_user", missing_field_msg("bitcoind_rpc_user")); validate_missing!("rpc_address", missing_field_msg("bitcoind_rpc_address")); @@ -1123,7 +1049,6 @@ mod tests { #[test] #[cfg(not(feature = "experimental-lsps2-support"))] - #[cfg(not(feature = "events-rabbitmq"))] fn test_config_from_args_config() { let args_config = default_args_config(); let config = load_config(&args_config).unwrap(); @@ -1154,8 +1079,6 @@ mod tests { rpc_password: args_config.bitcoind_rpc_password.unwrap(), }, rgs_server_url: None, - rabbitmq_connection_string: String::new(), - rabbitmq_exchange_name: String::new(), lsps2_client_config: None, lsps2_service_config: None, log_level: LevelFilter::Trace, @@ -1170,15 +1093,12 @@ mod tests { assert_eq!(config.storage_dir_path, expected.storage_dir_path); assert_eq!(config.chain_source, expected.chain_source); assert_eq!(config.rgs_server_url, expected.rgs_server_url); - assert_eq!(config.rabbitmq_connection_string, expected.rabbitmq_connection_string); - assert_eq!(config.rabbitmq_exchange_name, expected.rabbitmq_exchange_name); assert!(config.lsps2_service_config.is_none()); assert_eq!(config.pathfinding_scores_source_url, expected.pathfinding_scores_source_url); } #[test] #[cfg(not(feature = "experimental-lsps2-support"))] - #[cfg(not(feature = "events-rabbitmq"))] fn test_config_missing_fields_in_args_config() { macro_rules! validate_missing { ($field:ident, $err_msg:expr) => { @@ -1209,13 +1129,6 @@ mod tests { args_config.config_file = Some(storage_path.join(config_file_name).to_string_lossy().to_string()); - #[cfg(feature = "events-rabbitmq")] - let (expected_rabbit_conn, expected_rabbit_exchange) = - ("rabbitmq_connection_string".to_string(), "rabbitmq_exchange_name".to_string()); - - #[cfg(not(feature = "events-rabbitmq"))] - let (expected_rabbit_conn, expected_rabbit_exchange) = (String::new(), String::new()); - let (host, port) = parse_host_port(args_config.bitcoind_rpc_address.clone().unwrap().as_str()).unwrap(); @@ -1248,8 +1161,6 @@ mod tests { rpc_password: args_config.bitcoind_rpc_password.unwrap(), }, rgs_server_url: Some("https://rapidsync.lightningdevkit.org/snapshot/v2/".to_string()), - rabbitmq_connection_string: expected_rabbit_conn, - rabbitmq_exchange_name: expected_rabbit_exchange, lsps2_client_config: Some(LSPSClientConfig { node_id: PublicKey::from_str( "0217890e3aad8d35bc054f43acc00084b25229ecff0ab68debd82883ad65ee8266", @@ -1282,24 +1193,12 @@ mod tests { assert_eq!(config.storage_dir_path, expected.storage_dir_path); assert_eq!(config.chain_source, expected.chain_source); assert_eq!(config.rgs_server_url, expected.rgs_server_url); - assert_eq!(config.rabbitmq_connection_string, expected.rabbitmq_connection_string); - assert_eq!(config.rabbitmq_exchange_name, expected.rabbitmq_exchange_name); assert_eq!(config.lsps2_client_config, expected.lsps2_client_config); #[cfg(feature = "experimental-lsps2-support")] assert_eq!(config.lsps2_service_config.is_some(), expected.lsps2_service_config.is_some()); assert_eq!(config.pathfinding_scores_source_url, expected.pathfinding_scores_source_url); } - #[test] - #[cfg(feature = "events-rabbitmq")] - fn test_error_if_rabbitmq_feature_without_valid_config_file() { - let args_config = empty_args_config(); - let result = load_config(&args_config); - assert!(result.is_err()); - let err = result.unwrap_err(); - assert_eq!(err.kind(), io::ErrorKind::InvalidInput); - } - #[test] #[cfg(feature = "experimental-lsps2-support")] fn test_error_if_lsps2_feature_without_valid_config_file() { From dd3065a94dd3f2da8f833c71c3698253b6125e6a Mon Sep 17 00:00:00 2001 From: Joost Jager Date: Tue, 24 Mar 2026 09:25:55 +0100 Subject: [PATCH 3/4] Add CORS support and web UI prototype Add CORS headers to all server responses and handle OPTIONS preflight requests, enabling browser-based clients to connect directly to ldk-server. Include a single-file web UI (index.html) that demonstrates connecting to the JSON API and SSE event stream from the browser using @microsoft/fetch-event-source. The UI shows node info, lists peers with keysend buttons, supports BOLT11 payments, and displays the live event stream. AI tools were used in preparing this commit. --- index.html | 264 ++++++++++++++++++++++++++++++++++++++ ldk-server/src/service.rs | 30 +++++ 2 files changed, 294 insertions(+) create mode 100644 index.html diff --git a/index.html b/index.html new file mode 100644 index 00000000..6be48881 --- /dev/null +++ b/index.html @@ -0,0 +1,264 @@ + + + + + LDK Server + + + +

LDK Server

+ +
+

Connection

+ + +

+ + +

+ + +

+ + +
+ +
+

Peers

+ +
+

+  
+ +
+

Send Payment

+ + +

+  
+ +
+

Events

+ +

+  
+ + + + diff --git a/ldk-server/src/service.rs b/ldk-server/src/service.rs index 4ecc0579..2daeaa60 100644 --- a/ldk-server/src/service.rs +++ b/ldk-server/src/service.rs @@ -180,6 +180,24 @@ impl Service> for NodeService { type Future = Pin> + Send>>; fn call(&self, req: Request) -> Self::Future { + // Handle CORS preflight + if req.method() == hyper::Method::OPTIONS { + return Box::pin(async { + Ok(with_cors_headers( + Response::builder().status(StatusCode::NO_CONTENT).body(empty_body()).unwrap(), + )) + }); + } + + let inner = self.call_inner(req); + Box::pin(async move { inner.await.map(with_cors_headers) }) + } +} + +impl NodeService { + fn call_inner( + &self, req: Request, + ) -> Pin> + Send>> { // Extract auth params from headers (validation happens after body is read) let auth_params = match extract_auth_params(&req) { Ok(params) => params, @@ -465,6 +483,18 @@ fn error_to_response(e: LdkServerError) -> ServiceResponse { .unwrap() } +fn empty_body() -> BoxBody { + Full::new(Bytes::new()).boxed() +} + +fn with_cors_headers(mut response: ServiceResponse) -> ServiceResponse { + let headers = response.headers_mut(); + headers.insert("Access-Control-Allow-Origin", "*".parse().unwrap()); + headers.insert("Access-Control-Allow-Methods", "GET, POST, OPTIONS".parse().unwrap()); + headers.insert("Access-Control-Allow-Headers", "Content-Type, X-Auth".parse().unwrap()); + response +} + async fn handle_request< T: DeserializeOwned, R: Serialize, From 7c3884628162ae52c61c7e9c397a6e5278c6ac1a Mon Sep 17 00:00:00 2001 From: Joost Jager Date: Wed, 25 Mar 2026 11:52:05 +0100 Subject: [PATCH 4/4] Add OpenAPI spec generation via utoipa Derive ToSchema on all request/response and domain types in ldk-server-json-models, with #[schema(value_type = String)] overrides for hex-serialized fields. Define all 36 API path operations in a new openapi module using utoipa's path macro on stub functions, grouped by tags (Node, Onchain, Bolt11, Bolt12, Channels, Payments, Peers, Send, Graph, Crypto, Events). The spec is served at /openapi.json without authentication, cached via LazyLock. AI tools were used in preparing this commit. --- Cargo.lock | 27 ++ ldk-server-json-models/Cargo.toml | 1 + ldk-server-json-models/src/api.rs | 171 ++++---- ldk-server-json-models/src/error.rs | 7 +- ldk-server-json-models/src/events.rs | 13 +- ldk-server-json-models/src/types.rs | 140 +++++-- ldk-server/Cargo.toml | 1 + ldk-server/src/main.rs | 1 + ldk-server/src/openapi.rs | 580 +++++++++++++++++++++++++++ ldk-server/src/service.rs | 13 + 10 files changed, 834 insertions(+), 120 deletions(-) create mode 100644 ldk-server/src/openapi.rs diff --git a/Cargo.lock b/Cargo.lock index 8b6c3fe4..1465cc47 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1077,6 +1077,8 @@ checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" dependencies = [ "equivalent", "hashbrown 0.16.0", + "serde", + "serde_core", ] [[package]] @@ -1191,6 +1193,7 @@ dependencies = [ "tokio", "tokio-rustls 0.26.4", "toml", + "utoipa", ] [[package]] @@ -1226,6 +1229,7 @@ name = "ldk-server-json-models" version = "0.1.0" dependencies = [ "serde", + "utoipa", ] [[package]] @@ -2483,6 +2487,29 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +[[package]] +name = "utoipa" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fcc29c80c21c31608227e0912b2d7fddba57ad76b606890627ba8ee7964e993" +dependencies = [ + "indexmap", + "serde", + "serde_json", + "utoipa-gen", +] + +[[package]] +name = "utoipa-gen" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d79d08d92ab8af4c5e8a6da20c47ae3f61a0f1dabc1997cdf2d082b757ca08b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.108", +] + [[package]] name = "vcpkg" version = "0.2.15" diff --git a/ldk-server-json-models/Cargo.toml b/ldk-server-json-models/Cargo.toml index 928107a0..0805a357 100644 --- a/ldk-server-json-models/Cargo.toml +++ b/ldk-server-json-models/Cargo.toml @@ -5,3 +5,4 @@ edition = "2021" [dependencies] serde = { version = "1.0", features = ["derive"] } +utoipa = { version = "5", features = ["preserve_order"] } diff --git a/ldk-server-json-models/src/api.rs b/ldk-server-json-models/src/api.rs index 1a97eec4..a9cb598d 100644 --- a/ldk-server-json-models/src/api.rs +++ b/ldk-server-json-models/src/api.rs @@ -8,18 +8,20 @@ // licenses. use serde::{Deserialize, Serialize}; +use utoipa::ToSchema; /// Retrieve the latest node info like `node_id`, `current_best_block` etc. /// See more: /// - /// - -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct GetNodeInfoRequest {} /// The response `content` for the `GetNodeInfo` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct GetNodeInfoResponse { /// The hex-encoded `node-id` or public key for our own lightning node. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_33")] pub node_id: [u8; 33], /// The best block to which our Lightning wallet is currently synced. @@ -67,17 +69,17 @@ pub struct GetNodeInfoResponse { } /// Retrieve a new on-chain funding address. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct OnchainReceiveRequest {} /// The response `content` for the `OnchainReceive` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`.. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct OnchainReceiveResponse { /// A Bitcoin on-chain address. pub address: String, } /// Send an on-chain payment to the given address. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct OnchainSendRequest { /// The address to send coins to. pub address: String, @@ -100,9 +102,10 @@ pub struct OnchainSendRequest { } /// The response `content` for the `OnchainSend` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct OnchainSendResponse { /// The transaction ID of the broadcasted transaction. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_32")] pub txid: [u8; 32], } @@ -112,7 +115,7 @@ pub struct OnchainSendResponse { /// See more: /// - /// - -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct Bolt11ReceiveRequest { /// The amount in millisatoshi to send. If unset, a "zero-amount" or variable-amount invoice is returned. pub amount_msat: Option, @@ -124,16 +127,18 @@ pub struct Bolt11ReceiveRequest { } /// The response `content` for the `Bolt11Receive` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct Bolt11ReceiveResponse { /// An invoice for a payment within the Lightning Network. /// With the details of the invoice, the sender has all the data necessary to send a payment /// to the recipient. pub invoice: String, /// The hex-encoded 32-byte payment hash. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_32")] pub payment_hash: [u8; 32], /// The hex-encoded 32-byte payment secret. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_32")] pub payment_secret: [u8; 32], } @@ -144,7 +149,7 @@ pub struct Bolt11ReceiveResponse { /// See more: /// - /// - -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct Bolt11ReceiveForHashRequest { /// The amount in millisatoshi to receive. If unset, a "zero-amount" or variable-amount invoice is returned. pub amount_msat: Option, @@ -154,12 +159,13 @@ pub struct Bolt11ReceiveForHashRequest { /// Invoice expiry time in seconds. pub expiry_secs: u32, /// The hex-encoded 32-byte payment hash to use for the invoice. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_32")] pub payment_hash: [u8; 32], } /// The response `content` for the `Bolt11ReceiveForHash` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct Bolt11ReceiveForHashResponse { /// An invoice for a payment within the Lightning Network. /// With the details of the invoice, the sender has all the data necessary to send a payment @@ -169,40 +175,43 @@ pub struct Bolt11ReceiveForHashResponse { /// Manually claim a payment for a given payment hash with the corresponding preimage. /// This should be used to claim payments created via `Bolt11ReceiveForHash`. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct Bolt11ClaimForHashRequest { /// The hex-encoded 32-byte payment hash. /// If provided, it will be used to verify that the preimage matches. + #[schema(value_type = Option)] #[serde(default, with = "crate::serde_utils::opt_hex_32")] pub payment_hash: Option<[u8; 32]>, /// The amount in millisatoshi that is claimable. /// If not provided, skips amount verification. pub claimable_amount_msat: Option, /// The hex-encoded 32-byte payment preimage. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_32")] pub preimage: [u8; 32], } /// The response `content` for the `Bolt11ClaimForHash` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct Bolt11ClaimForHashResponse {} /// Manually fail a payment for a given payment hash. /// This should be used to reject payments created via `Bolt11ReceiveForHash`. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct Bolt11FailForHashRequest { /// The hex-encoded 32-byte payment hash. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_32")] pub payment_hash: [u8; 32], } /// The response `content` for the `Bolt11FailForHash` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct Bolt11FailForHashResponse {} /// Return a BOLT11 payable invoice that can be used to request and receive a payment via an /// LSPS2 just-in-time channel. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct Bolt11ReceiveViaJitChannelRequest { /// The amount in millisatoshi to request. pub amount_msat: u64, @@ -216,7 +225,7 @@ pub struct Bolt11ReceiveViaJitChannelRequest { } /// The response `content` for the `Bolt11ReceiveViaJitChannel` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct Bolt11ReceiveViaJitChannelResponse { /// An invoice for a payment within the Lightning Network. pub invoice: String, @@ -224,7 +233,7 @@ pub struct Bolt11ReceiveViaJitChannelResponse { /// Return a variable-amount BOLT11 invoice that can be used to receive a payment via an LSPS2 /// just-in-time channel. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct Bolt11ReceiveVariableAmountViaJitChannelRequest { /// An optional description to attach along with the invoice. /// Will be set in the description field of the encoded payment request. @@ -237,14 +246,14 @@ pub struct Bolt11ReceiveVariableAmountViaJitChannelRequest { } /// The response `content` for the `Bolt11ReceiveVariableAmountViaJitChannel` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct Bolt11ReceiveVariableAmountViaJitChannelResponse { /// An invoice for a payment within the Lightning Network. pub invoice: String, } /// Send a payment for a BOLT11 invoice. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct Bolt11SendRequest { /// An invoice for a payment within the Lightning Network. pub invoice: String, @@ -257,9 +266,10 @@ pub struct Bolt11SendRequest { } /// The response `content` for the `Bolt11Send` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct Bolt11SendResponse { /// An identifier used to uniquely identify a payment in hex-encoded form. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_32")] pub payment_id: [u8; 32], } @@ -268,7 +278,7 @@ pub struct Bolt11SendResponse { /// See more: /// - /// - -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct Bolt12ReceiveRequest { /// An optional description to attach along with the offer. /// Will be set in the description field of the encoded offer. @@ -282,13 +292,14 @@ pub struct Bolt12ReceiveRequest { } /// The response `content` for the `Bolt12Receive` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct Bolt12ReceiveResponse { /// An offer for a payment within the Lightning Network. /// With the details of the offer, the sender has all the data necessary to send a payment /// to the recipient. pub offer: String, /// The hex-encoded offer id. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_32")] pub offer_id: [u8; 32], } @@ -296,7 +307,7 @@ pub struct Bolt12ReceiveResponse { /// See more: /// - /// - -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct Bolt12SendRequest { /// An offer for a payment within the Lightning Network. pub offer: String, @@ -313,19 +324,21 @@ pub struct Bolt12SendRequest { } /// The response `content` for the `Bolt12Send` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct Bolt12SendResponse { /// An identifier used to uniquely identify a payment in hex-encoded form. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_32")] pub payment_id: [u8; 32], } /// Send a spontaneous payment, also known as "keysend", to a node. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct SpontaneousSendRequest { /// The amount in millisatoshis to send. pub amount_msat: u64, /// The hex-encoded public key of the node to send the payment to. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_33")] pub node_id: [u8; 33], /// Configuration options for payment routing and pathfinding. @@ -333,17 +346,19 @@ pub struct SpontaneousSendRequest { } /// The response `content` for the `SpontaneousSend` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct SpontaneousSendResponse { /// An identifier used to uniquely identify a payment in hex-encoded form. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_32")] pub payment_id: [u8; 32], } /// Creates a new outbound channel to the given remote node. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct OpenChannelRequest { /// The hex-encoded public key of the node to open a channel with. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_33")] pub node_pubkey: [u8; 33], /// An address which can be used to connect to a remote peer. @@ -360,18 +375,19 @@ pub struct OpenChannelRequest { } /// The response `content` for the `OpenChannel` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct OpenChannelResponse { /// The local channel id of the created channel that user can use to refer to channel. pub user_channel_id: String, } /// Increases the channel balance by the given amount. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct SpliceInRequest { /// The local `user_channel_id` of the channel. pub user_channel_id: String, /// The hex-encoded public key of the channel's counterparty node. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_33")] pub counterparty_node_id: [u8; 33], /// The amount of sats to splice into the channel. @@ -379,15 +395,16 @@ pub struct SpliceInRequest { } /// The response `content` for the `SpliceIn` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct SpliceInResponse {} /// Decreases the channel balance by the given amount. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct SpliceOutRequest { /// The local `user_channel_id` of this channel. pub user_channel_id: String, /// The hex-encoded public key of the channel's counterparty node. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_33")] pub counterparty_node_id: [u8; 33], /// A Bitcoin on-chain address to send the spliced-out funds. @@ -399,18 +416,19 @@ pub struct SpliceOutRequest { } /// The response `content` for the `SpliceOut` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct SpliceOutResponse { /// The Bitcoin on-chain address where the funds will be sent. pub address: String, } /// Update the config for a previously opened channel. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct UpdateChannelConfigRequest { /// The local `user_channel_id` of this channel. pub user_channel_id: String, /// The hex-encoded public key of the counterparty node to update channel config with. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_33")] pub counterparty_node_id: [u8; 33], /// The updated channel configuration settings for a channel. @@ -418,29 +436,31 @@ pub struct UpdateChannelConfigRequest { } /// The response `content` for the `UpdateChannelConfig` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct UpdateChannelConfigResponse {} /// Closes the channel specified by given request. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct CloseChannelRequest { /// The local `user_channel_id` of this channel. pub user_channel_id: String, /// The hex-encoded public key of the node to close a channel with. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_33")] pub counterparty_node_id: [u8; 33], } /// The response `content` for the `CloseChannel` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct CloseChannelResponse {} /// Force closes the channel specified by given request. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct ForceCloseChannelRequest { /// The local `user_channel_id` of this channel. pub user_channel_id: String, /// The hex-encoded public key of the node to close a channel with. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_33")] pub counterparty_node_id: [u8; 33], /// The reason for force-closing. @@ -448,30 +468,31 @@ pub struct ForceCloseChannelRequest { } /// The response `content` for the `ForceCloseChannel` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct ForceCloseChannelResponse {} /// Returns a list of known channels. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct ListChannelsRequest {} /// The response `content` for the `ListChannels` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct ListChannelsResponse { /// List of channels. pub channels: Vec, } /// Returns payment details for a given payment_id. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct GetPaymentDetailsRequest { /// An identifier used to uniquely identify a payment in hex-encoded form. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_32")] pub payment_id: [u8; 32], } /// The response `content` for the `GetPaymentDetails` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct GetPaymentDetailsResponse { /// Represents a payment. /// Will be `None` if payment doesn't exist. @@ -479,7 +500,7 @@ pub struct GetPaymentDetailsResponse { } /// Retrieves list of all payments. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct ListPaymentsRequest { /// `page_token` is a pagination token. /// @@ -491,7 +512,7 @@ pub struct ListPaymentsRequest { } /// The response `content` for the `ListPayments` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct ListPaymentsResponse { /// List of payments. pub payments: Vec, @@ -512,7 +533,7 @@ pub struct ListPaymentsResponse { } /// Retrieves list of all forwarded payments. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct ListForwardedPaymentsRequest { /// `page_token` is a pagination token. /// @@ -524,7 +545,7 @@ pub struct ListForwardedPaymentsRequest { } /// The response `content` for the `ListForwardedPayments` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct ListForwardedPaymentsResponse { /// List of forwarded payments. pub forwarded_payments: Vec, @@ -545,58 +566,62 @@ pub struct ListForwardedPaymentsResponse { } /// Sign a message with the node's secret key. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct SignMessageRequest { /// The message to sign, as raw bytes. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::bytes_hex")] pub message: Vec, } /// The response `content` for the `SignMessage` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct SignMessageResponse { /// The signature of the message, as a zbase32-encoded string. pub signature: String, } /// Verify a signature against a message and public key. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct VerifySignatureRequest { /// The message that was signed, as raw bytes. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::bytes_hex")] pub message: Vec, /// The signature to verify, as a zbase32-encoded string. pub signature: String, /// The hex-encoded public key of the signer. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_33")] pub public_key: [u8; 33], } /// The response `content` for the `VerifySignature` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct VerifySignatureResponse { /// Whether the signature is valid. pub valid: bool, } /// Export the pathfinding scores used by the router. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct ExportPathfindingScoresRequest {} /// The response `content` for the `ExportPathfindingScores` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct ExportPathfindingScoresResponse { /// The serialized pathfinding scores data. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::bytes_hex")] pub scores: Vec, } /// Retrieves an overview of all known balances. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct GetBalancesRequest {} /// The response `content` for the `GetBalances` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct GetBalancesResponse { /// The total balance of our on-chain wallet. pub total_onchain_balance_sats: u64, @@ -632,9 +657,10 @@ pub struct GetBalancesResponse { } /// Connect to a peer on the Lightning Network. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct ConnectPeerRequest { /// The hex-encoded public key of the node to connect to. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_33")] pub node_pubkey: [u8; 33], /// An address which can be used to connect to a remote peer. @@ -646,63 +672,64 @@ pub struct ConnectPeerRequest { } /// The response `content` for the `ConnectPeer` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct ConnectPeerResponse {} /// Disconnect from a peer and remove it from the peer store. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct DisconnectPeerRequest { /// The hex-encoded public key of the node to disconnect from. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_33")] pub node_pubkey: [u8; 33], } /// The response `content` for the `DisconnectPeer` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct DisconnectPeerResponse {} /// Returns a list of peers. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct ListPeersRequest {} /// The response `content` for the `ListPeers` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct ListPeersResponse { /// List of peers. pub peers: Vec, } /// Returns a list of all known short channel IDs in the network graph. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct GraphListChannelsRequest {} /// The response `content` for the `GraphListChannels` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct GraphListChannelsResponse { /// List of short channel IDs known to the network graph. pub short_channel_ids: Vec, } /// Returns information on a channel with the given short channel ID from the network graph. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct GraphGetChannelRequest { /// The short channel ID to look up. pub short_channel_id: u64, } /// The response `content` for the `GraphGetChannel` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct GraphGetChannelResponse { /// The channel information. pub channel: Option, } /// Returns a list of all known node IDs in the network graph. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct GraphListNodesRequest {} /// The response `content` for the `GraphListNodes` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct GraphListNodesResponse { /// List of hex-encoded node IDs known to the network graph. pub node_ids: Vec, @@ -713,7 +740,7 @@ pub struct GraphListNodesResponse { /// has an offer and/or invoice, it will try to pay the offer first followed by the invoice. /// If they both fail, the on-chain payment will be paid. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct UnifiedSendRequest { /// A BIP 21 URI or BIP 353 Human-Readable Name to pay. pub uri: String, @@ -724,16 +751,17 @@ pub struct UnifiedSendRequest { } /// The response `content` for the `UnifiedSend` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct UnifiedSendResponse { #[serde(flatten)] + #[schema(inline)] pub payment_result: Option, } /// The payment result of a unified send operation. /// /// Note: Variants use `String` instead of `[u8; 32]` because `#[serde(with)]` /// is not supported on enum tuple variants. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] #[serde(rename_all = "snake_case")] pub enum UnifiedSendPaymentResult { /// An on-chain payment was made. Contains the hex-encoded transaction ID. @@ -745,15 +773,16 @@ pub enum UnifiedSendPaymentResult { } /// Returns information on a node with the given ID from the network graph. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct GraphGetNodeRequest { /// The hex-encoded node ID to look up. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_33")] pub node_id: [u8; 33], } /// The response `content` for the `GraphGetNode` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct GraphGetNodeResponse { /// The node information. pub node: Option, diff --git a/ldk-server-json-models/src/error.rs b/ldk-server-json-models/src/error.rs index 2ee3fd0f..b3fa768a 100644 --- a/ldk-server-json-models/src/error.rs +++ b/ldk-server-json-models/src/error.rs @@ -8,10 +8,11 @@ // licenses. use serde::{Deserialize, Serialize}; +use utoipa::ToSchema; /// When HttpStatusCode is not ok (200), the response `content` contains a serialized `ErrorResponse` /// with the relevant ErrorCode and `message` -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct ErrorResponse { /// The error message containing a generic description of the error condition in English. /// It is intended for a human audience only and should not be parsed to extract any information @@ -23,7 +24,9 @@ pub struct ErrorResponse { pub error_code: ErrorCode, } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)] +#[derive( + Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, ToSchema, +)] pub enum ErrorCode { /// Will never be used as `error_code` by server. UnknownError, diff --git a/ldk-server-json-models/src/events.rs b/ldk-server-json-models/src/events.rs index 713951f7..3a0ca29c 100644 --- a/ldk-server-json-models/src/events.rs +++ b/ldk-server-json-models/src/events.rs @@ -8,11 +8,12 @@ // licenses. use serde::{Deserialize, Serialize}; +use utoipa::ToSchema; /// An event emitted by the LDK Server to notify consumers of payment lifecycle changes. /// /// Events are published to the configured messaging system (e.g., RabbitMQ) as JSON. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] #[serde(rename_all = "snake_case")] pub enum Event { PaymentReceived(PaymentReceived), @@ -23,21 +24,21 @@ pub enum Event { } /// PaymentReceived indicates a payment has been received. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct PaymentReceived { /// The payment details for the received payment. pub payment: super::types::Payment, } /// PaymentSuccessful indicates a sent payment was successful. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct PaymentSuccessful { /// The payment details for the successful payment. pub payment: super::types::Payment, } /// PaymentFailed indicates a sent payment has failed. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct PaymentFailed { /// The payment details for the failed payment. pub payment: super::types::Payment, @@ -45,14 +46,14 @@ pub struct PaymentFailed { /// PaymentClaimable indicates a payment has arrived and is waiting to be manually claimed or failed. /// This event is only emitted for payments created via `Bolt11ReceiveForHash`. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct PaymentClaimable { /// The payment details for the claimable payment. pub payment: super::types::Payment, } /// PaymentForwarded indicates a payment was forwarded through the node. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct PaymentForwarded { /// The forwarded payment details. pub forwarded_payment: super::types::ForwardedPayment, diff --git a/ldk-server-json-models/src/types.rs b/ldk-server-json-models/src/types.rs index a607a461..a408bc03 100644 --- a/ldk-server-json-models/src/types.rs +++ b/ldk-server-json-models/src/types.rs @@ -8,12 +8,14 @@ // licenses. use serde::{Deserialize, Serialize}; +use utoipa::ToSchema; /// Represents a payment. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct Payment { /// An identifier used to uniquely identify a payment in hex-encoded form. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_32")] pub id: [u8; 32], /// The kind of the payment. @@ -32,7 +34,7 @@ pub struct Payment { /// The timestamp, in seconds since start of the UNIX epoch, when this entry was last updated. pub latest_update_timestamp: u64, } -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] #[serde(rename_all = "snake_case")] pub enum PaymentKind { Onchain(Onchain), @@ -43,24 +45,26 @@ pub enum PaymentKind { Spontaneous(Spontaneous), } /// Represents an on-chain payment. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct Onchain { /// The transaction identifier of this payment. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_32")] pub txid: [u8; 32], /// The confirmation status of this payment. pub status: ConfirmationStatus, } -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] #[serde(rename_all = "snake_case")] pub enum ConfirmationStatus { Confirmed(Confirmed), Unconfirmed(Unconfirmed), } /// The on-chain transaction is confirmed in the best chain. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct Confirmed { /// The hex representation of hash of the block in which the transaction was confirmed. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_32")] pub block_hash: [u8; 32], /// The height under which the block was confirmed. @@ -69,31 +73,37 @@ pub struct Confirmed { pub timestamp: u64, } /// The on-chain transaction is unconfirmed. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct Unconfirmed {} /// Represents a BOLT 11 payment. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct Bolt11 { /// The payment hash, i.e., the hash of the preimage. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_32")] pub hash: [u8; 32], /// The pre-image used by the payment. + #[schema(value_type = Option)] #[serde(default, with = "crate::serde_utils::opt_hex_32")] pub preimage: Option<[u8; 32]>, /// The secret used by the payment. + #[schema(value_type = Option)] #[serde(default, with = "crate::serde_utils::opt_hex_32")] pub secret: Option<[u8; 32]>, } /// Represents a BOLT 11 payment intended to open an LSPS 2 just-in-time channel. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct Bolt11Jit { /// The payment hash, i.e., the hash of the preimage. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_32")] pub hash: [u8; 32], /// The pre-image used by the payment. + #[schema(value_type = Option)] #[serde(default, with = "crate::serde_utils::opt_hex_32")] pub preimage: Option<[u8; 32]>, /// The secret used by the payment. + #[schema(value_type = Option)] #[serde(default, with = "crate::serde_utils::opt_hex_32")] pub secret: Option<[u8; 32]>, /// Limits applying to how much fee we allow an LSP to deduct from the payment amount. @@ -110,18 +120,22 @@ pub struct Bolt11Jit { pub counterparty_skimmed_fee_msat: Option, } /// Represents a BOLT 12 'offer' payment, i.e., a payment for an Offer. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct Bolt12Offer { /// The payment hash, i.e., the hash of the preimage. + #[schema(value_type = Option)] #[serde(default, with = "crate::serde_utils::opt_hex_32")] pub hash: Option<[u8; 32]>, /// The pre-image used by the payment. + #[schema(value_type = Option)] #[serde(default, with = "crate::serde_utils::opt_hex_32")] pub preimage: Option<[u8; 32]>, /// The secret used by the payment. + #[schema(value_type = Option)] #[serde(default, with = "crate::serde_utils::opt_hex_32")] pub secret: Option<[u8; 32]>, /// The hex-encoded ID of the offer this payment is for. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_32")] pub offer_id: [u8; 32], /// The payer's note for the payment. @@ -134,15 +148,18 @@ pub struct Bolt12Offer { pub quantity: Option, } /// Represents a BOLT 12 'refund' payment, i.e., a payment for a Refund. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct Bolt12Refund { /// The payment hash, i.e., the hash of the preimage. + #[schema(value_type = Option)] #[serde(default, with = "crate::serde_utils::opt_hex_32")] pub hash: Option<[u8; 32]>, /// The pre-image used by the payment. + #[schema(value_type = Option)] #[serde(default, with = "crate::serde_utils::opt_hex_32")] pub preimage: Option<[u8; 32]>, /// The secret used by the payment. + #[schema(value_type = Option)] #[serde(default, with = "crate::serde_utils::opt_hex_32")] pub secret: Option<[u8; 32]>, /// The payer's note for the payment. @@ -155,12 +172,14 @@ pub struct Bolt12Refund { pub quantity: Option, } /// Represents a spontaneous ("keysend") payment. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct Spontaneous { /// The payment hash, i.e., the hash of the preimage. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_32")] pub hash: [u8; 32], /// The pre-image used by the payment. + #[schema(value_type = Option)] #[serde(default, with = "crate::serde_utils::opt_hex_32")] pub preimage: Option<[u8; 32]>, } @@ -168,7 +187,7 @@ pub struct Spontaneous { /// See \[`LdkChannelConfig::accept_underpaying_htlcs`\] for more information. /// /// \[`LdkChannelConfig::accept_underpaying_htlcs`\]: lightning::util::config::ChannelConfig::accept_underpaying_htlcs -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct LspFeeLimits { /// The maximal total amount we allow any configured LSP withhold from us when forwarding the /// payment. @@ -179,20 +198,24 @@ pub struct LspFeeLimits { } /// A forwarded payment through our node. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct ForwardedPayment { /// The channel id of the incoming channel between the previous node and us. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_32")] pub prev_channel_id: [u8; 32], /// The channel id of the outgoing channel between the next node and us. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_32")] pub next_channel_id: [u8; 32], /// The `user_channel_id` of the incoming channel between the previous node and us. pub prev_user_channel_id: String, /// The node id of the previous node. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_33")] pub prev_node_id: [u8; 33], /// The node id of the next node. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_33")] pub next_node_id: [u8; 33], /// The `user_channel_id` of the outgoing channel between the next node and us. @@ -222,7 +245,7 @@ pub struct ForwardedPayment { /// The caveat described above the `total_fee_earned_msat` field applies here as well. pub outbound_amount_forwarded_msat: Option, } -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct Channel { /// The channel ID (prior to funding transaction generation, this is a random 32-byte /// identifier, afterwards this is the transaction ID of the funding transaction XOR the @@ -230,9 +253,11 @@ pub struct Channel { /// /// Note that this means this value is *not* persistent - it can change once during the /// lifetime of the channel. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_32")] pub channel_id: [u8; 32], /// The node ID of our the channel's remote counterparty. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_33")] pub counterparty_node_id: [u8; 33], /// The channel's funding transaction output, if we've negotiated the funding transaction with @@ -330,7 +355,7 @@ pub struct Channel { } /// ChannelConfig represents the configuration settings for a channel in a Lightning Network node. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct ChannelConfig { /// Amount (in millionths of a satoshi) charged per satoshi for payments forwarded outbound /// over the channel. @@ -364,7 +389,7 @@ pub struct ChannelConfig { /// and fees on commitment transaction(s) broadcasted by our counterparty in excess of /// our own fee estimate. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] #[serde(rename_all = "snake_case")] pub enum MaxDustHtlcExposure { /// This sets a fixed limit on the total dust exposure in millisatoshis. @@ -375,24 +400,26 @@ pub enum MaxDustHtlcExposure { FeeRateMultiplier(u64), } /// Represent a transaction outpoint. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct OutPoint { /// The referenced transaction's txid. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_32")] pub txid: [u8; 32], /// The index of the referenced output in its transaction's vout. pub vout: u32, } -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct BestBlock { /// The block's hash + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_32")] pub block_hash: [u8; 32], /// The height at which the block was confirmed. pub height: u32, } /// Details about the status of a known Lightning balance. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] #[serde(rename_all = "snake_case")] pub enum LightningBalance { ClaimableOnChannelClose(ClaimableOnChannelClose), @@ -405,12 +432,14 @@ pub enum LightningBalance { /// The channel is not yet closed (or the commitment or closing transaction has not yet appeared in a block). /// The given balance is claimable (less on-chain fees) if the channel is force-closed now. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct ClaimableOnChannelClose { /// The identifier of the channel this balance belongs to. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_32")] pub channel_id: [u8; 32], /// The identifier of our channel counterparty. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_33")] pub counterparty_node_id: [u8; 33], /// The amount available to claim, in satoshis, excluding the on-chain fees which will be required to do so. @@ -453,12 +482,14 @@ pub struct ClaimableOnChannelClose { } /// The channel has been closed, and the given balance is ours but awaiting confirmations until we consider it spendable. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct ClaimableAwaitingConfirmations { /// The identifier of the channel this balance belongs to. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_32")] pub channel_id: [u8; 32], /// The identifier of our channel counterparty. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_33")] pub counterparty_node_id: [u8; 33], /// The amount available to claim, in satoshis, possibly excluding the on-chain fees which were spent in broadcasting @@ -476,12 +507,14 @@ pub struct ClaimableAwaitingConfirmations { /// Once the spending transaction confirms, before it has reached enough confirmations to be considered safe from chain /// reorganizations, the balance will instead be provided via `LightningBalance::ClaimableAwaitingConfirmations`. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct ContentiousClaimable { /// The identifier of the channel this balance belongs to. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_32")] pub channel_id: [u8; 32], /// The identifier of our channel counterparty. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_33")] pub counterparty_node_id: [u8; 33], /// The amount available to claim, in satoshis, excluding the on-chain fees which were spent in broadcasting @@ -490,21 +523,25 @@ pub struct ContentiousClaimable { /// The height at which the counterparty may be able to claim the balance if we have not done so. pub timeout_height: u32, /// The payment hash that locks this HTLC. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_32")] pub payment_hash: [u8; 32], /// The preimage that can be used to claim this HTLC. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_32")] pub payment_preimage: [u8; 32], } /// HTLCs which we sent to our counterparty which are claimable after a timeout (less on-chain fees) if the counterparty /// does not know the preimage for the HTLCs. These are somewhat likely to be claimed by our counterparty before we do. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct MaybeTimeoutClaimableHtlc { /// The identifier of the channel this balance belongs to. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_32")] pub channel_id: [u8; 32], /// The identifier of our channel counterparty. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_33")] pub counterparty_node_id: [u8; 33], /// The amount available to claim, in satoshis, excluding the on-chain fees which were spent in broadcasting @@ -513,6 +550,7 @@ pub struct MaybeTimeoutClaimableHtlc { /// The height at which we will be able to claim the balance if our counterparty has not done so. pub claimable_height: u32, /// The payment hash whose preimage our counterparty needs to claim this HTLC. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_32")] pub payment_hash: [u8; 32], /// Indicates whether this HTLC represents a payment which was sent outbound from us. @@ -522,12 +560,14 @@ pub struct MaybeTimeoutClaimableHtlc { /// This will only be claimable if we receive the preimage from the node to which we forwarded this HTLC before the /// timeout. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct MaybePreimageClaimableHtlc { /// The identifier of the channel this balance belongs to. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_32")] pub channel_id: [u8; 32], /// The identifier of our channel counterparty. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_33")] pub counterparty_node_id: [u8; 33], /// The amount available to claim, in satoshis, excluding the on-chain fees which were spent in broadcasting @@ -537,6 +577,7 @@ pub struct MaybePreimageClaimableHtlc { /// claimed it ourselves. pub expiry_height: u32, /// The payment hash whose preimage we need to claim this HTLC. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_32")] pub payment_hash: [u8; 32], } @@ -545,19 +586,21 @@ pub struct MaybePreimageClaimableHtlc { /// Thus, we're able to claim all outputs in the commitment transaction, one of which has the following amount. /// /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct CounterpartyRevokedOutputClaimable { /// The identifier of the channel this balance belongs to. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_32")] pub channel_id: [u8; 32], /// The identifier of our channel counterparty. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_33")] pub counterparty_node_id: [u8; 33], /// The amount, in satoshis, of the output which we can claim. pub amount_satoshis: u64, } /// Details about the status of a known balance currently being swept to our on-chain wallet. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] #[serde(rename_all = "snake_case")] pub enum PendingSweepBalance { PendingBroadcast(PendingBroadcast), @@ -566,9 +609,10 @@ pub enum PendingSweepBalance { } /// The spendable output is about to be swept, but a spending transaction has yet to be generated and broadcast. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct PendingBroadcast { /// The identifier of the channel this balance belongs to. + #[schema(value_type = Option)] #[serde(default, with = "crate::serde_utils::opt_hex_32")] pub channel_id: Option<[u8; 32]>, /// The amount, in satoshis, of the output being swept. @@ -576,14 +620,16 @@ pub struct PendingBroadcast { } /// A spending transaction has been generated and broadcast and is awaiting confirmation on-chain. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct BroadcastAwaitingConfirmation { /// The identifier of the channel this balance belongs to. + #[schema(value_type = Option)] #[serde(default, with = "crate::serde_utils::opt_hex_32")] pub channel_id: Option<[u8; 32]>, /// The best height when we last broadcast a transaction spending the output being swept. pub latest_broadcast_height: u32, /// The identifier of the transaction spending the swept output we last broadcast. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_32")] pub latest_spending_txid: [u8; 32], /// The amount, in satoshis, of the output being swept. @@ -593,15 +639,18 @@ pub struct BroadcastAwaitingConfirmation { /// /// It will be considered irrevocably confirmed after reaching `ANTI_REORG_DELAY`. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct AwaitingThresholdConfirmations { /// The identifier of the channel this balance belongs to. + #[schema(value_type = Option)] #[serde(default, with = "crate::serde_utils::opt_hex_32")] pub channel_id: Option<[u8; 32]>, /// The identifier of the confirmed transaction spending the swept output. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_32")] pub latest_spending_txid: [u8; 32], /// The hash of the block in which the spending transaction was confirmed. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_32")] pub confirmation_hash: [u8; 32], /// The height at which the spending transaction was confirmed. @@ -610,12 +659,12 @@ pub struct AwaitingThresholdConfirmations { pub amount_satoshis: u64, } /// Token used to determine start of next page in paginated APIs. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct PageToken { pub token: String, pub index: i64, } -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] #[serde(rename_all = "snake_case")] pub enum Bolt11InvoiceDescription { Direct(String), @@ -623,7 +672,7 @@ pub enum Bolt11InvoiceDescription { } /// Configuration options for payment routing and pathfinding. /// See for more details on each field. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct RouteParametersConfig { /// The maximum total fees, in millisatoshi, that may accrue during route finding. /// Defaults to 1% of the payment amount + 50 sats @@ -640,7 +689,7 @@ pub struct RouteParametersConfig { pub max_channel_saturation_power_of_half: u32, } /// Routing fees for a channel as part of the network graph. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct GraphRoutingFees { /// Flat routing fee in millisatoshis. pub base_msat: u32, @@ -649,7 +698,7 @@ pub struct GraphRoutingFees { } /// Details about one direction of a channel in the network graph, /// as received within a `ChannelUpdate`. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct GraphChannelUpdate { /// When the last update to the channel direction was issued. /// Value is opaque, as set in the announcement. @@ -667,12 +716,14 @@ pub struct GraphChannelUpdate { } /// Details about a channel in the network graph (both directions). /// Received within a channel announcement. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct GraphChannel { /// Source node of the first direction of the channel (hex-encoded public key). + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_33")] pub node_one: [u8; 33], /// Source node of the second direction of the channel (hex-encoded public key). + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_33")] pub node_two: [u8; 33], /// The channel capacity as seen on-chain, if chain lookup is available. @@ -683,7 +734,7 @@ pub struct GraphChannel { pub two_to_one: Option, } /// Information received in the latest node_announcement from this node. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct GraphNodeAnnouncement { /// When the last known update to the node state was issued. /// Value is opaque, as set in the announcement. @@ -698,9 +749,10 @@ pub struct GraphNodeAnnouncement { } /// Details of a known Lightning peer. /// See more: -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct Peer { /// The hex-encoded node ID of the peer. + #[schema(value_type = String)] #[serde(with = "crate::serde_utils::hex_33")] pub node_id: [u8; 33], /// The network address of the peer. @@ -711,7 +763,7 @@ pub struct Peer { pub is_connected: bool, } /// Details about a node in the network graph, known from the network announcement. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] pub struct GraphNode { /// All valid channels a node has announced. pub channels: Vec, @@ -721,7 +773,9 @@ pub struct GraphNode { pub announcement_info: Option, } /// Represents the direction of a payment. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)] +#[derive( + Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, ToSchema, +)] #[serde(rename_all = "snake_case")] pub enum PaymentDirection { /// The payment is inbound. @@ -730,7 +784,9 @@ pub enum PaymentDirection { Outbound, } /// Represents the current status of a payment. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)] +#[derive( + Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, ToSchema, +)] #[serde(rename_all = "snake_case")] pub enum PaymentStatus { /// The payment is still pending. @@ -742,7 +798,9 @@ pub enum PaymentStatus { } /// Indicates whether the balance is derived from a cooperative close, a force-close (for holder or counterparty), /// or whether it is for an HTLC. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)] +#[derive( + Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, ToSchema, +)] #[serde(rename_all = "snake_case")] pub enum BalanceSource { /// The channel was force closed by the holder. diff --git a/ldk-server/Cargo.toml b/ldk-server/Cargo.toml index 152fe908..f4e0b59d 100644 --- a/ldk-server/Cargo.toml +++ b/ldk-server/Cargo.toml @@ -15,6 +15,7 @@ tokio-rustls = { version = "0.26", default-features = false, features = ["ring"] ring = { version = "0.17", default-features = false } getrandom = { version = "0.2", default-features = false } ldk-server-json-models = { path = "../ldk-server-json-models" } +utoipa = { version = "5" } hex = { package = "hex-conservative", version = "0.2.1", default-features = false } rusqlite = { version = "0.31.0", features = ["bundled"] } toml = { version = "0.8.9", default-features = false, features = ["parse"] } diff --git a/ldk-server/src/main.rs b/ldk-server/src/main.rs index d50590f6..e4bae32c 100644 --- a/ldk-server/src/main.rs +++ b/ldk-server/src/main.rs @@ -9,6 +9,7 @@ mod api; mod io; +mod openapi; mod service; mod util; diff --git a/ldk-server/src/openapi.rs b/ldk-server/src/openapi.rs new file mode 100644 index 00000000..006edf33 --- /dev/null +++ b/ldk-server/src/openapi.rs @@ -0,0 +1,580 @@ +// This file is Copyright its original authors, visible in version control +// history. +// +// This file is licensed under the Apache License, Version 2.0 or the MIT license +// , at your option. +// You may not use this file except in accordance with one or both of these +// licenses. + +#![allow(dead_code, unused_imports)] + +use ldk_server_json_models::api::*; +use ldk_server_json_models::error::{ErrorCode, ErrorResponse}; +use ldk_server_json_models::events::*; +use ldk_server_json_models::types::*; +use utoipa::openapi::security::{ApiKey, ApiKeyValue, SecurityScheme}; +use utoipa::{Modify, OpenApi}; + +#[derive(OpenApi)] +#[openapi( + info( + title = "LDK Server API", + version = "0.1.0", + description = "REST API for LDK Server, a Lightning Network node daemon built on LDK." + ), + paths( + get_node_info, + get_balances, + onchain_receive, + onchain_send, + bolt11_receive, + bolt11_receive_for_hash, + bolt11_claim_for_hash, + bolt11_fail_for_hash, + bolt11_receive_via_jit_channel, + bolt11_receive_variable_amount_via_jit_channel, + bolt11_send, + bolt12_receive, + bolt12_send, + spontaneous_send, + open_channel, + splice_in, + splice_out, + close_channel, + force_close_channel, + list_channels, + update_channel_config, + get_payment_details, + list_payments, + list_forwarded_payments, + list_peers, + connect_peer, + disconnect_peer, + sign_message, + verify_signature, + export_pathfinding_scores, + unified_send, + graph_list_channels, + graph_get_channel, + graph_list_nodes, + graph_get_node, + subscribe, + ), + components(schemas( + // API request/response types + GetNodeInfoRequest, GetNodeInfoResponse, + GetBalancesRequest, GetBalancesResponse, + OnchainReceiveRequest, OnchainReceiveResponse, + OnchainSendRequest, OnchainSendResponse, + Bolt11ReceiveRequest, Bolt11ReceiveResponse, + Bolt11ReceiveForHashRequest, Bolt11ReceiveForHashResponse, + Bolt11ClaimForHashRequest, Bolt11ClaimForHashResponse, + Bolt11FailForHashRequest, Bolt11FailForHashResponse, + Bolt11ReceiveViaJitChannelRequest, Bolt11ReceiveViaJitChannelResponse, + Bolt11ReceiveVariableAmountViaJitChannelRequest, Bolt11ReceiveVariableAmountViaJitChannelResponse, + Bolt11SendRequest, Bolt11SendResponse, + Bolt12ReceiveRequest, Bolt12ReceiveResponse, + Bolt12SendRequest, Bolt12SendResponse, + SpontaneousSendRequest, SpontaneousSendResponse, + OpenChannelRequest, OpenChannelResponse, + SpliceInRequest, SpliceInResponse, + SpliceOutRequest, SpliceOutResponse, + CloseChannelRequest, CloseChannelResponse, + ForceCloseChannelRequest, ForceCloseChannelResponse, + ListChannelsRequest, ListChannelsResponse, + UpdateChannelConfigRequest, UpdateChannelConfigResponse, + GetPaymentDetailsRequest, GetPaymentDetailsResponse, + ListPaymentsRequest, ListPaymentsResponse, + ListForwardedPaymentsRequest, ListForwardedPaymentsResponse, + ListPeersRequest, ListPeersResponse, + ConnectPeerRequest, ConnectPeerResponse, + DisconnectPeerRequest, DisconnectPeerResponse, + SignMessageRequest, SignMessageResponse, + VerifySignatureRequest, VerifySignatureResponse, + ExportPathfindingScoresRequest, ExportPathfindingScoresResponse, + UnifiedSendRequest, UnifiedSendResponse, UnifiedSendPaymentResult, + GraphListChannelsRequest, GraphListChannelsResponse, + GraphGetChannelRequest, GraphGetChannelResponse, + GraphListNodesRequest, GraphListNodesResponse, + GraphGetNodeRequest, GraphGetNodeResponse, + // Domain types + Payment, PaymentKind, PaymentDirection, PaymentStatus, + Onchain, ConfirmationStatus, Confirmed, Unconfirmed, + Bolt11, Bolt11Jit, Bolt12Offer, Bolt12Refund, Spontaneous, + LspFeeLimits, ForwardedPayment, + Channel, ChannelConfig, MaxDustHtlcExposure, OutPoint, BestBlock, + LightningBalance, ClaimableOnChannelClose, ClaimableAwaitingConfirmations, + ContentiousClaimable, MaybeTimeoutClaimableHtlc, MaybePreimageClaimableHtlc, + CounterpartyRevokedOutputClaimable, + PendingSweepBalance, PendingBroadcast, BroadcastAwaitingConfirmation, + AwaitingThresholdConfirmations, + PageToken, Bolt11InvoiceDescription, RouteParametersConfig, BalanceSource, + GraphRoutingFees, GraphChannelUpdate, GraphChannel, + GraphNodeAnnouncement, GraphNode, Peer, + // Event types + Event, PaymentReceived, PaymentSuccessful, PaymentFailed, + PaymentClaimable, PaymentForwarded, + // Error types + ErrorResponse, ErrorCode, + )), + modifiers(&HmacSecurityAddon), + tags( + (name = "Node", description = "Node information and balances"), + (name = "Onchain", description = "On-chain wallet operations"), + (name = "Bolt11", description = "BOLT11 Lightning invoice operations"), + (name = "Bolt12", description = "BOLT12 Lightning offer operations"), + (name = "Channels", description = "Channel management"), + (name = "Payments", description = "Payment queries"), + (name = "Peers", description = "Peer management"), + (name = "Send", description = "Sending payments"), + (name = "Graph", description = "Network graph queries"), + (name = "Crypto", description = "Message signing and verification"), + (name = "Events", description = "Server-Sent Events for payment notifications"), + ) +)] +pub(crate) struct ApiDoc; + +struct HmacSecurityAddon; + +impl Modify for HmacSecurityAddon { + fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) { + if let Some(components) = openapi.components.as_mut() { + components.add_security_scheme( + "hmac_auth", + SecurityScheme::ApiKey(ApiKey::Header(ApiKeyValue::new("X-Auth"))), + ); + } + } +} + +#[utoipa::path( + post, path = "/GetNodeInfo", + request_body = GetNodeInfoRequest, + responses( + (status = 200, body = GetNodeInfoResponse), + (status = 400, body = ErrorResponse), + ), + security(("hmac_auth" = [])), + tag = "Node" +)] +fn get_node_info() {} + +#[utoipa::path( + post, path = "/GetBalances", + request_body = GetBalancesRequest, + responses( + (status = 200, body = GetBalancesResponse), + (status = 400, body = ErrorResponse), + ), + security(("hmac_auth" = [])), + tag = "Node" +)] +fn get_balances() {} + +#[utoipa::path( + post, path = "/OnchainReceive", + request_body = OnchainReceiveRequest, + responses( + (status = 200, body = OnchainReceiveResponse), + (status = 400, body = ErrorResponse), + ), + security(("hmac_auth" = [])), + tag = "Onchain" +)] +fn onchain_receive() {} + +#[utoipa::path( + post, path = "/OnchainSend", + request_body = OnchainSendRequest, + responses( + (status = 200, body = OnchainSendResponse), + (status = 400, body = ErrorResponse), + ), + security(("hmac_auth" = [])), + tag = "Onchain" +)] +fn onchain_send() {} + +#[utoipa::path( + post, path = "/Bolt11Receive", + request_body = Bolt11ReceiveRequest, + responses( + (status = 200, body = Bolt11ReceiveResponse), + (status = 400, body = ErrorResponse), + ), + security(("hmac_auth" = [])), + tag = "Bolt11" +)] +fn bolt11_receive() {} + +#[utoipa::path( + post, path = "/Bolt11ReceiveForHash", + request_body = Bolt11ReceiveForHashRequest, + responses( + (status = 200, body = Bolt11ReceiveForHashResponse), + (status = 400, body = ErrorResponse), + ), + security(("hmac_auth" = [])), + tag = "Bolt11" +)] +fn bolt11_receive_for_hash() {} + +#[utoipa::path( + post, path = "/Bolt11ClaimForHash", + request_body = Bolt11ClaimForHashRequest, + responses( + (status = 200, body = Bolt11ClaimForHashResponse), + (status = 400, body = ErrorResponse), + ), + security(("hmac_auth" = [])), + tag = "Bolt11" +)] +fn bolt11_claim_for_hash() {} + +#[utoipa::path( + post, path = "/Bolt11FailForHash", + request_body = Bolt11FailForHashRequest, + responses( + (status = 200, body = Bolt11FailForHashResponse), + (status = 400, body = ErrorResponse), + ), + security(("hmac_auth" = [])), + tag = "Bolt11" +)] +fn bolt11_fail_for_hash() {} + +#[utoipa::path( + post, path = "/Bolt11ReceiveViaJitChannel", + request_body = Bolt11ReceiveViaJitChannelRequest, + responses( + (status = 200, body = Bolt11ReceiveViaJitChannelResponse), + (status = 400, body = ErrorResponse), + ), + security(("hmac_auth" = [])), + tag = "Bolt11" +)] +fn bolt11_receive_via_jit_channel() {} + +#[utoipa::path( + post, path = "/Bolt11ReceiveVariableAmountViaJitChannel", + request_body = Bolt11ReceiveVariableAmountViaJitChannelRequest, + responses( + (status = 200, body = Bolt11ReceiveVariableAmountViaJitChannelResponse), + (status = 400, body = ErrorResponse), + ), + security(("hmac_auth" = [])), + tag = "Bolt11" +)] +fn bolt11_receive_variable_amount_via_jit_channel() {} + +#[utoipa::path( + post, path = "/Bolt11Send", + request_body = Bolt11SendRequest, + responses( + (status = 200, body = Bolt11SendResponse), + (status = 400, body = ErrorResponse), + ), + security(("hmac_auth" = [])), + tag = "Bolt11" +)] +fn bolt11_send() {} + +#[utoipa::path( + post, path = "/Bolt12Receive", + request_body = Bolt12ReceiveRequest, + responses( + (status = 200, body = Bolt12ReceiveResponse), + (status = 400, body = ErrorResponse), + ), + security(("hmac_auth" = [])), + tag = "Bolt12" +)] +fn bolt12_receive() {} + +#[utoipa::path( + post, path = "/Bolt12Send", + request_body = Bolt12SendRequest, + responses( + (status = 200, body = Bolt12SendResponse), + (status = 400, body = ErrorResponse), + ), + security(("hmac_auth" = [])), + tag = "Bolt12" +)] +fn bolt12_send() {} + +#[utoipa::path( + post, path = "/SpontaneousSend", + request_body = SpontaneousSendRequest, + responses( + (status = 200, body = SpontaneousSendResponse), + (status = 400, body = ErrorResponse), + ), + security(("hmac_auth" = [])), + tag = "Send" +)] +fn spontaneous_send() {} + +#[utoipa::path( + post, path = "/OpenChannel", + request_body = OpenChannelRequest, + responses( + (status = 200, body = OpenChannelResponse), + (status = 400, body = ErrorResponse), + ), + security(("hmac_auth" = [])), + tag = "Channels" +)] +fn open_channel() {} + +#[utoipa::path( + post, path = "/SpliceIn", + request_body = SpliceInRequest, + responses( + (status = 200, body = SpliceInResponse), + (status = 400, body = ErrorResponse), + ), + security(("hmac_auth" = [])), + tag = "Channels" +)] +fn splice_in() {} + +#[utoipa::path( + post, path = "/SpliceOut", + request_body = SpliceOutRequest, + responses( + (status = 200, body = SpliceOutResponse), + (status = 400, body = ErrorResponse), + ), + security(("hmac_auth" = [])), + tag = "Channels" +)] +fn splice_out() {} + +#[utoipa::path( + post, path = "/CloseChannel", + request_body = CloseChannelRequest, + responses( + (status = 200, body = CloseChannelResponse), + (status = 400, body = ErrorResponse), + ), + security(("hmac_auth" = [])), + tag = "Channels" +)] +fn close_channel() {} + +#[utoipa::path( + post, path = "/ForceCloseChannel", + request_body = ForceCloseChannelRequest, + responses( + (status = 200, body = ForceCloseChannelResponse), + (status = 400, body = ErrorResponse), + ), + security(("hmac_auth" = [])), + tag = "Channels" +)] +fn force_close_channel() {} + +#[utoipa::path( + post, path = "/ListChannels", + request_body = ListChannelsRequest, + responses( + (status = 200, body = ListChannelsResponse), + (status = 400, body = ErrorResponse), + ), + security(("hmac_auth" = [])), + tag = "Channels" +)] +fn list_channels() {} + +#[utoipa::path( + post, path = "/UpdateChannelConfig", + request_body = UpdateChannelConfigRequest, + responses( + (status = 200, body = UpdateChannelConfigResponse), + (status = 400, body = ErrorResponse), + ), + security(("hmac_auth" = [])), + tag = "Channels" +)] +fn update_channel_config() {} + +#[utoipa::path( + post, path = "/GetPaymentDetails", + request_body = GetPaymentDetailsRequest, + responses( + (status = 200, body = GetPaymentDetailsResponse), + (status = 400, body = ErrorResponse), + ), + security(("hmac_auth" = [])), + tag = "Payments" +)] +fn get_payment_details() {} + +#[utoipa::path( + post, path = "/ListPayments", + request_body = ListPaymentsRequest, + responses( + (status = 200, body = ListPaymentsResponse), + (status = 400, body = ErrorResponse), + ), + security(("hmac_auth" = [])), + tag = "Payments" +)] +fn list_payments() {} + +#[utoipa::path( + post, path = "/ListForwardedPayments", + request_body = ListForwardedPaymentsRequest, + responses( + (status = 200, body = ListForwardedPaymentsResponse), + (status = 400, body = ErrorResponse), + ), + security(("hmac_auth" = [])), + tag = "Payments" +)] +fn list_forwarded_payments() {} + +#[utoipa::path( + post, path = "/ListPeers", + request_body = ListPeersRequest, + responses( + (status = 200, body = ListPeersResponse), + (status = 400, body = ErrorResponse), + ), + security(("hmac_auth" = [])), + tag = "Peers" +)] +fn list_peers() {} + +#[utoipa::path( + post, path = "/ConnectPeer", + request_body = ConnectPeerRequest, + responses( + (status = 200, body = ConnectPeerResponse), + (status = 400, body = ErrorResponse), + ), + security(("hmac_auth" = [])), + tag = "Peers" +)] +fn connect_peer() {} + +#[utoipa::path( + post, path = "/DisconnectPeer", + request_body = DisconnectPeerRequest, + responses( + (status = 200, body = DisconnectPeerResponse), + (status = 400, body = ErrorResponse), + ), + security(("hmac_auth" = [])), + tag = "Peers" +)] +fn disconnect_peer() {} + +#[utoipa::path( + post, path = "/SignMessage", + request_body = SignMessageRequest, + responses( + (status = 200, body = SignMessageResponse), + (status = 400, body = ErrorResponse), + ), + security(("hmac_auth" = [])), + tag = "Crypto" +)] +fn sign_message() {} + +#[utoipa::path( + post, path = "/VerifySignature", + request_body = VerifySignatureRequest, + responses( + (status = 200, body = VerifySignatureResponse), + (status = 400, body = ErrorResponse), + ), + security(("hmac_auth" = [])), + tag = "Crypto" +)] +fn verify_signature() {} + +#[utoipa::path( + post, path = "/ExportPathfindingScores", + request_body = ExportPathfindingScoresRequest, + responses( + (status = 200, body = ExportPathfindingScoresResponse), + (status = 400, body = ErrorResponse), + ), + security(("hmac_auth" = [])), + tag = "Node" +)] +fn export_pathfinding_scores() {} + +#[utoipa::path( + post, path = "/UnifiedSend", + request_body = UnifiedSendRequest, + responses( + (status = 200, body = UnifiedSendResponse), + (status = 400, body = ErrorResponse), + ), + security(("hmac_auth" = [])), + tag = "Send" +)] +fn unified_send() {} + +#[utoipa::path( + post, path = "/GraphListChannels", + request_body = GraphListChannelsRequest, + responses( + (status = 200, body = GraphListChannelsResponse), + (status = 400, body = ErrorResponse), + ), + security(("hmac_auth" = [])), + tag = "Graph" +)] +fn graph_list_channels() {} + +#[utoipa::path( + post, path = "/GraphGetChannel", + request_body = GraphGetChannelRequest, + responses( + (status = 200, body = GraphGetChannelResponse), + (status = 400, body = ErrorResponse), + ), + security(("hmac_auth" = [])), + tag = "Graph" +)] +fn graph_get_channel() {} + +#[utoipa::path( + post, path = "/GraphListNodes", + request_body = GraphListNodesRequest, + responses( + (status = 200, body = GraphListNodesResponse), + (status = 400, body = ErrorResponse), + ), + security(("hmac_auth" = [])), + tag = "Graph" +)] +fn graph_list_nodes() {} + +#[utoipa::path( + post, path = "/GraphGetNode", + request_body = GraphGetNodeRequest, + responses( + (status = 200, body = GraphGetNodeResponse), + (status = 400, body = ErrorResponse), + ), + security(("hmac_auth" = [])), + tag = "Graph" +)] +fn graph_get_node() {} + +#[utoipa::path( + post, path = "/Subscribe", + responses( + (status = 200, content_type = "text/event-stream", description = "Server-Sent Events stream of payment lifecycle events"), + (status = 401, body = ErrorResponse), + ), + security(("hmac_auth" = [])), + tag = "Events" +)] +fn subscribe() {} diff --git a/ldk-server/src/service.rs b/ldk-server/src/service.rs index 2daeaa60..641fb927 100644 --- a/ldk-server/src/service.rs +++ b/ldk-server/src/service.rs @@ -198,6 +198,19 @@ impl NodeService { fn call_inner( &self, req: Request, ) -> Pin> + Send>> { + if req.uri().path() == "/openapi.json" { + return Box::pin(async { + static OPENAPI_JSON: std::sync::LazyLock = std::sync::LazyLock::new(|| { + use utoipa::OpenApi; + crate::openapi::ApiDoc::openapi().to_json().unwrap() + }); + Ok(Response::builder() + .header("Content-Type", "application/json") + .body(Full::new(Bytes::from(OPENAPI_JSON.as_str())).boxed()) + .unwrap()) + }); + } + // Extract auth params from headers (validation happens after body is read) let auth_params = match extract_auth_params(&req) { Ok(params) => params,