From 44a225853e94e33ca7b982d688dc21a297b6b482 Mon Sep 17 00:00:00 2001 From: benthecarman Date: Mon, 23 Mar 2026 19:21:39 -0500 Subject: [PATCH] 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. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/check-protos.yml | 32 - CLAUDE.md | 2 +- CONTRIBUTING.md | 13 +- Cargo.lock | 19 +- 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 | 219 +-- ldk-server-cli/Cargo.toml | 2 +- ldk-server-cli/src/main.rs | 373 ++--- ldk-server-cli/src/types.rs | 27 - 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 | 10 + .../src/api.rs | 781 ++++------- .../src/endpoints.rs | 0 ldk-server-json-models/src/error.rs | 88 ++ ldk-server-json-models/src/events.rs | 59 + .../src/lib.rs | 1 - ldk-server-json-models/src/serde_utils.rs | 139 ++ ldk-server-json-models/src/types.rs | 750 +++++++++++ ldk-server-json-models/tests/serialization.rs | 1200 +++++++++++++++++ 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 | 20 +- .../src/api/bolt11_receive_via_jit_channel.rs | 24 +- 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 | 14 +- 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 | 17 +- ldk-server/src/api/list_payments.rs | 15 +- ldk-server/src/api/list_peers.rs | 6 +- ldk-server/src/api/mod.rs | 48 +- ldk-server/src/api/onchain_receive.rs | 2 +- ldk-server/src/api/onchain_send.rs | 7 +- 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 | 27 +- 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/main.rs | 51 +- ldk-server/src/service.rs | 68 +- .../src/util/{proto_adapter.rs => adapter.rs} | 442 +++--- ldk-server/src/util/mod.rs | 2 +- 79 files changed, 3337 insertions(+), 4750 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 (50%) 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 create mode 100644 ldk-server-json-models/tests/serialization.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..7a9496a5 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,19 +1790,18 @@ 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", + "serde_json", ] [[package]] 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..9e08de08 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,18 @@ 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()); + + // Cross-check block hash against bitcoind's RPC at the same height. + // This verifies byte order is correct (Bitcoin hashes display reversed). + let height = output["current_best_block"]["height"].as_u64().unwrap(); + assert!(height > 0); + let api_block_hash = output["current_best_block"]["block_hash"].as_str().unwrap(); + let rpc_block_hash = bitcoind.bitcoind.client.get_block_hash(height).unwrap().0; + assert_eq!( + api_block_hash, rpc_block_hash, + "block_hash from API should match bitcoind RPC (display byte order)" + ); } #[tokio::test] @@ -73,7 +83,7 @@ async fn test_cli_list_payments_empty() { let server = LdkServerHandle::start(&bitcoind).await; let output = run_cli(&server, &["list-payments"]); - assert!(output["list"].as_array().unwrap().is_empty()); + assert!(output["payments"].as_array().unwrap().is_empty()); } #[tokio::test] @@ -82,7 +92,7 @@ async fn test_cli_list_forwarded_payments_empty() { let server = LdkServerHandle::start(&bitcoind).await; let output = run_cli(&server, &["list-forwarded-payments"]); - assert!(output["list"].as_array().unwrap().is_empty()); + assert!(output["forwarded_payments"].as_array().unwrap().is_empty()); } #[tokio::test] @@ -102,7 +112,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 +129,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 +151,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 +198,13 @@ 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(); + + // Verify the txid is in display byte order by round-tripping through + // bitcoin::Txid::from_str (which expects display/reversed hex) + let txid = ldk_node::bitcoin::Txid::from_str(txid_hex) + .expect("API txid should parse as a Bitcoin Txid (display byte order)"); + assert_eq!(txid.to_string(), txid_hex, "Txid round-trip should preserve hex"); } #[tokio::test] @@ -191,7 +214,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 +232,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 +263,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 +281,33 @@ 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 txid is in correct display byte order + let funding_txid_hex = ch["funding_txo"]["txid"].as_str().unwrap(); + let funding_txid = ldk_node::bitcoin::Txid::from_str(funding_txid_hex) + .expect("funding txid should parse as a Bitcoin Txid (display byte order)"); + assert_eq!(funding_txid.to_string(), funding_txid_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 +317,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 +348,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 +363,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,15 +386,16 @@ 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 .unwrap(); let output = run_cli(&server_a, &["pay", &invoice_resp.invoice]); - assert!(output.get("bolt11_payment_id").is_some()); + assert!( + output.get("bolt11").unwrap().get("payment_id").is_some(), + "Expected bolt11.payment_id in output, got {output:?}" + ); // Pay a BOLT12 offer via unified `pay` command let offer_resp = server_b @@ -357,7 +409,10 @@ async fn test_cli_pay() { .await .unwrap(); let output = run_cli(&server_a, &["pay", &offer_resp.offer, "10000sat"]); - assert!(output.get("bolt12_payment_id").is_some()); + assert!( + output.get("bolt12").unwrap().get("payment_id").is_some(), + "Expected bolt12.payment_id in output, got {output:?}" + ); } #[tokio::test] @@ -395,7 +450,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 +459,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 +482,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 +497,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 +524,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 @@ -469,7 +534,7 @@ async fn test_cli_list_payments() { tokio::time::sleep(Duration::from_secs(3)).await; let output = run_cli(&server_a, &["list-payments"]); - assert!(!output["list"].as_array().unwrap().is_empty()); + assert!(!output["payments"].as_array().unwrap().is_empty()); } #[tokio::test] @@ -479,7 +544,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 +562,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 +580,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 +592,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 +644,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 +654,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 +699,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 +713,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 +748,8 @@ 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(_)))), - "Expected PaymentForwarded event on LSP node B, got events: {:?}", - events_b.iter().map(|e| &e.event).collect::>() + events_b.iter().any(|e| matches!(e, Event::PaymentForwarded(_))), + "Expected PaymentForwarded event on LSP node B, got events: {events_b:?}" ); node_c.stop().unwrap(); @@ -731,9 +800,8 @@ 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(_)))), - "Expected PaymentClaimable on receiver, got events: {:?}", - events_b.iter().map(|e| &e.event).collect::>() + events_b.iter().any(|e| matches!(e, Event::PaymentClaimable(_))), + "Expected PaymentClaimable on receiver, got events: {events_b:?}", ); // Claim the payment on B @@ -749,26 +817,21 @@ 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(_)))), - "Expected PaymentReceived on receiver after claim, got events: {:?}", - events_b.iter().map(|e| &e.event).collect::>() + events_b.iter().any(|e| matches!(e, Event::PaymentReceived(_))), + "Expected PaymentReceived on receiver after claim, got events: {events_b:?}", ); // 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(_)))), - "Expected PaymentSuccessful on sender, got events: {:?}", - events_a.iter().map(|e| &e.event).collect::>() + events_a.iter().any(|e| matches!(e, Event::PaymentSuccessful(_))), + "Expected PaymentSuccessful on sender, got events: {events_a:?}", ); } } #[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 +871,8 @@ 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(_)))), - "Expected PaymentClaimable on receiver, got events: {:?}", - events_b.iter().map(|e| &e.event).collect::>() + events_b.iter().any(|e| matches!(e, Event::PaymentClaimable(_))), + "Expected PaymentClaimable on receiver, got events: {events_b:?}", ); // Fail the payment on B using CLI @@ -822,8 +884,7 @@ 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(_)))), - "Expected PaymentFailed on sender after hodl rejection, got events: {:?}", - events_a.iter().map(|e| &e.event).collect::>() + events_a.iter().any(|e| matches!(e, Event::PaymentFailed(_))), + "Expected PaymentFailed on sender after hodl rejection, got events: {events_a:?}", ); } 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..f5253462 100644 --- a/ldk-server-cli/src/main.rs +++ b/ldk-server-cli/src/main.rs @@ -15,49 +15,36 @@ 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::{ - Bolt11ClaimForHashRequest, Bolt11ClaimForHashResponse, Bolt11FailForHashRequest, - Bolt11FailForHashResponse, Bolt11ReceiveForHashRequest, Bolt11ReceiveForHashResponse, - Bolt11ReceiveRequest, Bolt11ReceiveResponse, Bolt11ReceiveVariableAmountViaJitChannelRequest, - Bolt11ReceiveVariableAmountViaJitChannelResponse, Bolt11ReceiveViaJitChannelRequest, - Bolt11ReceiveViaJitChannelResponse, Bolt11SendRequest, Bolt11SendResponse, - Bolt12ReceiveRequest, Bolt12ReceiveResponse, Bolt12SendRequest, Bolt12SendResponse, - CloseChannelRequest, CloseChannelResponse, ConnectPeerRequest, ConnectPeerResponse, - DisconnectPeerRequest, DisconnectPeerResponse, ExportPathfindingScoresRequest, - ForceCloseChannelRequest, ForceCloseChannelResponse, GetBalancesRequest, GetBalancesResponse, - GetNodeInfoRequest, GetNodeInfoResponse, GetPaymentDetailsRequest, GetPaymentDetailsResponse, - GraphGetChannelRequest, GraphGetChannelResponse, GraphGetNodeRequest, GraphGetNodeResponse, - GraphListChannelsRequest, GraphListChannelsResponse, GraphListNodesRequest, - GraphListNodesResponse, ListChannelsRequest, ListChannelsResponse, - ListForwardedPaymentsRequest, ListPaymentsRequest, ListPeersRequest, ListPeersResponse, - OnchainReceiveRequest, OnchainReceiveResponse, OnchainSendRequest, OnchainSendResponse, - OpenChannelRequest, OpenChannelResponse, SignMessageRequest, SignMessageResponse, - SpliceInRequest, SpliceInResponse, SpliceOutRequest, SpliceOutResponse, SpontaneousSendRequest, - SpontaneousSendResponse, UnifiedSendRequest, UnifiedSendResponse, UpdateChannelConfigRequest, - UpdateChannelConfigResponse, VerifySignatureRequest, VerifySignatureResponse, +use ldk_server_client::ldk_server_json_models::api::{ + Bolt11ClaimForHashRequest, Bolt11FailForHashRequest, Bolt11ReceiveForHashRequest, + Bolt11ReceiveRequest, Bolt11ReceiveVariableAmountViaJitChannelRequest, + Bolt11ReceiveViaJitChannelRequest, Bolt11SendRequest, Bolt12ReceiveRequest, Bolt12SendRequest, + CloseChannelRequest, ConnectPeerRequest, DisconnectPeerRequest, ExportPathfindingScoresRequest, + ForceCloseChannelRequest, GetBalancesRequest, GetNodeInfoRequest, GetPaymentDetailsRequest, + GraphGetChannelRequest, GraphGetNodeRequest, GraphListChannelsRequest, GraphListNodesRequest, + ListChannelsRequest, ListForwardedPaymentsRequest, ListPaymentsRequest, ListPeersRequest, + OnchainReceiveRequest, OnchainSendRequest, OpenChannelRequest, SignMessageRequest, + SpliceInRequest, SpliceOutRequest, SpontaneousSendRequest, UnifiedSendRequest, + UpdateChannelConfigRequest, VerifySignatureRequest, }; -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, RouteParametersConfig, }; use serde::Serialize; -use serde_json::{json, Value}; -use types::{ - Amount, CliListForwardedPaymentsResponse, CliListPaymentsResponse, CliPaginatedResponse, -}; +use serde_json::json; +use types::Amount; 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; @@ -421,13 +408,7 @@ enum Commands { ListChannels, #[command(about = "Retrieve list of all payments")] ListPayments { - #[arg(short, long)] - #[arg( - help = "Fetch at least this many payments by iterating through multiple pages. Returns combined results with the last page token. If not provided, returns only a single page." - )] - number_of_payments: Option, - #[arg(long)] - #[arg(help = "Page token to continue from a previous page (format: token:index)")] + #[arg(help = "Page token to continue from a previous page")] page_token: Option, }, #[command(about = "Get details of a specific payment by its payment ID")] @@ -437,13 +418,7 @@ enum Commands { }, #[command(about = "Retrieves list of all forwarded payments")] ListForwardedPayments { - #[arg( - short, - long, - help = "Fetch at least this many forwarded payments by iterating through multiple pages. Returns combined results with the last page token. If not provided, returns only a single page." - )] - number_of_payments: Option, - #[arg(long, help = "Page token to continue from a previous page (format: token:index)")] + #[arg(help = "Page token to continue from a previous page")] page_token: Option, }, #[command(about = "Update the forwarding fees and CLTV expiry delta for an existing channel")] @@ -605,23 +580,17 @@ async fn main() { match cli.command { Commands::GetNodeInfo => { - handle_response_result::<_, GetNodeInfoResponse>( - client.get_node_info(GetNodeInfoRequest {}).await, - ); + handle_response_result(client.get_node_info(GetNodeInfoRequest {}).await); }, Commands::GetBalances => { - handle_response_result::<_, GetBalancesResponse>( - client.get_balances(GetBalancesRequest {}).await, - ); + handle_response_result(client.get_balances(GetBalancesRequest {}).await); }, Commands::OnchainReceive => { - handle_response_result::<_, OnchainReceiveResponse>( - client.onchain_receive(OnchainReceiveRequest {}).await, - ); + handle_response_result(client.onchain_receive(OnchainReceiveRequest {}).await); }, Commands::OnchainSend { address, amount, send_all, fee_rate_sat_per_vb } => { let amount_sats = amount.map(|a| a.to_sat().unwrap_or_else(|e| handle_error_msg(&e))); - handle_response_result::<_, OnchainSendResponse>( + handle_response_result( client .onchain_send(OnchainSendRequest { address, @@ -641,9 +610,7 @@ async fn main() { let request = Bolt11ReceiveRequest { description: invoice_description, expiry_secs, amount_msat }; - handle_response_result::<_, Bolt11ReceiveResponse>( - client.bolt11_receive(request).await, - ); + handle_response_result(client.bolt11_receive(request).await); }, Commands::Bolt11ReceiveForHash { payment_hash, @@ -654,12 +621,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,27 +637,29 @@ 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>( - client.bolt11_receive_for_hash(request).await, - ); + handle_response_result(client.bolt11_receive_for_hash(request).await); }, Commands::Bolt11ClaimForHash { preimage, claimable_amount, payment_hash } => { - handle_response_result::<_, Bolt11ClaimForHashResponse>( + handle_response_result( 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, + handle_response_result( + client + .bolt11_fail_for_hash(Bolt11FailForHashRequest { + payment_hash: parse_hex_32(&payment_hash, "payment_hash"), + }) + .await, ); }, Commands::Bolt11ReceiveViaJitChannel { @@ -711,9 +676,7 @@ async fn main() { max_total_lsp_fee_limit_msat: max_total_lsp_fee_limit.map(|a| a.to_msat()), }; - handle_response_result::<_, Bolt11ReceiveViaJitChannelResponse>( - client.bolt11_receive_via_jit_channel(request).await, - ); + handle_response_result(client.bolt11_receive_via_jit_channel(request).await); }, Commands::Bolt11ReceiveVariableAmountViaJitChannel { description, @@ -727,7 +690,7 @@ async fn main() { max_proportional_lsp_fee_limit_ppm_msat, }; - handle_response_result::<_, Bolt11ReceiveVariableAmountViaJitChannelResponse>( + handle_response_result( client.bolt11_receive_variable_amount_via_jit_channel(request).await, ); }, @@ -749,7 +712,7 @@ async fn main() { max_channel_saturation_power_of_half: max_channel_saturation_power_of_half .unwrap_or(DEFAULT_MAX_CHANNEL_SATURATION_POWER_OF_HALF), }; - handle_response_result::<_, Bolt11SendResponse>( + handle_response_result( client .bolt11_send(Bolt11SendRequest { invoice, @@ -761,7 +724,7 @@ async fn main() { }, Commands::Bolt12Receive { description, amount, expiry_secs, quantity } => { let amount_msat = amount.map(|a| a.to_msat()); - handle_response_result::<_, Bolt12ReceiveResponse>( + handle_response_result( client .bolt12_receive(Bolt12ReceiveRequest { description, @@ -793,7 +756,7 @@ async fn main() { .unwrap_or(DEFAULT_MAX_CHANNEL_SATURATION_POWER_OF_HALF), }; - handle_response_result::<_, Bolt12SendResponse>( + handle_response_result( client .bolt12_send(Bolt12SendRequest { offer, @@ -824,11 +787,11 @@ async fn main() { .unwrap_or(DEFAULT_MAX_CHANNEL_SATURATION_POWER_OF_HALF), }; - handle_response_result::<_, SpontaneousSendResponse>( + handle_response_result( client .spontaneous_send(SpontaneousSendRequest { amount_msat, - node_id, + node_id: parse_hex_33(&node_id, "node_id"), route_parameters: Some(route_parameters), }) .await, @@ -852,7 +815,7 @@ async fn main() { max_channel_saturation_power_of_half: max_channel_saturation_power_of_half .unwrap_or(DEFAULT_MAX_CHANNEL_SATURATION_POWER_OF_HALF), }; - handle_response_result::<_, UnifiedSendResponse>( + handle_response_result( client .unified_send(UnifiedSendRequest { uri, @@ -863,9 +826,15 @@ async fn main() { ); }, Commands::CloseChannel { user_channel_id, counterparty_node_id } => { - handle_response_result::<_, CloseChannelResponse>( + handle_response_result( 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, ); }, @@ -874,11 +843,14 @@ async fn main() { counterparty_node_id, force_close_reason, } => { - handle_response_result::<_, ForceCloseChannelResponse>( + handle_response_result( 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, @@ -903,10 +875,10 @@ async fn main() { cltv_expiry_delta, ); - handle_response_result::<_, OpenChannelResponse>( + handle_response_result( client .open_channel(OpenChannelRequest { - node_pubkey, + node_pubkey: parse_hex_33(&node_pubkey, "node_pubkey"), address, channel_amount_sats, push_to_counterparty_msat, @@ -919,11 +891,14 @@ async fn main() { Commands::SpliceIn { user_channel_id, counterparty_node_id, splice_amount } => { let splice_amount_sats = splice_amount.to_sat().unwrap_or_else(|e| handle_error_msg(&e)); - handle_response_result::<_, SpliceInResponse>( + handle_response_result( 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, @@ -932,11 +907,14 @@ async fn main() { Commands::SpliceOut { user_channel_id, counterparty_node_id, address, splice_amount } => { let splice_amount_sats = splice_amount.to_sat().unwrap_or_else(|e| handle_error_msg(&e)); - handle_response_result::<_, SpliceOutResponse>( + handle_response_result( 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, }) @@ -944,45 +922,23 @@ async fn main() { ); }, Commands::ListChannels => { - handle_response_result::<_, ListChannelsResponse>( - client.list_channels(ListChannelsRequest {}).await, - ); + handle_response_result(client.list_channels(ListChannelsRequest {}).await); }, - Commands::ListPayments { number_of_payments, page_token } => { - let page_token = page_token - .map(|token_str| parse_page_token(&token_str).unwrap_or_else(|e| handle_error(e))); - - handle_response_result::<_, CliListPaymentsResponse>( - fetch_paginated( - number_of_payments, - page_token, - |pt| client.list_payments(ListPaymentsRequest { page_token: pt }), - |r| (r.payments, r.next_page_token), - ) - .await, - ); + Commands::ListPayments { page_token } => { + handle_response_result(client.list_payments(ListPaymentsRequest { page_token }).await); }, Commands::GetPaymentDetails { payment_id } => { - handle_response_result::<_, GetPaymentDetailsResponse>( - client.get_payment_details(GetPaymentDetailsRequest { payment_id }).await, + handle_response_result( + client + .get_payment_details(GetPaymentDetailsRequest { + payment_id: parse_hex_32(&payment_id, "payment_id"), + }) + .await, ); }, - Commands::ListForwardedPayments { number_of_payments, page_token } => { - let page_token = page_token - .map(|token_str| parse_page_token(&token_str).unwrap_or_else(|e| handle_error(e))); - - handle_response_result::<_, CliListForwardedPaymentsResponse>( - fetch_paginated( - number_of_payments, - page_token, - |pt| { - client.list_forwarded_payments(ListForwardedPaymentsRequest { - page_token: pt, - }) - }, - |r| (r.forwarded_payments, r.next_page_token), - ) - .await, + Commands::ListForwardedPayments { page_token } => { + handle_response_result( + client.list_forwarded_payments(ListForwardedPaymentsRequest { page_token }).await, ); }, Commands::UpdateChannelConfig { @@ -1001,11 +957,14 @@ async fn main() { max_dust_htlc_exposure: None, }; - handle_response_result::<_, UpdateChannelConfigResponse>( + handle_response_result( 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, @@ -1020,40 +979,46 @@ async fn main() { eprintln!("Error: address is required. Provide it as pubkey@address or as a separate argument."); std::process::exit(1); }; - handle_response_result::<_, ConnectPeerResponse>( - client.connect_peer(ConnectPeerRequest { node_pubkey, address, persist }).await, + handle_response_result( + 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, + handle_response_result( + client + .disconnect_peer(DisconnectPeerRequest { + node_pubkey: parse_hex_33(&node_pubkey, "node_pubkey"), + }) + .await, ); }, Commands::ListPeers => { - handle_response_result::<_, ListPeersResponse>( - client.list_peers(ListPeersRequest {}).await, - ); + handle_response_result(client.list_peers(ListPeersRequest {}).await); }, Commands::SignMessage { message } => { - handle_response_result::<_, SignMessageResponse>( - client - .sign_message(SignMessageRequest { message: message.into_bytes().into() }) - .await, + handle_response_result( + client.sign_message(SignMessageRequest { message: message.into_bytes() }).await, ); }, Commands::VerifySignature { message, signature, public_key } => { - handle_response_result::<_, VerifySignatureResponse>( + handle_response_result( 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, ); }, Commands::ExportPathfindingScores => { - handle_response_result::<_, Value>( + handle_response_result( client.export_pathfinding_scores(ExportPathfindingScoresRequest {}).await.map( |s| { let scores_hex = s.scores.as_hex().to_string(); @@ -1063,23 +1028,23 @@ async fn main() { ); }, Commands::GraphListChannels => { - handle_response_result::<_, GraphListChannelsResponse>( - client.graph_list_channels(GraphListChannelsRequest {}).await, - ); + handle_response_result(client.graph_list_channels(GraphListChannelsRequest {}).await); }, Commands::GraphGetChannel { short_channel_id } => { - handle_response_result::<_, GraphGetChannelResponse>( + handle_response_result( client.graph_get_channel(GraphGetChannelRequest { short_channel_id }).await, ); }, Commands::GraphListNodes => { - handle_response_result::<_, GraphListNodesResponse>( - client.graph_list_nodes(GraphListNodesRequest {}).await, - ); + handle_response_result(client.graph_list_nodes(GraphListNodesRequest {}).await); }, Commands::GraphGetNode { node_id } => { - handle_response_result::<_, GraphGetNodeResponse>( - client.graph_get_node(GraphGetNodeRequest { node_id }).await, + handle_response_result( + client + .graph_get_node(GraphGetNodeRequest { + node_id: parse_hex_33(&node_id, "node_id"), + }) + .await, ); }, Commands::Completions { .. } => unreachable!("Handled above"), @@ -1108,74 +1073,43 @@ fn build_open_channel_config( }) } -async fn fetch_paginated( - target_count: Option, initial_page_token: Option, - fetch_page: impl Fn(Option) -> Fut, - extract: impl Fn(R) -> (Vec, Option), -) -> Result, LdkServerError> -where - Fut: std::future::Future>, -{ - match target_count { - Some(count) => { - let mut items = Vec::with_capacity(count as usize); - let mut page_token = initial_page_token; - let mut next_page_token; - - loop { - let response = fetch_page(page_token).await?; - let (new_items, new_next_page_token) = extract(response); - items.extend(new_items); - next_page_token = new_next_page_token; - - if items.len() >= count as usize || next_page_token.is_none() { - break; - } - page_token = next_page_token; - } - - Ok(CliPaginatedResponse::new(items, next_page_token)) - }, - None => { - let response = fetch_page(initial_page_token).await?; - let (items, next_page_token) = extract(response); - Ok(CliPaginatedResponse::new(items, next_page_token)) +fn handle_response_result(response: Result) { + match response { + Ok(response) => match serde_json::to_string_pretty(&response) { + Ok(json) => println!("{json}"), + Err(e) => { + eprintln!("Error serializing response ({response:?}) to JSON: {e}"); + std::process::exit(1); + }, }, + Err(e) => handle_error(e), } } -fn handle_response_result(response: Result) -where - Rs: Into, - Js: Serialize + std::fmt::Debug, -{ - match response { - Ok(response) => { - let json_response: Js = response.into(); - match serde_json::to_string_pretty(&json_response) { - Ok(json) => println!("{json}"), - Err(e) => { - eprintln!("Error serializing response ({json_response:?}) to JSON: {e}"); - std::process::exit(1); - }, - } - }, - Err(e) => { - handle_error(e); - }, - } +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, @@ -1186,20 +1120,6 @@ fn parse_bolt11_invoice_description( } } -fn parse_page_token(token_str: &str) -> Result { - let parts: Vec<&str> = token_str.split(':').collect(); - if parts.len() != 2 { - return Err(LdkServerError::new( - InternalError, - "Page token must be in format 'token:index'".to_string(), - )); - } - let index = parts[1] - .parse::() - .map_err(|_| LdkServerError::new(InternalError, "Invalid page token index".to_string()))?; - Ok(PageToken { token: parts[0].to_string(), index }) -} - fn handle_error_msg(msg: &str) -> ! { eprintln!("Error: {msg}"); std::process::exit(1); @@ -1211,6 +1131,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..2a6a2cb8 100644 --- a/ldk-server-cli/src/types.rs +++ b/ldk-server-cli/src/types.rs @@ -16,33 +16,6 @@ use std::fmt; use std::str::FromStr; -use ldk_server_client::ldk_server_protos::types::{ForwardedPayment, PageToken, Payment}; -use serde::Serialize; - -/// CLI-specific wrapper for paginated responses that formats the page token -/// as "token:idx" instead of a JSON object. -#[derive(Debug, Clone, Serialize)] -pub struct CliPaginatedResponse { - /// List of items. - pub list: Vec, - /// Next page token formatted as "token:idx", or None if no more pages. - #[serde(skip_serializing_if = "Option::is_none")] - pub next_page_token: Option, -} - -impl CliPaginatedResponse { - pub fn new(list: Vec, next_page_token: Option) -> Self { - Self { list, next_page_token: next_page_token.map(format_page_token) } - } -} - -pub type CliListPaymentsResponse = CliPaginatedResponse; -pub type CliListForwardedPaymentsResponse = CliPaginatedResponse; - -fn format_page_token(token: PageToken) -> String { - format!("{}:{}", token.token, token.index) -} - /// A denomination-aware amount that stores its value internally in millisatoshis. /// /// Accepts the following formats when parsed from a string: 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..1d25ed2e --- /dev/null +++ b/ldk-server-json-models/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "ldk-server-json-models" +version = "0.1.0" +edition = "2021" + +[dependencies] +serde = { version = "1.0", features = ["derive"] } + +[dev-dependencies] +serde_json = "1.0" diff --git a/ldk-server-protos/src/api.rs b/ldk-server-json-models/src/api.rs similarity index 50% rename from ldk-server-protos/src/api.rs rename to ldk-server-json-models/src/api.rs index f72357d3..b3c5f849 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,21 @@ 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], + /// The payment ID for this on-chain payment, usable with `GetPaymentDetails`. + #[serde(with = "crate::serde_utils::hex_32")] + pub payment_id: [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 +115,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 +147,325 @@ 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, -} -/// 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)] -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, + #[serde(with = "crate::serde_utils::hex_32")] + pub payment_hash: [u8; 32], } +/// The response for the `Bolt11ReceiveForHash` API. Same shape as [`Bolt11ReceiveResponse`]. +pub type Bolt11ReceiveForHashResponse = Bolt11ReceiveResponse; /// 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, -} -/// 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)] -pub struct Bolt11ReceiveViaJitChannelResponse { - /// An invoice for a payment within the Lightning Network. - #[prost(string, tag = "1")] - pub invoice: ::prost::alloc::string::String, + pub max_total_lsp_fee_limit_msat: Option, } +/// The response for the `Bolt11ReceiveViaJitChannel` API. Same shape as [`Bolt11ReceiveResponse`]. +pub type Bolt11ReceiveViaJitChannelResponse = Bolt11ReceiveResponse; /// 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, -} -/// 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)] -pub struct Bolt11ReceiveVariableAmountViaJitChannelResponse { - /// An invoice for a payment within the Lightning Network. - #[prost(string, tag = "1")] - pub invoice: ::prost::alloc::string::String, + pub max_proportional_lsp_fee_limit_ppm_msat: Option, } +/// The response for the `Bolt11ReceiveVariableAmountViaJitChannel` API. Same shape as [`Bolt11ReceiveResponse`]. +pub type Bolt11ReceiveVariableAmountViaJitChannelResponse = Bolt11ReceiveResponse; /// 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 +473,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 +494,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 +506,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 +527,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 +605,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 +614,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 +699,54 @@ 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)] -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, -} -/// 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), - } +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum UnifiedSendResponse { + /// An on-chain payment was made. + Onchain { + /// The transaction ID of the broadcasted transaction. + #[serde(with = "crate::serde_utils::hex_32")] + txid: [u8; 32], + /// The payment ID for this on-chain payment, usable with `GetPaymentDetails`. + #[serde(with = "crate::serde_utils::hex_32")] + payment_id: [u8; 32], + }, + /// A BOLT11 payment was made. + Bolt11 { + /// An identifier used to uniquely identify a payment in hex-encoded form. + #[serde(with = "crate::serde_utils::hex_32")] + payment_id: [u8; 32], + }, + /// A BOLT12 payment was made. + Bolt12 { + /// An identifier used to uniquely identify a payment in hex-encoded form. + #[serde(with = "crate::serde_utils::hex_32")] + payment_id: [u8; 32], + }, } /// 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..44cd4d93 --- /dev/null +++ b/ldk-server-json-models/src/error.rs @@ -0,0 +1,88 @@ +// 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)] +#[serde(rename_all = "snake_case")] +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, +} + +#[cfg(test)] +mod tests { + use super::{ErrorCode, ErrorResponse}; + + #[test] + fn error_response_serializes_error_code_in_snake_case() { + let response = ErrorResponse { + message: "bad request".to_string(), + error_code: ErrorCode::InvalidRequestError, + }; + + let value = serde_json::to_value(response).unwrap(); + + assert_eq!(value["error_code"], "invalid_request_error"); + } + + #[test] + fn error_response_roundtrip() { + let err = ErrorResponse { + message: "something went wrong".into(), + error_code: ErrorCode::InternalServerError, + }; + let json = serde_json::to_value(&err).unwrap(); + assert_eq!(json["error_code"], "internal_server_error"); + let back: ErrorResponse = serde_json::from_value(json).unwrap(); + assert_eq!(back, err); + } + + #[test] + fn error_code_all_variants() { + for (variant, expected) in [ + (ErrorCode::UnknownError, "unknown_error"), + (ErrorCode::InvalidRequestError, "invalid_request_error"), + (ErrorCode::AuthError, "auth_error"), + (ErrorCode::LightningError, "lightning_error"), + (ErrorCode::InternalServerError, "internal_server_error"), + ] { + let json = serde_json::to_value(&variant).unwrap(); + assert_eq!(json, expected); + let back: ErrorCode = serde_json::from_value(json).unwrap(); + assert_eq!(back, variant); + } + } +} 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..47defcf8 --- /dev/null +++ b/ldk-server-json-models/src/serde_utils.rs @@ -0,0 +1,139 @@ +// 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_fixed::<32>(&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_fixed::<32>(&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())) +} diff --git a/ldk-server-json-models/src/types.rs b/ldk-server-json-models/src/types.rs new file mode 100644 index 00000000..5d8ddabd --- /dev/null +++ b/ldk-server-json-models/src/types.rs @@ -0,0 +1,750 @@ +// 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, +} +#[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-json-models/tests/serialization.rs b/ldk-server-json-models/tests/serialization.rs new file mode 100644 index 00000000..133fe593 --- /dev/null +++ b/ldk-server-json-models/tests/serialization.rs @@ -0,0 +1,1200 @@ +// 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. + +//! Serialization round-trip tests for all JSON models. +//! +//! Each test constructs a value, serializes it to JSON, deserializes back, +//! and asserts equality. Key serde attributes (hex encoding, rename_all, +//! tagged enums) are spot-checked against the raw JSON. + +use std::fmt::Debug; + +use ldk_server_json_models::api::*; +use ldk_server_json_models::events::*; +use ldk_server_json_models::types::*; +use serde::de::DeserializeOwned; +use serde::Serialize; + +/// Serialize to JSON and back, asserting round-trip equality. +fn roundtrip(value: &T) -> serde_json::Value { + let json = serde_json::to_value(value).expect("serialize"); + let back: T = serde_json::from_value(json.clone()).expect("deserialize"); + assert_eq!(&back, value, "round-trip mismatch"); + json +} + +// --------------------------------------------------------------------------- +// Test data helpers +// --------------------------------------------------------------------------- + +const HASH_32: [u8; 32] = [ + 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, + 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, +]; +const HASH_32_HEX: &str = "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"; + +const HASH_32_B: [u8; 32] = [0x11; 32]; +const HASH_32_B_HEX: &str = "1111111111111111111111111111111111111111111111111111111111111111"; + +const HASH_32_C: [u8; 32] = [0x22; 32]; + +const PUBKEY_33: [u8; 33] = [ + 0x02, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, + 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, + 0x89, +]; +const PUBKEY_33_HEX: &str = "02abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"; + +const PUBKEY_33_B: [u8; 33] = [0x03; 33]; + +fn sample_payment() -> Payment { + Payment { + id: HASH_32, + kind: PaymentKind::Bolt11(Bolt11 { + hash: HASH_32_B, + preimage: Some(HASH_32_C), + secret: None, + }), + amount_msat: Some(100_000), + fee_paid_msat: Some(10), + direction: PaymentDirection::Outbound, + status: PaymentStatus::Succeeded, + latest_update_timestamp: 1_700_000_000, + } +} + +fn sample_forwarded_payment() -> ForwardedPayment { + ForwardedPayment { + prev_channel_id: HASH_32, + next_channel_id: HASH_32_B, + prev_user_channel_id: "abc123".into(), + prev_node_id: PUBKEY_33, + next_node_id: PUBKEY_33_B, + next_user_channel_id: Some("def456".into()), + total_fee_earned_msat: Some(500), + skimmed_fee_msat: None, + claim_from_onchain_tx: false, + outbound_amount_forwarded_msat: Some(99_500), + } +} + +fn sample_channel_config() -> ChannelConfig { + ChannelConfig { + forwarding_fee_proportional_millionths: Some(100), + forwarding_fee_base_msat: Some(1000), + cltv_expiry_delta: Some(144), + force_close_avoidance_max_fee_satoshis: Some(1000), + accept_underpaying_htlcs: Some(false), + max_dust_htlc_exposure: Some(MaxDustHtlcExposure::FixedLimitMsat(5_000_000)), + } +} + +fn sample_channel() -> Channel { + Channel { + channel_id: HASH_32, + counterparty_node_id: PUBKEY_33, + funding_txo: Some(OutPoint { txid: HASH_32_B, vout: 0 }), + user_channel_id: "user123".into(), + unspendable_punishment_reserve: Some(1000), + channel_value_sats: 1_000_000, + feerate_sat_per_1000_weight: 253, + outbound_capacity_msat: 500_000_000, + inbound_capacity_msat: 400_000_000, + confirmations_required: Some(3), + confirmations: Some(6), + is_outbound: true, + is_channel_ready: true, + is_usable: true, + is_announced: false, + channel_config: Some(sample_channel_config()), + next_outbound_htlc_limit_msat: 450_000_000, + next_outbound_htlc_minimum_msat: 1000, + force_close_spend_delay: Some(144), + counterparty_outbound_htlc_minimum_msat: Some(1000), + counterparty_outbound_htlc_maximum_msat: Some(450_000_000), + counterparty_unspendable_punishment_reserve: 1000, + counterparty_forwarding_info_fee_base_msat: Some(1000), + counterparty_forwarding_info_fee_proportional_millionths: Some(100), + counterparty_forwarding_info_cltv_expiry_delta: Some(40), + } +} + +fn sample_route_params() -> RouteParametersConfig { + RouteParametersConfig { + max_total_routing_fee_msat: Some(5000), + max_total_cltv_expiry_delta: 1008, + max_path_count: 10, + max_channel_saturation_power_of_half: 2, + } +} + +// =========================================================================== +// types.rs +// =========================================================================== + +#[test] +fn payment_bolt11_roundtrip() { + let p = sample_payment(); + let json = roundtrip(&p); + assert_eq!(json["id"], HASH_32_HEX); + assert_eq!(json["direction"], "outbound"); + assert_eq!(json["status"], "succeeded"); + assert!(json["kind"]["bolt11"].is_object()); + assert_eq!(json["kind"]["bolt11"]["hash"], HASH_32_B_HEX); +} + +#[test] +fn payment_onchain_confirmed_roundtrip() { + let p = Payment { + id: HASH_32, + kind: PaymentKind::Onchain(Onchain { + txid: HASH_32_B, + status: ConfirmationStatus::Confirmed(Confirmed { + block_hash: HASH_32_C, + height: 800_000, + timestamp: 1_700_000_000, + }), + }), + amount_msat: Some(50_000_000), + fee_paid_msat: None, + direction: PaymentDirection::Inbound, + status: PaymentStatus::Succeeded, + latest_update_timestamp: 1_700_000_000, + }; + let json = roundtrip(&p); + assert!(json["kind"]["onchain"]["status"]["confirmed"].is_object()); + assert_eq!(json["direction"], "inbound"); +} + +#[test] +fn payment_onchain_unconfirmed_roundtrip() { + let p = Payment { + id: HASH_32, + kind: PaymentKind::Onchain(Onchain { + txid: HASH_32_B, + status: ConfirmationStatus::Unconfirmed(Unconfirmed {}), + }), + amount_msat: None, + fee_paid_msat: None, + direction: PaymentDirection::Outbound, + status: PaymentStatus::Pending, + latest_update_timestamp: 1_700_000_000, + }; + let json = roundtrip(&p); + assert!(json["kind"]["onchain"]["status"]["unconfirmed"].is_object()); + assert_eq!(json["status"], "pending"); +} + +#[test] +fn payment_bolt11_jit_roundtrip() { + let p = Payment { + id: HASH_32, + kind: PaymentKind::Bolt11Jit(Bolt11Jit { + hash: HASH_32_B, + preimage: None, + secret: Some(HASH_32_C), + lsp_fee_limits: Some(LspFeeLimits { + max_total_opening_fee_msat: Some(10_000), + max_proportional_opening_fee_ppm_msat: Some(5000), + }), + counterparty_skimmed_fee_msat: Some(100), + }), + amount_msat: Some(100_000), + fee_paid_msat: None, + direction: PaymentDirection::Inbound, + status: PaymentStatus::Pending, + latest_update_timestamp: 1_700_000_000, + }; + let json = roundtrip(&p); + assert!(json["kind"]["bolt11_jit"].is_object()); +} + +#[test] +fn payment_bolt12_offer_roundtrip() { + let p = Payment { + id: HASH_32, + kind: PaymentKind::Bolt12Offer(Bolt12Offer { + hash: Some(HASH_32_B), + preimage: None, + secret: None, + offer_id: HASH_32_C, + payer_note: Some("thanks".into()), + quantity: Some(2), + }), + amount_msat: Some(200_000), + fee_paid_msat: Some(20), + direction: PaymentDirection::Outbound, + status: PaymentStatus::Succeeded, + latest_update_timestamp: 1_700_000_000, + }; + let json = roundtrip(&p); + assert!(json["kind"]["bolt12_offer"].is_object()); + assert_eq!(json["kind"]["bolt12_offer"]["payer_note"], "thanks"); +} + +#[test] +fn payment_bolt12_refund_roundtrip() { + let p = Payment { + id: HASH_32, + kind: PaymentKind::Bolt12Refund(Bolt12Refund { + hash: None, + preimage: None, + secret: None, + payer_note: None, + quantity: None, + }), + amount_msat: None, + fee_paid_msat: None, + direction: PaymentDirection::Inbound, + status: PaymentStatus::Failed, + latest_update_timestamp: 1_700_000_000, + }; + let json = roundtrip(&p); + assert!(json["kind"]["bolt12_refund"].is_object()); + assert_eq!(json["status"], "failed"); +} + +#[test] +fn payment_spontaneous_roundtrip() { + let p = Payment { + id: HASH_32, + kind: PaymentKind::Spontaneous(Spontaneous { hash: HASH_32_B, preimage: Some(HASH_32_C) }), + amount_msat: Some(50_000), + fee_paid_msat: None, + direction: PaymentDirection::Inbound, + status: PaymentStatus::Succeeded, + latest_update_timestamp: 1_700_000_000, + }; + let json = roundtrip(&p); + assert!(json["kind"]["spontaneous"].is_object()); +} + +#[test] +fn forwarded_payment_roundtrip() { + let fp = sample_forwarded_payment(); + let json = roundtrip(&fp); + assert_eq!(json["prev_channel_id"], HASH_32_HEX); + assert_eq!(json["prev_node_id"], PUBKEY_33_HEX); + assert_eq!(json["claim_from_onchain_tx"], false); +} + +#[test] +fn channel_roundtrip() { + let ch = sample_channel(); + let json = roundtrip(&ch); + assert_eq!(json["channel_id"], HASH_32_HEX); + assert_eq!(json["counterparty_node_id"], PUBKEY_33_HEX); + assert_eq!(json["is_usable"], true); + assert!(json["funding_txo"].is_object()); + assert!(json["channel_config"].is_object()); +} + +#[test] +fn channel_config_roundtrip() { + let cfg = sample_channel_config(); + let json = roundtrip(&cfg); + assert!(json["max_dust_htlc_exposure"]["fixed_limit_msat"].is_number()); +} + +#[test] +fn max_dust_htlc_exposure_variants() { + let fixed = MaxDustHtlcExposure::FixedLimitMsat(5_000_000); + let json = roundtrip(&fixed); + assert_eq!(json["fixed_limit_msat"], 5_000_000); + + let rate = MaxDustHtlcExposure::FeeRateMultiplier(1000); + let json = roundtrip(&rate); + assert_eq!(json["fee_rate_multiplier"], 1000); +} + +#[test] +fn outpoint_roundtrip() { + let op = OutPoint { txid: HASH_32, vout: 1 }; + let json = roundtrip(&op); + assert_eq!(json["txid"], HASH_32_HEX); + assert_eq!(json["vout"], 1); +} + +#[test] +fn best_block_roundtrip() { + let bb = BestBlock { block_hash: HASH_32, height: 800_000 }; + let json = roundtrip(&bb); + assert_eq!(json["block_hash"], HASH_32_HEX); + assert_eq!(json["height"], 800_000); +} + +#[test] +fn confirmation_status_variants() { + let confirmed = ConfirmationStatus::Confirmed(Confirmed { + block_hash: HASH_32, + height: 800_000, + timestamp: 1_700_000_000, + }); + let json = roundtrip(&confirmed); + assert!(json["confirmed"].is_object()); + + let unconfirmed = ConfirmationStatus::Unconfirmed(Unconfirmed {}); + let json = roundtrip(&unconfirmed); + assert!(json["unconfirmed"].is_object()); +} + +#[test] +fn lightning_balance_claimable_on_channel_close() { + let bal = LightningBalance::ClaimableOnChannelClose(ClaimableOnChannelClose { + channel_id: HASH_32, + counterparty_node_id: PUBKEY_33, + amount_satoshis: 500_000, + transaction_fee_satoshis: 300, + outbound_payment_htlc_rounded_msat: 100, + outbound_forwarded_htlc_rounded_msat: 200, + inbound_claiming_htlc_rounded_msat: 50, + inbound_htlc_rounded_msat: 25, + }); + let json = roundtrip(&bal); + assert!(json["claimable_on_channel_close"].is_object()); + assert_eq!(json["claimable_on_channel_close"]["channel_id"], HASH_32_HEX); +} + +#[test] +fn lightning_balance_claimable_awaiting_confirmations() { + let bal = LightningBalance::ClaimableAwaitingConfirmations(ClaimableAwaitingConfirmations { + channel_id: HASH_32, + counterparty_node_id: PUBKEY_33, + amount_satoshis: 100_000, + confirmation_height: 800_100, + source: BalanceSource::HolderForceClosed, + }); + let json = roundtrip(&bal); + assert!(json["claimable_awaiting_confirmations"].is_object()); + assert_eq!(json["claimable_awaiting_confirmations"]["source"], "holder_force_closed"); +} + +#[test] +fn lightning_balance_contentious_claimable() { + let bal = LightningBalance::ContentiousClaimable(ContentiousClaimable { + channel_id: HASH_32, + counterparty_node_id: PUBKEY_33, + amount_satoshis: 50_000, + timeout_height: 800_200, + payment_hash: HASH_32_B, + payment_preimage: HASH_32_C, + }); + let json = roundtrip(&bal); + assert!(json["contentious_claimable"].is_object()); +} + +#[test] +fn lightning_balance_maybe_timeout_claimable() { + let bal = LightningBalance::MaybeTimeoutClaimableHtlc(MaybeTimeoutClaimableHtlc { + channel_id: HASH_32, + counterparty_node_id: PUBKEY_33, + amount_satoshis: 25_000, + claimable_height: 800_300, + payment_hash: HASH_32_B, + outbound_payment: true, + }); + let json = roundtrip(&bal); + assert!(json["maybe_timeout_claimable_htlc"].is_object()); +} + +#[test] +fn lightning_balance_maybe_preimage_claimable() { + let bal = LightningBalance::MaybePreimageClaimableHtlc(MaybePreimageClaimableHtlc { + channel_id: HASH_32, + counterparty_node_id: PUBKEY_33, + amount_satoshis: 10_000, + expiry_height: 800_400, + payment_hash: HASH_32_B, + }); + let json = roundtrip(&bal); + assert!(json["maybe_preimage_claimable_htlc"].is_object()); +} + +#[test] +fn lightning_balance_counterparty_revoked() { + let bal = + LightningBalance::CounterpartyRevokedOutputClaimable(CounterpartyRevokedOutputClaimable { + channel_id: HASH_32, + counterparty_node_id: PUBKEY_33, + amount_satoshis: 75_000, + }); + let json = roundtrip(&bal); + assert!(json["counterparty_revoked_output_claimable"].is_object()); +} + +#[test] +fn balance_source_variants() { + for (variant, expected) in [ + (BalanceSource::HolderForceClosed, "holder_force_closed"), + (BalanceSource::CounterpartyForceClosed, "counterparty_force_closed"), + (BalanceSource::CoopClose, "coop_close"), + (BalanceSource::Htlc, "htlc"), + ] { + let json = serde_json::to_value(&variant).unwrap(); + assert_eq!(json, expected); + let back: BalanceSource = serde_json::from_value(json).unwrap(); + assert_eq!(back, variant); + } +} + +#[test] +fn pending_sweep_balance_pending_broadcast() { + let bal = PendingSweepBalance::PendingBroadcast(PendingBroadcast { + channel_id: Some(HASH_32), + amount_satoshis: 50_000, + }); + let json = roundtrip(&bal); + assert!(json["pending_broadcast"].is_object()); + assert_eq!(json["pending_broadcast"]["channel_id"], HASH_32_HEX); +} + +#[test] +fn pending_sweep_balance_pending_broadcast_no_channel() { + let bal = PendingSweepBalance::PendingBroadcast(PendingBroadcast { + channel_id: None, + amount_satoshis: 50_000, + }); + let json = roundtrip(&bal); + assert!(json["pending_broadcast"]["channel_id"].is_null()); +} + +#[test] +fn pending_sweep_balance_broadcast_awaiting_confirmation() { + let bal = PendingSweepBalance::BroadcastAwaitingConfirmation(BroadcastAwaitingConfirmation { + channel_id: Some(HASH_32), + latest_broadcast_height: 800_000, + latest_spending_txid: HASH_32_B, + amount_satoshis: 50_000, + }); + let json = roundtrip(&bal); + assert!(json["broadcast_awaiting_confirmation"].is_object()); +} + +#[test] +fn pending_sweep_balance_awaiting_threshold() { + let bal = PendingSweepBalance::AwaitingThresholdConfirmations(AwaitingThresholdConfirmations { + channel_id: None, + latest_spending_txid: HASH_32, + confirmation_hash: HASH_32_B, + confirmation_height: 800_010, + amount_satoshis: 50_000, + }); + let json = roundtrip(&bal); + assert!(json["awaiting_threshold_confirmations"].is_object()); +} + +#[test] +fn bolt11_invoice_description_variants() { + let direct = Bolt11InvoiceDescription::Direct("coffee".into()); + let json = roundtrip(&direct); + assert_eq!(json["direct"], "coffee"); + + let hash = Bolt11InvoiceDescription::Hash("abc123".into()); + let json = roundtrip(&hash); + assert_eq!(json["hash"], "abc123"); +} + +#[test] +fn route_parameters_config_roundtrip() { + let rp = sample_route_params(); + let json = roundtrip(&rp); + assert_eq!(json["max_total_cltv_expiry_delta"], 1008); + assert_eq!(json["max_path_count"], 10); +} + +#[test] +fn graph_routing_fees_roundtrip() { + let fees = GraphRoutingFees { base_msat: 1000, proportional_millionths: 100 }; + roundtrip(&fees); +} + +#[test] +fn graph_channel_update_roundtrip() { + let update = GraphChannelUpdate { + last_update: 1_700_000_000, + enabled: true, + cltv_expiry_delta: 144, + htlc_minimum_msat: 1000, + htlc_maximum_msat: 1_000_000_000, + fees: GraphRoutingFees { base_msat: 1000, proportional_millionths: 100 }, + }; + roundtrip(&update); +} + +#[test] +fn graph_channel_roundtrip() { + let ch = GraphChannel { + node_one: PUBKEY_33, + node_two: PUBKEY_33_B, + capacity_sats: Some(1_000_000), + one_to_two: Some(GraphChannelUpdate { + last_update: 1_700_000_000, + enabled: true, + cltv_expiry_delta: 144, + htlc_minimum_msat: 1000, + htlc_maximum_msat: 1_000_000_000, + fees: GraphRoutingFees { base_msat: 1000, proportional_millionths: 100 }, + }), + two_to_one: None, + }; + let json = roundtrip(&ch); + assert_eq!(json["node_one"], PUBKEY_33_HEX); +} + +#[test] +fn graph_node_announcement_roundtrip() { + let ann = GraphNodeAnnouncement { + last_update: 1_700_000_000, + alias: "my-node".into(), + rgb: "ff6600".into(), + addresses: vec!["127.0.0.1:9735".into()], + }; + roundtrip(&ann); +} + +#[test] +fn graph_node_roundtrip() { + let node = GraphNode { + channels: vec![123456789, 987654321], + announcement_info: Some(GraphNodeAnnouncement { + last_update: 1_700_000_000, + alias: "test-node".into(), + rgb: "aabbcc".into(), + addresses: vec![], + }), + }; + roundtrip(&node); +} + +#[test] +fn peer_roundtrip() { + let peer = Peer { + node_id: PUBKEY_33, + address: "127.0.0.1:9735".into(), + is_persisted: true, + is_connected: true, + }; + let json = roundtrip(&peer); + assert_eq!(json["node_id"], PUBKEY_33_HEX); +} + +#[test] +fn payment_direction_variants() { + for (variant, expected) in + [(PaymentDirection::Inbound, "inbound"), (PaymentDirection::Outbound, "outbound")] + { + let json = serde_json::to_value(&variant).unwrap(); + assert_eq!(json, expected); + let back: PaymentDirection = serde_json::from_value(json).unwrap(); + assert_eq!(back, variant); + } +} + +#[test] +fn payment_status_variants() { + for (variant, expected) in [ + (PaymentStatus::Pending, "pending"), + (PaymentStatus::Succeeded, "succeeded"), + (PaymentStatus::Failed, "failed"), + ] { + let json = serde_json::to_value(&variant).unwrap(); + assert_eq!(json, expected); + let back: PaymentStatus = serde_json::from_value(json).unwrap(); + assert_eq!(back, variant); + } +} + +#[test] +fn lsp_fee_limits_roundtrip() { + let limits = LspFeeLimits { + max_total_opening_fee_msat: Some(10_000), + max_proportional_opening_fee_ppm_msat: None, + }; + roundtrip(&limits); +} + +// =========================================================================== +// events.rs +// =========================================================================== + +#[test] +fn event_payment_received() { + let ev = Event::PaymentReceived(PaymentReceived { payment: sample_payment() }); + let json = roundtrip(&ev); + assert!(json["payment_received"].is_object()); + assert!(json["payment_received"]["payment"].is_object()); +} + +#[test] +fn event_payment_successful() { + let ev = Event::PaymentSuccessful(PaymentSuccessful { payment: sample_payment() }); + let json = roundtrip(&ev); + assert!(json["payment_successful"].is_object()); +} + +#[test] +fn event_payment_failed() { + let ev = Event::PaymentFailed(PaymentFailed { payment: sample_payment() }); + let json = roundtrip(&ev); + assert!(json["payment_failed"].is_object()); +} + +#[test] +fn event_payment_claimable() { + let ev = Event::PaymentClaimable(PaymentClaimable { payment: sample_payment() }); + let json = roundtrip(&ev); + assert!(json["payment_claimable"].is_object()); +} + +#[test] +fn event_payment_forwarded() { + let ev = + Event::PaymentForwarded(PaymentForwarded { forwarded_payment: sample_forwarded_payment() }); + let json = roundtrip(&ev); + assert!(json["payment_forwarded"].is_object()); + assert!(json["payment_forwarded"]["forwarded_payment"].is_object()); +} + +// =========================================================================== +// api.rs +// =========================================================================== + +#[test] +fn get_node_info_roundtrip() { + roundtrip(&GetNodeInfoRequest {}); + let resp = GetNodeInfoResponse { + node_id: PUBKEY_33, + current_best_block: BestBlock { block_hash: HASH_32, height: 800_000 }, + latest_lightning_wallet_sync_timestamp: Some(1_700_000_000), + latest_onchain_wallet_sync_timestamp: Some(1_700_000_000), + latest_fee_rate_cache_update_timestamp: None, + latest_rgs_snapshot_timestamp: None, + latest_node_announcement_broadcast_timestamp: None, + listening_addresses: vec!["0.0.0.0:9735".into()], + announcement_addresses: vec![], + node_alias: Some("my-node".into()), + node_uris: vec![], + }; + let json = roundtrip(&resp); + assert_eq!(json["node_id"], PUBKEY_33_HEX); +} + +#[test] +fn onchain_receive_roundtrip() { + roundtrip(&OnchainReceiveRequest {}); + roundtrip(&OnchainReceiveResponse { address: "bc1qtest".into() }); +} + +#[test] +fn onchain_send_roundtrip() { + let req = OnchainSendRequest { + address: "bc1qtest".into(), + amount_sats: Some(100_000), + send_all: None, + fee_rate_sat_per_vb: Some(5), + }; + roundtrip(&req); + + let resp = OnchainSendResponse { txid: HASH_32, payment_id: HASH_32_B }; + let json = roundtrip(&resp); + assert_eq!(json["txid"], HASH_32_HEX); + assert_eq!(json["payment_id"], HASH_32_B_HEX); +} + +#[test] +fn onchain_send_all_roundtrip() { + let req = OnchainSendRequest { + address: "bc1qtest".into(), + amount_sats: None, + send_all: Some(true), + fee_rate_sat_per_vb: None, + }; + roundtrip(&req); +} + +#[test] +fn bolt11_receive_roundtrip() { + let req = Bolt11ReceiveRequest { + amount_msat: Some(100_000), + description: Some(Bolt11InvoiceDescription::Direct("test".into())), + expiry_secs: 3600, + }; + roundtrip(&req); + + let resp = Bolt11ReceiveResponse { + invoice: "lnbc1...".into(), + payment_hash: HASH_32, + payment_secret: HASH_32_B, + }; + let json = roundtrip(&resp); + assert_eq!(json["payment_hash"], HASH_32_HEX); +} + +#[test] +fn bolt11_receive_variable_amount_roundtrip() { + let req = Bolt11ReceiveRequest { amount_msat: None, description: None, expiry_secs: 3600 }; + roundtrip(&req); +} + +#[test] +fn bolt11_receive_for_hash_roundtrip() { + let req = Bolt11ReceiveForHashRequest { + amount_msat: Some(50_000), + description: Some(Bolt11InvoiceDescription::Hash("deadbeef".into())), + expiry_secs: 1800, + payment_hash: HASH_32, + }; + let json = roundtrip(&req); + assert_eq!(json["payment_hash"], HASH_32_HEX); +} + +#[test] +fn bolt11_claim_for_hash_roundtrip() { + let req = Bolt11ClaimForHashRequest { + payment_hash: Some(HASH_32), + claimable_amount_msat: Some(100_000), + preimage: HASH_32_B, + }; + roundtrip(&req); + roundtrip(&Bolt11ClaimForHashResponse {}); +} + +#[test] +fn bolt11_fail_for_hash_roundtrip() { + let req = Bolt11FailForHashRequest { payment_hash: HASH_32 }; + let json = roundtrip(&req); + assert_eq!(json["payment_hash"], HASH_32_HEX); + roundtrip(&Bolt11FailForHashResponse {}); +} + +#[test] +fn bolt11_receive_via_jit_channel_roundtrip() { + let req = Bolt11ReceiveViaJitChannelRequest { + amount_msat: 1_000_000, + description: Some(Bolt11InvoiceDescription::Direct("jit test".into())), + expiry_secs: 3600, + max_total_lsp_fee_limit_msat: Some(5000), + }; + roundtrip(&req); +} + +#[test] +fn bolt11_receive_variable_amount_via_jit_channel_roundtrip() { + let req = Bolt11ReceiveVariableAmountViaJitChannelRequest { + description: None, + expiry_secs: 3600, + max_proportional_lsp_fee_limit_ppm_msat: Some(1000), + }; + roundtrip(&req); +} + +#[test] +fn bolt11_send_roundtrip() { + let req = Bolt11SendRequest { + invoice: "lnbc1...".into(), + amount_msat: Some(100_000), + route_parameters: Some(sample_route_params()), + }; + roundtrip(&req); + + let resp = Bolt11SendResponse { payment_id: HASH_32 }; + let json = roundtrip(&resp); + assert_eq!(json["payment_id"], HASH_32_HEX); +} + +#[test] +fn bolt12_receive_roundtrip() { + let req = Bolt12ReceiveRequest { + description: "test offer".into(), + amount_msat: Some(500_000), + expiry_secs: Some(86400), + quantity: Some(1), + }; + roundtrip(&req); + + let resp = Bolt12ReceiveResponse { offer: "lno1...".into(), offer_id: HASH_32 }; + let json = roundtrip(&resp); + assert_eq!(json["offer_id"], HASH_32_HEX); +} + +#[test] +fn bolt12_send_roundtrip() { + let req = Bolt12SendRequest { + offer: "lno1...".into(), + amount_msat: Some(500_000), + quantity: Some(1), + payer_note: Some("thanks".into()), + route_parameters: None, + }; + roundtrip(&req); + + let resp = Bolt12SendResponse { payment_id: HASH_32 }; + roundtrip(&resp); +} + +#[test] +fn spontaneous_send_roundtrip() { + let req = SpontaneousSendRequest { + amount_msat: 100_000, + node_id: PUBKEY_33, + route_parameters: Some(sample_route_params()), + }; + let json = roundtrip(&req); + assert_eq!(json["node_id"], PUBKEY_33_HEX); + + let resp = SpontaneousSendResponse { payment_id: HASH_32 }; + roundtrip(&resp); +} + +#[test] +fn open_channel_roundtrip() { + let req = OpenChannelRequest { + node_pubkey: PUBKEY_33, + address: "127.0.0.1:9735".into(), + channel_amount_sats: 1_000_000, + push_to_counterparty_msat: Some(10_000), + channel_config: Some(sample_channel_config()), + announce_channel: true, + }; + let json = roundtrip(&req); + assert_eq!(json["node_pubkey"], PUBKEY_33_HEX); + + roundtrip(&OpenChannelResponse { user_channel_id: "abc123".into() }); +} + +#[test] +fn splice_in_roundtrip() { + let req = SpliceInRequest { + user_channel_id: "abc123".into(), + counterparty_node_id: PUBKEY_33, + splice_amount_sats: 500_000, + }; + roundtrip(&req); + roundtrip(&SpliceInResponse {}); +} + +#[test] +fn splice_out_roundtrip() { + let req = SpliceOutRequest { + user_channel_id: "abc123".into(), + counterparty_node_id: PUBKEY_33, + address: Some("bc1qtest".into()), + splice_amount_sats: 200_000, + }; + roundtrip(&req); + roundtrip(&SpliceOutResponse { address: "bc1qtest".into() }); +} + +#[test] +fn update_channel_config_roundtrip() { + let req = UpdateChannelConfigRequest { + user_channel_id: "abc123".into(), + counterparty_node_id: PUBKEY_33, + channel_config: Some(sample_channel_config()), + }; + roundtrip(&req); + roundtrip(&UpdateChannelConfigResponse {}); +} + +#[test] +fn close_channel_roundtrip() { + let req = + CloseChannelRequest { user_channel_id: "abc123".into(), counterparty_node_id: PUBKEY_33 }; + let json = roundtrip(&req); + assert_eq!(json["counterparty_node_id"], PUBKEY_33_HEX); + roundtrip(&CloseChannelResponse {}); +} + +#[test] +fn force_close_channel_roundtrip() { + let req = ForceCloseChannelRequest { + user_channel_id: "abc123".into(), + counterparty_node_id: PUBKEY_33, + force_close_reason: Some("unresponsive".into()), + }; + roundtrip(&req); + roundtrip(&ForceCloseChannelResponse {}); +} + +#[test] +fn list_channels_roundtrip() { + roundtrip(&ListChannelsRequest {}); + let resp = ListChannelsResponse { channels: vec![sample_channel()] }; + roundtrip(&resp); +} + +#[test] +fn get_payment_details_roundtrip() { + let req = GetPaymentDetailsRequest { payment_id: HASH_32 }; + let json = roundtrip(&req); + assert_eq!(json["payment_id"], HASH_32_HEX); + + let resp_some = GetPaymentDetailsResponse { payment: Some(sample_payment()) }; + roundtrip(&resp_some); + + let resp_none = GetPaymentDetailsResponse { payment: None }; + roundtrip(&resp_none); +} + +#[test] +fn list_payments_roundtrip() { + let req = ListPaymentsRequest { page_token: Some("token123".into()) }; + roundtrip(&req); + + let resp = ListPaymentsResponse { + payments: vec![sample_payment()], + next_page_token: Some("next_token".into()), + }; + roundtrip(&resp); +} + +#[test] +fn list_forwarded_payments_roundtrip() { + let req = ListForwardedPaymentsRequest { page_token: None }; + roundtrip(&req); + + let resp = ListForwardedPaymentsResponse { + forwarded_payments: vec![sample_forwarded_payment()], + next_page_token: None, + }; + roundtrip(&resp); +} + +#[test] +fn sign_message_roundtrip() { + let req = SignMessageRequest { message: vec![0x48, 0x65, 0x6c, 0x6c, 0x6f] }; + let json = roundtrip(&req); + assert_eq!(json["message"], "48656c6c6f"); // "Hello" in hex + + let resp = SignMessageResponse { signature: "d2mea3...".into() }; + roundtrip(&resp); +} + +#[test] +fn verify_signature_roundtrip() { + let req = VerifySignatureRequest { + message: vec![0x48, 0x69], + signature: "d2mea3...".into(), + public_key: PUBKEY_33, + }; + let json = roundtrip(&req); + assert_eq!(json["public_key"], PUBKEY_33_HEX); + + let resp = VerifySignatureResponse { valid: true }; + let json = roundtrip(&resp); + assert_eq!(json["valid"], true); +} + +#[test] +fn export_pathfinding_scores_roundtrip() { + roundtrip(&ExportPathfindingScoresRequest {}); + + let resp = ExportPathfindingScoresResponse { scores: vec![0xde, 0xad, 0xbe, 0xef] }; + let json = roundtrip(&resp); + assert_eq!(json["scores"], "deadbeef"); +} + +#[test] +fn get_balances_roundtrip() { + roundtrip(&GetBalancesRequest {}); + + let resp = GetBalancesResponse { + total_onchain_balance_sats: 1_000_000, + spendable_onchain_balance_sats: 900_000, + total_anchor_channels_reserve_sats: 100_000, + total_lightning_balance_sats: 500_000, + lightning_balances: vec![LightningBalance::ClaimableOnChannelClose( + ClaimableOnChannelClose { + channel_id: HASH_32, + counterparty_node_id: PUBKEY_33, + amount_satoshis: 500_000, + transaction_fee_satoshis: 300, + outbound_payment_htlc_rounded_msat: 0, + outbound_forwarded_htlc_rounded_msat: 0, + inbound_claiming_htlc_rounded_msat: 0, + inbound_htlc_rounded_msat: 0, + }, + )], + pending_balances_from_channel_closures: vec![PendingSweepBalance::PendingBroadcast( + PendingBroadcast { channel_id: Some(HASH_32), amount_satoshis: 50_000 }, + )], + }; + roundtrip(&resp); +} + +#[test] +fn connect_peer_roundtrip() { + let req = ConnectPeerRequest { + node_pubkey: PUBKEY_33, + address: "127.0.0.1:9735".into(), + persist: true, + }; + let json = roundtrip(&req); + assert_eq!(json["node_pubkey"], PUBKEY_33_HEX); + roundtrip(&ConnectPeerResponse {}); +} + +#[test] +fn disconnect_peer_roundtrip() { + let req = DisconnectPeerRequest { node_pubkey: PUBKEY_33 }; + let json = roundtrip(&req); + assert_eq!(json["node_pubkey"], PUBKEY_33_HEX); + roundtrip(&DisconnectPeerResponse {}); +} + +#[test] +fn list_peers_roundtrip() { + roundtrip(&ListPeersRequest {}); + + let resp = ListPeersResponse { + peers: vec![Peer { + node_id: PUBKEY_33, + address: "127.0.0.1:9735".into(), + is_persisted: true, + is_connected: false, + }], + }; + roundtrip(&resp); +} + +#[test] +fn graph_list_channels_roundtrip() { + roundtrip(&GraphListChannelsRequest {}); + roundtrip(&GraphListChannelsResponse { short_channel_ids: vec![123456789, 987654321] }); +} + +#[test] +fn graph_get_channel_roundtrip() { + let req = GraphGetChannelRequest { short_channel_id: 123456789 }; + roundtrip(&req); + + let resp = GraphGetChannelResponse { + channel: Some(GraphChannel { + node_one: PUBKEY_33, + node_two: PUBKEY_33_B, + capacity_sats: Some(1_000_000), + one_to_two: None, + two_to_one: None, + }), + }; + roundtrip(&resp); + + roundtrip(&GraphGetChannelResponse { channel: None }); +} + +#[test] +fn graph_list_nodes_roundtrip() { + roundtrip(&GraphListNodesRequest {}); + roundtrip(&GraphListNodesResponse { node_ids: vec!["abc".into(), "def".into()] }); +} + +#[test] +fn graph_get_node_roundtrip() { + let req = GraphGetNodeRequest { node_id: PUBKEY_33 }; + let json = roundtrip(&req); + assert_eq!(json["node_id"], PUBKEY_33_HEX); + + let resp = GraphGetNodeResponse { + node: Some(GraphNode { + channels: vec![123], + announcement_info: Some(GraphNodeAnnouncement { + last_update: 1_700_000_000, + alias: "test".into(), + rgb: "ffffff".into(), + addresses: vec![], + }), + }), + }; + roundtrip(&resp); + + roundtrip(&GraphGetNodeResponse { node: None }); +} + +#[test] +fn unified_send_request_roundtrip() { + let req = UnifiedSendRequest { + uri: "bitcoin:bc1q...?lightning=lnbc1...".into(), + amount_msat: Some(100_000), + route_parameters: Some(sample_route_params()), + }; + roundtrip(&req); +} + +#[test] +fn unified_send_response_onchain() { + let resp = UnifiedSendResponse::Onchain { txid: HASH_32, payment_id: HASH_32_B }; + let json = roundtrip(&resp); + assert!(json["onchain"].is_object()); + assert_eq!(json["onchain"]["txid"], HASH_32_HEX); +} + +#[test] +fn unified_send_response_bolt11() { + let resp = UnifiedSendResponse::Bolt11 { payment_id: HASH_32 }; + let json = roundtrip(&resp); + assert!(json["bolt11"].is_object()); + assert_eq!(json["bolt11"]["payment_id"], HASH_32_HEX); +} + +#[test] +fn unified_send_response_bolt12() { + let resp = UnifiedSendResponse::Bolt12 { payment_id: HASH_32 }; + let json = roundtrip(&resp); + assert!(json["bolt12"].is_object()); +} + +// =========================================================================== +// Cross-cutting: hex serde round-trips from raw JSON strings +// =========================================================================== + +#[test] +fn hex_32_from_json_string() { + let json_str = format!(r#"{{"payment_id":"{}"}}"#, HASH_32_HEX); + let req: GetPaymentDetailsRequest = serde_json::from_str(&json_str).unwrap(); + assert_eq!(req.payment_id, HASH_32); +} + +#[test] +fn hex_33_from_json_string() { + let json_str = format!( + r#"{{"node_pubkey":"{}","address":"127.0.0.1:9735","persist":true}}"#, + PUBKEY_33_HEX + ); + let req: ConnectPeerRequest = serde_json::from_str(&json_str).unwrap(); + assert_eq!(req.node_pubkey, PUBKEY_33); +} + +#[test] +fn opt_hex_32_null_from_json() { + let json_str = r#"{"hash":"1111111111111111111111111111111111111111111111111111111111111111","preimage":null,"secret":null}"#; + let bolt11: Bolt11 = serde_json::from_str(json_str).unwrap(); + assert_eq!(bolt11.hash, HASH_32_B); + assert_eq!(bolt11.preimage, None); + assert_eq!(bolt11.secret, None); +} + +#[test] +fn opt_hex_32_absent_from_json() { + // Fields with #[serde(default)] should work when absent + let json_str = r#"{"hash":"1111111111111111111111111111111111111111111111111111111111111111"}"#; + let bolt11: Bolt11 = serde_json::from_str(json_str).unwrap(); + assert_eq!(bolt11.preimage, None); + assert_eq!(bolt11.secret, None); +} + +#[test] +fn bytes_hex_roundtrip() { + let req = SignMessageRequest { message: vec![] }; + let json = roundtrip(&req); + assert_eq!(json["message"], ""); + + let req = SignMessageRequest { message: vec![0xff, 0x00, 0xab] }; + let json = roundtrip(&req); + assert_eq!(json["message"], "ff00ab"); +} 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..72122c17 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( @@ -42,5 +34,7 @@ pub(crate) fn handle_bolt11_receive_for_hash_request( )?, }; - Ok(Bolt11ReceiveForHashResponse { invoice: invoice.to_string() }) + let payment_hash = invoice.payment_hash().0; + let payment_secret = invoice.payment_secret().0; + Ok(Bolt11ReceiveForHashResponse { invoice: invoice.to_string(), payment_hash, payment_secret }) } 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..54b18087 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, @@ -28,18 +28,30 @@ pub(crate) fn handle_bolt11_receive_via_jit_channel_request( request.max_total_lsp_fee_limit_msat, )?; - Ok(Bolt11ReceiveViaJitChannelResponse { invoice: invoice.to_string() }) + let payment_hash = invoice.payment_hash().0; + let payment_secret = invoice.payment_secret().0; + Ok(Bolt11ReceiveViaJitChannelResponse { + invoice: invoice.to_string(), + payment_hash, + payment_secret, + }) } 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, request.max_proportional_lsp_fee_limit_ppm_msat, )?; - Ok(Bolt11ReceiveVariableAmountViaJitChannelResponse { invoice: invoice.to_string() }) + let payment_hash = invoice.payment_hash().0; + let payment_secret = invoice.payment_secret().0; + Ok(Bolt11ReceiveVariableAmountViaJitChannelResponse { + invoice: invoice.to_string(), + payment_hash, + payment_secret, + }) } 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..c92fc93c 100644 --- a/ldk-server/src/api/get_node_info.rs +++ b/ldk-server/src/api/get_node_info.rs @@ -7,11 +7,13 @@ // 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; +use crate::util::adapter::to_display_bytes; pub(crate) fn handle_get_node_info_request( context: Context, _request: GetNodeInfoRequest, @@ -19,7 +21,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: to_display_bytes(node_status.current_best_block.block_hash.to_byte_array()), height: node_status.current_best_block.height, }; @@ -37,7 +39,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 +50,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..8ccde337 100644 --- a/ldk-server/src/api/list_forwarded_payments.rs +++ b/ldk-server/src/api/list_forwarded_payments.rs @@ -7,13 +7,12 @@ // 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; use crate::api::error::LdkServerError; use crate::api::error::LdkServerErrorCode::InternalServerError; +use crate::api::{decode_page_token, encode_page_token}; use crate::io::persist::{ FORWARDED_PAYMENTS_PERSISTENCE_PRIMARY_NAMESPACE, FORWARDED_PAYMENTS_PERSISTENCE_SECONDARY_NAMESPACE, @@ -23,7 +22,7 @@ use crate::service::Context; pub(crate) fn handle_list_forwarded_payments_request( context: Context, request: ListForwardedPaymentsRequest, ) -> Result { - let page_token = request.page_token.map(|p| (p.token, p.index)); + let page_token = decode_page_token(request.page_token)?; let list_response = context .paginated_kv_store .list( @@ -54,8 +53,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), @@ -65,9 +64,7 @@ pub(crate) fn handle_list_forwarded_payments_request( } let response = ListForwardedPaymentsResponse { forwarded_payments, - next_page_token: list_response - .next_page_token - .map(|(token, index)| PageToken { token, index }), + next_page_token: encode_page_token(list_response.next_page_token), }; Ok(response) } diff --git a/ldk-server/src/api/list_payments.rs b/ldk-server/src/api/list_payments.rs index fbaf7c55..a619e915 100644 --- a/ldk-server/src/api/list_payments.rs +++ b/ldk-server/src/api/list_payments.rs @@ -7,13 +7,12 @@ // 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::Payment; use crate::api::error::LdkServerError; use crate::api::error::LdkServerErrorCode::InternalServerError; +use crate::api::{decode_page_token, encode_page_token}; use crate::io::persist::{ PAYMENTS_PERSISTENCE_PRIMARY_NAMESPACE, PAYMENTS_PERSISTENCE_SECONDARY_NAMESPACE, }; @@ -22,7 +21,7 @@ use crate::service::Context; pub(crate) fn handle_list_payments_request( context: Context, request: ListPaymentsRequest, ) -> Result { - let page_token = request.page_token.map(|p| (p.token, p.index)); + let page_token = decode_page_token(request.page_token)?; let list_response = context .paginated_kv_store .list( @@ -49,16 +48,14 @@ 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); } let response = ListPaymentsResponse { payments, - next_page_token: list_response - .next_page_token - .map(|(token, index)| PageToken { token, index }), + next_page_token: encode_page_token(list_response.next_page_token), }; Ok(response) } 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..8ca5d8fd 100644 --- a/ldk-server/src/api/mod.rs +++ b/ldk-server/src/api/mod.rs @@ -9,11 +9,34 @@ 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; +/// Decode an opaque page token string into the internal `(key, creation_time)` tuple. +pub(crate) fn decode_page_token( + token: Option, +) -> Result, LdkServerError> { + match token { + None => Ok(None), + Some(s) => { + let (key, idx) = s.rsplit_once(':').ok_or_else(|| { + LdkServerError::new(InvalidRequestError, "Invalid page token format") + })?; + let index = idx.parse::().map_err(|_| { + LdkServerError::new(InvalidRequestError, "Invalid page token index") + })?; + Ok(Some((key.to_string(), index))) + }, + } +} + +/// Encode the internal `(key, creation_time)` tuple into an opaque page token string. +pub(crate) fn encode_page_token(token: Option<(String, i64)>) -> Option { + token.map(|(key, index)| format!("{key}:{index}")) +} + pub(crate) mod bolt11_claim_for_hash; pub(crate) mod bolt11_fail_for_hash; pub(crate) mod bolt11_receive; @@ -48,10 +71,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 +87,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 +99,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..5ddc3574 100644 --- a/ldk-server/src/api/onchain_send.rs +++ b/ldk-server/src/api/onchain_send.rs @@ -9,12 +9,14 @@ 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; use crate::service::Context; +use crate::util::adapter::to_display_bytes; pub(crate) fn handle_onchain_send_request( context: Context, request: OnchainSendRequest, @@ -45,6 +47,7 @@ pub(crate) fn handle_onchain_send_request( )) }, }; - let response = OnchainSendResponse { txid: txid.to_string() }; + let payment_id = txid.to_byte_array(); + let response = OnchainSendResponse { txid: to_display_bytes(txid.to_byte_array()), payment_id }; 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..081cbf16 100644 --- a/ldk-server/src/api/unified_send.rs +++ b/ldk-server/src/api/unified_send.rs @@ -7,19 +7,20 @@ // You may not use this file except in accordance with one or both of these // licenses. +use ldk_node::bitcoin::hashes::Hash; 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::{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; +use crate::util::adapter::to_display_bytes; 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( @@ -29,15 +30,19 @@ pub(crate) fn handle_unified_send_request( )) })?; - let payment_result = match result { - UnifiedPaymentResult::Onchain { txid } => PaymentResult::Txid(txid.to_string()), + Ok(match result { + UnifiedPaymentResult::Onchain { txid } => { + let payment_id = txid.to_byte_array(); + UnifiedSendResponse::Onchain { + txid: to_display_bytes(txid.to_byte_array()), + payment_id, + } + }, UnifiedPaymentResult::Bolt11 { payment_id } => { - PaymentResult::Bolt11PaymentId(payment_id.to_string()) + UnifiedSendResponse::Bolt11 { payment_id: payment_id.0 } }, UnifiedPaymentResult::Bolt12 { payment_id } => { - PaymentResult::Bolt12PaymentId(payment_id.to_string()) + UnifiedSendResponse::Bolt12 { payment_id: payment_id.0 } }, - }; - - Ok(UnifiedSendResponse { payment_result: Some(payment_result) }) + }) } 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/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..ac5e002f 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 @@ -165,6 +166,23 @@ pub(crate) struct Context { pub(crate) paginated_kv_store: Arc, } +fn json_response(status: StatusCode, body: &impl Serialize) -> Response> { + match serde_json::to_vec(body) { + Ok(bytes) => Response::builder() + .status(status) + .header("Content-Type", "application/json") + .body(Full::new(Bytes::from(bytes))) + // unwrap safety: body only errors when previous chained calls failed. + .unwrap(), + Err(_) => Response::builder() + .status(StatusCode::INTERNAL_SERVER_ERROR) + .header("Content-Type", "text/plain") + .body(Full::new(Bytes::from("Internal server error"))) + // unwrap safety: body only errors when previous chained calls failed. + .unwrap(), + } +} + impl Service> for NodeService { type Response = Response>; type Error = hyper::Error; @@ -176,13 +194,7 @@ impl Service> for NodeService { 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) - .body(Full::new(Bytes::from(error_response.encode_to_vec()))) - // unwrap safety: body only errors when previous chained calls failed. - .unwrap()) - }); + return Box::pin(async move { Ok(json_response(status_code, &error_response)) }); }, }; @@ -442,8 +454,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, @@ -458,11 +470,7 @@ async fn handle_request< InvalidRequestError, "Request body too large or failed to read.", )); - return Ok(Response::builder() - .status(status_code) - .body(Full::new(Bytes::from(error_response.encode_to_vec()))) - // unwrap safety: body only errors when previous chained calls failed. - .unwrap()); + return Ok(json_response(status_code, &error_response)); }, }; @@ -471,36 +479,22 @@ async fn handle_request< 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) - .body(Full::new(Bytes::from(error_response.encode_to_vec()))) - // unwrap safety: body only errors when previous chained calls failed. - .unwrap()); + return Ok(json_response(status_code, &error_response)); } - 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()))) - // unwrap safety: body only errors when previous chained calls failed. - .unwrap()), + Ok(response) => Ok(json_response(StatusCode::OK, &response)), 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()))) - // unwrap safety: body only errors when previous chained calls failed. - .unwrap()) + Ok(json_response(status_code, &error_response)) }, }, Err(_) => { let (error_response, status_code) = to_error_response(LdkServerError::new(InvalidRequestError, "Malformed request.")); - Ok(Response::builder() - .status(status_code) - .body(Full::new(Bytes::from(error_response.encode_to_vec()))) - // unwrap safety: body only errors when previous chained calls failed. - .unwrap()) + Ok(json_response(status_code, &error_response)) }, } } 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..11f49fc0 100644 --- a/ldk-server/src/util/proto_adapter.rs +++ b/ldk-server/src/util/adapter.rs @@ -7,10 +7,9 @@ // 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::{sha256, Hash as _}; use ldk_node::bitcoin::secp256k1::PublicKey; use ldk_node::config::{ChannelConfig, MaxDustHTLCExposure}; use ldk_node::lightning::chain::channelmonitor::BalanceSource; @@ -23,20 +22,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 +32,30 @@ use crate::api::error::LdkServerErrorCode::{ AuthError, InternalServerError, InvalidRequestError, LightningError, }; -pub(crate) fn peer_to_proto(peer: PeerDetails) -> Peer { +/// Bitcoin block hashes and txids are displayed in reversed byte order by convention. +/// `Hash::to_byte_array()` returns internal order; this helper reverses for display. +pub(crate) fn to_display_bytes(hash: [u8; 32]) -> [u8; 32] { + let mut bytes = hash; + bytes.reverse(); + bytes +} + +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: to_display_bytes(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 +68,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 +86,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 +101,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 +122,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: to_display_bytes(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 +166,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: to_display_bytes(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 +247,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 +292,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 +309,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: to_display_bytes(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: to_display_bytes(latest_spending_txid.to_byte_array()), + confirmation_hash: to_display_bytes(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 +400,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 +416,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 +428,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 +448,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 +494,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 +507,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;