From f08effac57162f9b822057697c6bd0da557521ac Mon Sep 17 00:00:00 2001 From: benthecarman Date: Fri, 27 Mar 2026 13:22:55 -0500 Subject: [PATCH 1/8] Add gRPC service definition to api.proto Define the LightningNode service with all 38 unary RPCs and a server-streaming SubscribeEvents RPC. Add SubscribeEventsRequest message and events.proto import. Update response comments from REST/HTTP style to gRPC style. prost-build ignores service blocks, so this is a documentation- only change that prepares for the gRPC migration without altering any generated code or runtime behavior. Co-Authored-By: Claude Opus 4.6 (1M context) --- ldk-server-protos/src/proto/api.proto | 194 ++++++++++++++++---------- 1 file changed, 120 insertions(+), 74 deletions(-) diff --git a/ldk-server-protos/src/proto/api.proto b/ldk-server-protos/src/proto/api.proto index 4d21c4d8..6bb1aa1f 100644 --- a/ldk-server-protos/src/proto/api.proto +++ b/ldk-server-protos/src/proto/api.proto @@ -2,6 +2,7 @@ syntax = "proto3"; package api; import 'types.proto'; +import 'events.proto'; // Retrieve the latest node info like `node_id`, `current_best_block` etc. // See more: @@ -10,8 +11,7 @@ import 'types.proto'; 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`. +// The response for the `GetNodeInfo` RPC. On failure, a gRPC error status is returned. message GetNodeInfoResponse { // The hex-encoded `node-id` or public key for our own lightning node. @@ -77,8 +77,7 @@ message GetNodeInfoResponse { 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`.. +// The response for the `OnchainReceive` RPC. On failure, a gRPC error status is returned. message OnchainReceiveResponse { // A Bitcoin on-chain address. @@ -111,8 +110,7 @@ message OnchainSendRequest { 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`. +// The response for the `OnchainSend` RPC. On failure, a gRPC error status is returned. message OnchainSendResponse { // The transaction ID of the broadcasted transaction. @@ -138,8 +136,7 @@ message Bolt11ReceiveRequest { 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`. +// The response for the `Bolt11Receive` RPC. On failure, a gRPC error status is returned. message Bolt11ReceiveResponse { // An invoice for a payment within the Lightning Network. @@ -177,8 +174,7 @@ message Bolt11ReceiveForHashRequest { 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`. +// The response for the `Bolt11ReceiveForHash` RPC. On failure, a gRPC error status is returned. message Bolt11ReceiveForHashResponse { // An invoice for a payment within the Lightning Network. @@ -204,8 +200,7 @@ message Bolt11ClaimForHashRequest { 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`. +// The response for the `Bolt11ClaimForHash` RPC. On failure, a gRPC error status is returned. message Bolt11ClaimForHashResponse {} // Manually fail a payment for a given payment hash. @@ -217,8 +212,7 @@ message Bolt11FailForHashRequest { 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`. +// The response for the `Bolt11FailForHash` RPC. On failure, a gRPC error status is returned. message Bolt11FailForHashResponse {} // Return a BOLT11 payable invoice that can be used to request and receive a payment via an @@ -240,8 +234,7 @@ message Bolt11ReceiveViaJitChannelRequest { 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`. +// The response for the `Bolt11ReceiveViaJitChannel` RPC. On failure, a gRPC error status is returned. message Bolt11ReceiveViaJitChannelResponse { // An invoice for a payment within the Lightning Network. @@ -265,8 +258,7 @@ message Bolt11ReceiveVariableAmountViaJitChannelRequest { 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`. +// The response for the `Bolt11ReceiveVariableAmountViaJitChannel` RPC. On failure, a gRPC error status is returned. message Bolt11ReceiveVariableAmountViaJitChannelResponse { // An invoice for a payment within the Lightning Network. @@ -291,8 +283,7 @@ message Bolt11SendRequest { } -// The response `content` for the `Bolt11Send` API, when HttpStatusCode is OK (200). -// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. +// The response for the `Bolt11Send` RPC. On failure, a gRPC error status is returned. message Bolt11SendResponse { // An identifier used to uniquely identify a payment in hex-encoded form. @@ -320,8 +311,7 @@ message Bolt12ReceiveRequest { 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`. +// The response for the `Bolt12Receive` RPC. On failure, a gRPC error status is returned. message Bolt12ReceiveResponse { // An offer for a payment within the Lightning Network. @@ -357,8 +347,7 @@ message Bolt12SendRequest { 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`. +// The response for the `Bolt12Send` RPC. On failure, a gRPC error status is returned. message Bolt12SendResponse { // An identifier used to uniquely identify a payment in hex-encoded form. @@ -378,8 +367,7 @@ message SpontaneousSendRequest { 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`. +// The response for the `SpontaneousSend` RPC. On failure, a gRPC error status is returned. message SpontaneousSendResponse { // An identifier used to uniquely identify a payment in hex-encoded form. string payment_id = 1; @@ -409,8 +397,7 @@ message OpenChannelRequest { 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`. +// The response for the `OpenChannel` RPC. On failure, a gRPC error status is returned. message OpenChannelResponse { // The local channel id of the created channel that user can use to refer to channel. @@ -431,8 +418,7 @@ message SpliceInRequest { 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`. +// The response for the `SpliceIn` RPC. On failure, a gRPC error status is returned. message SpliceInResponse {} // Decreases the channel balance by the given amount. @@ -454,8 +440,7 @@ message SpliceOutRequest { 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`. +// The response for the `SpliceOut` RPC. On failure, a gRPC error status is returned. message SpliceOutResponse { // The Bitcoin on-chain address where the funds will be sent. @@ -476,8 +461,7 @@ message UpdateChannelConfigRequest { 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`. +// The response for the `UpdateChannelConfig` RPC. On failure, a gRPC error status is returned. message UpdateChannelConfigResponse { } @@ -492,8 +476,7 @@ message CloseChannelRequest { 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`. +// The response for the `CloseChannel` RPC. On failure, a gRPC error status is returned. message CloseChannelResponse {} // Force closes the channel specified by given request. @@ -507,16 +490,14 @@ message ForceCloseChannelRequest { 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`. +// The response for the `ForceCloseChannel` RPC. On failure, a gRPC error status is returned. 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`. +// The response for the `ListChannels` RPC. On failure, a gRPC error status is returned. message ListChannelsResponse { // List of channels. @@ -530,8 +511,7 @@ message GetPaymentDetailsRequest { 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`. +// The response for the `GetPaymentDetails` RPC. On failure, a gRPC error status is returned. message GetPaymentDetailsResponse { // Represents a payment. // Will be `None` if payment doesn't exist. @@ -550,8 +530,7 @@ message ListPaymentsRequest { 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`. +// The response for the `ListPayments` RPC. On failure, a gRPC error status is returned. message ListPaymentsResponse { // List of payments. repeated types.Payment payments = 1; @@ -584,8 +563,7 @@ message ListForwardedPaymentsRequest { 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`. +// The response for the `ListForwardedPayments` RPC. On failure, a gRPC error status is returned. message ListForwardedPaymentsResponse { // List of forwarded payments. repeated types.ForwardedPayment forwarded_payments = 1; @@ -613,8 +591,7 @@ message SignMessageRequest { 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`. +// The response for the `SignMessage` RPC. On failure, a gRPC error status is returned. message SignMessageResponse { // The signature of the message, as a zbase32-encoded string. string signature = 1; @@ -633,8 +610,7 @@ message VerifySignatureRequest { 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`. +// The response for the `VerifySignature` RPC. On failure, a gRPC error status is returned. message VerifySignatureResponse { // Whether the signature is valid. bool valid = 1; @@ -644,8 +620,7 @@ message VerifySignatureResponse { // 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`. +// The response for the `ExportPathfindingScores` RPC. On failure, a gRPC error status is returned. message ExportPathfindingScoresResponse { // The serialized pathfinding scores data. bytes scores = 1; @@ -655,8 +630,7 @@ message ExportPathfindingScoresResponse { // 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`. +// The response for the `GetBalances` RPC. On failure, a gRPC error status is returned. message GetBalancesResponse { // The total balance of our on-chain wallet. uint64 total_onchain_balance_sats = 1; @@ -711,8 +685,7 @@ message ConnectPeerRequest { 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`. +// The response for the `ConnectPeer` RPC. On failure, a gRPC error status is returned. message ConnectPeerResponse {} // Disconnect from a peer and remove it from the peer store. @@ -722,16 +695,14 @@ message DisconnectPeerRequest { 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`. +// The response for the `DisconnectPeer` RPC. On failure, a gRPC error status is returned. 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`. +// The response for the `ListPeers` RPC. On failure, a gRPC error status is returned. message ListPeersResponse { // List of peers. @@ -742,8 +713,7 @@ message ListPeersResponse { // 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`. +// The response for the `GraphListChannels` RPC. On failure, a gRPC error status is returned. message GraphListChannelsResponse { // List of short channel IDs known to the network graph. repeated uint64 short_channel_ids = 1; @@ -756,8 +726,7 @@ message GraphGetChannelRequest { 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`. +// The response for the `GraphGetChannel` RPC. On failure, a gRPC error status is returned. message GraphGetChannelResponse { // The channel information. types.GraphChannel channel = 1; @@ -767,8 +736,7 @@ message GraphGetChannelResponse { // 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`. +// The response for the `GraphListNodes` RPC. On failure, a gRPC error status is returned. message GraphListNodesResponse { // List of hex-encoded node IDs known to the network graph. repeated string node_ids = 1; @@ -792,8 +760,7 @@ message UnifiedSendRequest { 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`. +// The response for the `UnifiedSend` RPC. On failure, a gRPC error status is returned. message UnifiedSendResponse { oneof payment_result { @@ -816,8 +783,7 @@ message GraphGetNodeRequest { 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`. +// The response for the `GraphGetNode` RPC. On failure, a gRPC error status is returned. message GraphGetNodeResponse { // The node information. types.GraphNode node = 1; @@ -830,8 +796,7 @@ message DecodeInvoiceRequest { string invoice = 1; } -// The response `content` for the `DecodeInvoice` API, when HttpStatusCode is OK (200). -// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. +// The response for the `DecodeInvoice` RPC. On failure, a gRPC error status is returned. message DecodeInvoiceResponse { // The hex-encoded public key of the destination node. string destination = 1; @@ -886,8 +851,7 @@ message DecodeOfferRequest { string offer = 1; } -// The response `content` for the `DecodeOffer` API, when HttpStatusCode is OK (200). -// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. +// The response for the `DecodeOffer` RPC. On failure, a gRPC error status is returned. message DecodeOfferResponse { // The hex-encoded offer ID. string offer_id = 1; @@ -925,3 +889,85 @@ message DecodeOfferResponse { // Whether the offer has expired. bool is_expired = 12; } + +// Subscribe to a stream of server events. +message SubscribeEventsRequest {} + +service LightningNode { + // Retrieve the latest node info. + rpc GetNodeInfo(GetNodeInfoRequest) returns (GetNodeInfoResponse); + // Retrieve an overview of all known balances. + rpc GetBalances(GetBalancesRequest) returns (GetBalancesResponse); + // Retrieve a new on-chain funding address. + rpc OnchainReceive(OnchainReceiveRequest) returns (OnchainReceiveResponse); + // Send an on-chain payment to the given address. + rpc OnchainSend(OnchainSendRequest) returns (OnchainSendResponse); + // Return a BOLT11 payable invoice. + rpc Bolt11Receive(Bolt11ReceiveRequest) returns (Bolt11ReceiveResponse); + // Return a BOLT11 payable invoice for a given payment hash. + rpc Bolt11ReceiveForHash(Bolt11ReceiveForHashRequest) returns (Bolt11ReceiveForHashResponse); + // Manually claim a payment for a given payment hash. + rpc Bolt11ClaimForHash(Bolt11ClaimForHashRequest) returns (Bolt11ClaimForHashResponse); + // Manually fail a payment for a given payment hash. + rpc Bolt11FailForHash(Bolt11FailForHashRequest) returns (Bolt11FailForHashResponse); + // Return a BOLT11 invoice for receiving via a JIT channel. + rpc Bolt11ReceiveViaJitChannel(Bolt11ReceiveViaJitChannelRequest) returns (Bolt11ReceiveViaJitChannelResponse); + // Return a variable-amount BOLT11 invoice for receiving via a JIT channel. + rpc Bolt11ReceiveVariableAmountViaJitChannel(Bolt11ReceiveVariableAmountViaJitChannelRequest) returns (Bolt11ReceiveVariableAmountViaJitChannelResponse); + // Send a payment for a BOLT11 invoice. + rpc Bolt11Send(Bolt11SendRequest) returns (Bolt11SendResponse); + // Return a BOLT12 offer. + rpc Bolt12Receive(Bolt12ReceiveRequest) returns (Bolt12ReceiveResponse); + // Send a payment for a BOLT12 offer. + rpc Bolt12Send(Bolt12SendRequest) returns (Bolt12SendResponse); + // Send a spontaneous payment (keysend). + rpc SpontaneousSend(SpontaneousSendRequest) returns (SpontaneousSendResponse); + // Create a new outbound channel. + rpc OpenChannel(OpenChannelRequest) returns (OpenChannelResponse); + // Splice funds into a channel. + rpc SpliceIn(SpliceInRequest) returns (SpliceInResponse); + // Splice funds out of a channel. + rpc SpliceOut(SpliceOutRequest) returns (SpliceOutResponse); + // Update the config for a channel. + rpc UpdateChannelConfig(UpdateChannelConfigRequest) returns (UpdateChannelConfigResponse); + // Close a channel cooperatively. + rpc CloseChannel(CloseChannelRequest) returns (CloseChannelResponse); + // Force close a channel. + rpc ForceCloseChannel(ForceCloseChannelRequest) returns (ForceCloseChannelResponse); + // List known channels. + rpc ListChannels(ListChannelsRequest) returns (ListChannelsResponse); + // Get payment details by payment ID. + rpc GetPaymentDetails(GetPaymentDetailsRequest) returns (GetPaymentDetailsResponse); + // List all payments. + rpc ListPayments(ListPaymentsRequest) returns (ListPaymentsResponse); + // List all forwarded payments. + rpc ListForwardedPayments(ListForwardedPaymentsRequest) returns (ListForwardedPaymentsResponse); + // Connect to a peer. + rpc ConnectPeer(ConnectPeerRequest) returns (ConnectPeerResponse); + // Disconnect from a peer. + rpc DisconnectPeer(DisconnectPeerRequest) returns (DisconnectPeerResponse); + // List peers. + rpc ListPeers(ListPeersRequest) returns (ListPeersResponse); + // Sign a message with the node's secret key. + rpc SignMessage(SignMessageRequest) returns (SignMessageResponse); + // Verify a signature against a message and public key. + rpc VerifySignature(VerifySignatureRequest) returns (VerifySignatureResponse); + // Export the pathfinding scores used by the router. + rpc ExportPathfindingScores(ExportPathfindingScoresRequest) returns (ExportPathfindingScoresResponse); + // Send a payment given a BIP 21 URI or BIP 353 Human-Readable Name. + rpc UnifiedSend(UnifiedSendRequest) returns (UnifiedSendResponse); + // Decode a BOLT11 invoice and return its parsed fields. + rpc DecodeInvoice(DecodeInvoiceRequest) returns (DecodeInvoiceResponse); + // Decode a BOLT12 offer and return its parsed fields. + rpc DecodeOffer(DecodeOfferRequest) returns (DecodeOfferResponse); + // List all known short channel IDs in the network graph. + rpc GraphListChannels(GraphListChannelsRequest) returns (GraphListChannelsResponse); + // Get channel info from the network graph by short channel ID. + rpc GraphGetChannel(GraphGetChannelRequest) returns (GraphGetChannelResponse); + // List all known node IDs in the network graph. + rpc GraphListNodes(GraphListNodesRequest) returns (GraphListNodesResponse); + // Get node info from the network graph by node ID. + rpc GraphGetNode(GraphGetNodeRequest) returns (GraphGetNodeResponse); + // Subscribe to a stream of server events. + rpc SubscribeEvents(SubscribeEventsRequest) returns (stream events.EventEnvelope); +} From 494e2b7d774b203348040a78942d5541eaf4d6af Mon Sep 17 00:00:00 2001 From: benthecarman Date: Fri, 27 Mar 2026 13:28:19 -0500 Subject: [PATCH 2/8] Change handler signatures to borrow Context Take &Context instead of Context by value in all 37 handler functions. The handlers only read from context.node and context.paginated_kv_store (both Arc), so borrowing avoids unnecessary Arc clones per request and prepares for the gRPC service rewrite where Context lives in the service struct. Co-Authored-By: Claude Opus 4.6 (1M context) --- ldk-server/src/api/bolt11_claim_for_hash.rs | 5 +++-- ldk-server/src/api/bolt11_fail_for_hash.rs | 5 +++-- ldk-server/src/api/bolt11_receive.rs | 5 +++-- ldk-server/src/api/bolt11_receive_for_hash.rs | 5 +++-- .../src/api/bolt11_receive_via_jit_channel.rs | 9 +++++---- ldk-server/src/api/bolt11_send.rs | 5 +++-- ldk-server/src/api/bolt12_receive.rs | 5 +++-- ldk-server/src/api/bolt12_send.rs | 5 +++-- ldk-server/src/api/close_channel.rs | 9 +++++---- ldk-server/src/api/connect_peer.rs | 5 +++-- ldk-server/src/api/decode_invoice.rs | 5 +++-- ldk-server/src/api/decode_offer.rs | 5 +++-- ldk-server/src/api/disconnect_peer.rs | 5 +++-- .../src/api/export_pathfinding_scores.rs | 5 +++-- ldk-server/src/api/get_balances.rs | 5 +++-- ldk-server/src/api/get_node_info.rs | 5 +++-- ldk-server/src/api/get_payment_details.rs | 5 +++-- ldk-server/src/api/graph_get_channel.rs | 5 +++-- ldk-server/src/api/graph_get_node.rs | 5 +++-- ldk-server/src/api/graph_list_channels.rs | 5 +++-- ldk-server/src/api/graph_list_nodes.rs | 5 +++-- ldk-server/src/api/list_channels.rs | 5 +++-- ldk-server/src/api/list_forwarded_payments.rs | 5 +++-- ldk-server/src/api/list_payments.rs | 5 +++-- ldk-server/src/api/list_peers.rs | 5 +++-- ldk-server/src/api/onchain_receive.rs | 5 +++-- ldk-server/src/api/onchain_send.rs | 5 +++-- ldk-server/src/api/open_channel.rs | 5 +++-- ldk-server/src/api/sign_message.rs | 5 +++-- ldk-server/src/api/splice_channel.rs | 9 +++++---- ldk-server/src/api/spontaneous_send.rs | 5 +++-- ldk-server/src/api/unified_send.rs | 18 ++++++++---------- ldk-server/src/api/update_channel_config.rs | 5 +++-- ldk-server/src/api/verify_signature.rs | 5 +++-- ldk-server/src/service.rs | 5 +++-- 35 files changed, 116 insertions(+), 84 deletions(-) diff --git a/ldk-server/src/api/bolt11_claim_for_hash.rs b/ldk-server/src/api/bolt11_claim_for_hash.rs index e1e62288..52a668da 100644 --- a/ldk-server/src/api/bolt11_claim_for_hash.rs +++ b/ldk-server/src/api/bolt11_claim_for_hash.rs @@ -15,9 +15,10 @@ use ldk_server_protos::api::{Bolt11ClaimForHashRequest, Bolt11ClaimForHashRespon use crate::api::error::LdkServerError; use crate::api::error::LdkServerErrorCode::InvalidRequestError; use crate::service::Context; +use std::sync::Arc; -pub(crate) fn handle_bolt11_claim_for_hash_request( - context: Context, request: Bolt11ClaimForHashRequest, +pub(crate) async fn handle_bolt11_claim_for_hash_request( + context: Arc, request: Bolt11ClaimForHashRequest, ) -> Result { let preimage_bytes = <[u8; 32]>::from_hex(&request.preimage).map_err(|_| { LdkServerError::new( diff --git a/ldk-server/src/api/bolt11_fail_for_hash.rs b/ldk-server/src/api/bolt11_fail_for_hash.rs index 9f349164..61b05814 100644 --- a/ldk-server/src/api/bolt11_fail_for_hash.rs +++ b/ldk-server/src/api/bolt11_fail_for_hash.rs @@ -14,9 +14,10 @@ use ldk_server_protos::api::{Bolt11FailForHashRequest, Bolt11FailForHashResponse use crate::api::error::LdkServerError; use crate::api::error::LdkServerErrorCode::InvalidRequestError; use crate::service::Context; +use std::sync::Arc; -pub(crate) fn handle_bolt11_fail_for_hash_request( - context: Context, request: Bolt11FailForHashRequest, +pub(crate) async fn handle_bolt11_fail_for_hash_request( + context: Arc, request: Bolt11FailForHashRequest, ) -> Result { let hash_bytes = <[u8; 32]>::from_hex(&request.payment_hash).map_err(|_| { LdkServerError::new( diff --git a/ldk-server/src/api/bolt11_receive.rs b/ldk-server/src/api/bolt11_receive.rs index 793a0928..0291bc8f 100644 --- a/ldk-server/src/api/bolt11_receive.rs +++ b/ldk-server/src/api/bolt11_receive.rs @@ -13,9 +13,10 @@ use ldk_server_protos::api::{Bolt11ReceiveRequest, Bolt11ReceiveResponse}; use crate::api::error::LdkServerError; use crate::service::Context; use crate::util::proto_adapter::proto_to_bolt11_description; +use std::sync::Arc; -pub(crate) fn handle_bolt11_receive_request( - context: Context, request: Bolt11ReceiveRequest, +pub(crate) async fn handle_bolt11_receive_request( + context: Arc, request: Bolt11ReceiveRequest, ) -> Result { let description = proto_to_bolt11_description(request.description)?; let invoice = match request.amount_msat { diff --git a/ldk-server/src/api/bolt11_receive_for_hash.rs b/ldk-server/src/api/bolt11_receive_for_hash.rs index 8f687067..80ec8e5e 100644 --- a/ldk-server/src/api/bolt11_receive_for_hash.rs +++ b/ldk-server/src/api/bolt11_receive_for_hash.rs @@ -15,9 +15,10 @@ 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 std::sync::Arc; -pub(crate) fn handle_bolt11_receive_for_hash_request( - context: Context, request: Bolt11ReceiveForHashRequest, +pub(crate) async fn handle_bolt11_receive_for_hash_request( + context: Arc, request: Bolt11ReceiveForHashRequest, ) -> Result { let description = proto_to_bolt11_description(request.description)?; let hash_bytes = <[u8; 32]>::from_hex(&request.payment_hash).map_err(|_| { 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..5f79e197 100644 --- a/ldk-server/src/api/bolt11_receive_via_jit_channel.rs +++ b/ldk-server/src/api/bolt11_receive_via_jit_channel.rs @@ -16,9 +16,10 @@ use ldk_server_protos::api::{ use crate::api::error::LdkServerError; use crate::service::Context; use crate::util::proto_adapter::proto_to_bolt11_description; +use std::sync::Arc; -pub(crate) fn handle_bolt11_receive_via_jit_channel_request( - context: Context, request: Bolt11ReceiveViaJitChannelRequest, +pub(crate) async fn handle_bolt11_receive_via_jit_channel_request( + context: Arc, request: Bolt11ReceiveViaJitChannelRequest, ) -> Result { let description = proto_to_bolt11_description(request.description)?; let invoice = context.node.bolt11_payment().receive_via_jit_channel( @@ -31,8 +32,8 @@ pub(crate) fn handle_bolt11_receive_via_jit_channel_request( Ok(Bolt11ReceiveViaJitChannelResponse { invoice: invoice.to_string() }) } -pub(crate) fn handle_bolt11_receive_variable_amount_via_jit_channel_request( - context: Context, request: Bolt11ReceiveVariableAmountViaJitChannelRequest, +pub(crate) async fn handle_bolt11_receive_variable_amount_via_jit_channel_request( + context: Arc, request: Bolt11ReceiveVariableAmountViaJitChannelRequest, ) -> Result { let description = proto_to_bolt11_description(request.description)?; let invoice = context.node.bolt11_payment().receive_variable_amount_via_jit_channel( diff --git a/ldk-server/src/api/bolt11_send.rs b/ldk-server/src/api/bolt11_send.rs index 1298896e..981290f0 100644 --- a/ldk-server/src/api/bolt11_send.rs +++ b/ldk-server/src/api/bolt11_send.rs @@ -15,9 +15,10 @@ use ldk_server_protos::api::{Bolt11SendRequest, Bolt11SendResponse}; use crate::api::build_route_parameters_config_from_proto; use crate::api::error::LdkServerError; use crate::service::Context; +use std::sync::Arc; -pub(crate) fn handle_bolt11_send_request( - context: Context, request: Bolt11SendRequest, +pub(crate) async fn handle_bolt11_send_request( + context: Arc, request: Bolt11SendRequest, ) -> Result { let invoice = Bolt11Invoice::from_str(request.invoice.as_str()) .map_err(|_| ldk_node::NodeError::InvalidInvoice)?; diff --git a/ldk-server/src/api/bolt12_receive.rs b/ldk-server/src/api/bolt12_receive.rs index 42f4b489..5c0e3051 100644 --- a/ldk-server/src/api/bolt12_receive.rs +++ b/ldk-server/src/api/bolt12_receive.rs @@ -12,9 +12,10 @@ use ldk_server_protos::api::{Bolt12ReceiveRequest, Bolt12ReceiveResponse}; use crate::api::error::LdkServerError; use crate::service::Context; +use std::sync::Arc; -pub(crate) fn handle_bolt12_receive_request( - context: Context, request: Bolt12ReceiveRequest, +pub(crate) async fn handle_bolt12_receive_request( + context: Arc, request: Bolt12ReceiveRequest, ) -> Result { let offer = match request.amount_msat { Some(amount_msat) => context.node.bolt12_payment().receive( diff --git a/ldk-server/src/api/bolt12_send.rs b/ldk-server/src/api/bolt12_send.rs index 30df2dd8..d4503a82 100644 --- a/ldk-server/src/api/bolt12_send.rs +++ b/ldk-server/src/api/bolt12_send.rs @@ -15,9 +15,10 @@ use ldk_server_protos::api::{Bolt12SendRequest, Bolt12SendResponse}; use crate::api::build_route_parameters_config_from_proto; use crate::api::error::LdkServerError; use crate::service::Context; +use std::sync::Arc; -pub(crate) fn handle_bolt12_send_request( - context: Context, request: Bolt12SendRequest, +pub(crate) async fn handle_bolt12_send_request( + context: Arc, request: Bolt12SendRequest, ) -> Result { let offer = Offer::from_str(request.offer.as_str()).map_err(|_| ldk_node::NodeError::InvalidOffer)?; diff --git a/ldk-server/src/api/close_channel.rs b/ldk-server/src/api/close_channel.rs index 5ae4070b..6067e2b6 100644 --- a/ldk-server/src/api/close_channel.rs +++ b/ldk-server/src/api/close_channel.rs @@ -18,9 +18,10 @@ use ldk_server_protos::api::{ use crate::api::error::LdkServerError; use crate::api::error::LdkServerErrorCode::InvalidRequestError; use crate::service::Context; +use std::sync::Arc; -pub(crate) fn handle_close_channel_request( - context: Context, request: CloseChannelRequest, +pub(crate) async fn handle_close_channel_request( + context: Arc, request: CloseChannelRequest, ) -> Result { let user_channel_id = parse_user_channel_id(&request.user_channel_id)?; let counterparty_node_id = parse_counterparty_node_id(&request.counterparty_node_id)?; @@ -30,8 +31,8 @@ pub(crate) fn handle_close_channel_request( Ok(CloseChannelResponse {}) } -pub(crate) fn handle_force_close_channel_request( - context: Context, request: ForceCloseChannelRequest, +pub(crate) async fn handle_force_close_channel_request( + context: Arc, request: ForceCloseChannelRequest, ) -> Result { let user_channel_id = parse_user_channel_id(&request.user_channel_id)?; let counterparty_node_id = parse_counterparty_node_id(&request.counterparty_node_id)?; diff --git a/ldk-server/src/api/connect_peer.rs b/ldk-server/src/api/connect_peer.rs index d3fac3a0..54fb87ba 100644 --- a/ldk-server/src/api/connect_peer.rs +++ b/ldk-server/src/api/connect_peer.rs @@ -15,9 +15,10 @@ use ldk_server_protos::api::{ConnectPeerRequest, ConnectPeerResponse}; use crate::api::error::LdkServerError; use crate::service::Context; +use std::sync::Arc; -pub(crate) fn handle_connect_peer( - context: Context, request: ConnectPeerRequest, +pub(crate) async fn handle_connect_peer( + context: Arc, request: ConnectPeerRequest, ) -> Result { let node_id = PublicKey::from_str(&request.node_pubkey) .map_err(|_| ldk_node::NodeError::InvalidPublicKey)?; diff --git a/ldk-server/src/api/decode_invoice.rs b/ldk-server/src/api/decode_invoice.rs index c2b63e6a..863ec0ea 100644 --- a/ldk-server/src/api/decode_invoice.rs +++ b/ldk-server/src/api/decode_invoice.rs @@ -18,9 +18,10 @@ use ldk_server_protos::types::{Bolt11HopHint, Bolt11RouteHint}; use crate::api::decode_features; use crate::api::error::LdkServerError; use crate::service::Context; +use std::sync::Arc; -pub(crate) fn handle_decode_invoice_request( - _context: Context, request: DecodeInvoiceRequest, +pub(crate) async fn handle_decode_invoice_request( + _context: Arc, request: DecodeInvoiceRequest, ) -> Result { let invoice = Bolt11Invoice::from_str(request.invoice.as_str()) .map_err(|_| ldk_node::NodeError::InvalidInvoice)?; diff --git a/ldk-server/src/api/decode_offer.rs b/ldk-server/src/api/decode_offer.rs index adb12dc3..e44dbf6b 100644 --- a/ldk-server/src/api/decode_offer.rs +++ b/ldk-server/src/api/decode_offer.rs @@ -22,9 +22,10 @@ use ldk_server_protos::types::{BlindedPath, CurrencyAmount, OfferAmount, OfferQu use crate::api::decode_features; use crate::api::error::LdkServerError; use crate::service::Context; +use std::sync::Arc; -pub(crate) fn handle_decode_offer_request( - _context: Context, request: DecodeOfferRequest, +pub(crate) async fn handle_decode_offer_request( + _context: Arc, request: DecodeOfferRequest, ) -> Result { let offer = Offer::from_str(request.offer.as_str()).map_err(|_| ldk_node::NodeError::InvalidOffer)?; diff --git a/ldk-server/src/api/disconnect_peer.rs b/ldk-server/src/api/disconnect_peer.rs index 76f2ecce..3e1bbfc6 100644 --- a/ldk-server/src/api/disconnect_peer.rs +++ b/ldk-server/src/api/disconnect_peer.rs @@ -14,9 +14,10 @@ use ldk_server_protos::api::{DisconnectPeerRequest, DisconnectPeerResponse}; use crate::api::error::LdkServerError; use crate::service::Context; +use std::sync::Arc; -pub(crate) fn handle_disconnect_peer( - context: Context, request: DisconnectPeerRequest, +pub(crate) async fn handle_disconnect_peer( + context: Arc, request: DisconnectPeerRequest, ) -> Result { let node_id = PublicKey::from_str(&request.node_pubkey) .map_err(|_| ldk_node::NodeError::InvalidPublicKey)?; diff --git a/ldk-server/src/api/export_pathfinding_scores.rs b/ldk-server/src/api/export_pathfinding_scores.rs index a6924a22..a38c2989 100644 --- a/ldk-server/src/api/export_pathfinding_scores.rs +++ b/ldk-server/src/api/export_pathfinding_scores.rs @@ -11,9 +11,10 @@ use ldk_server_protos::api::{ExportPathfindingScoresRequest, ExportPathfindingSc use crate::api::error::LdkServerError; use crate::service::Context; +use std::sync::Arc; -pub(crate) fn handle_export_pathfinding_scores_request( - context: Context, _request: ExportPathfindingScoresRequest, +pub(crate) async fn handle_export_pathfinding_scores_request( + context: Arc, _request: ExportPathfindingScoresRequest, ) -> Result { let scores = context.node.export_pathfinding_scores()?; diff --git a/ldk-server/src/api/get_balances.rs b/ldk-server/src/api/get_balances.rs index 82ab69b7..626f632f 100644 --- a/ldk-server/src/api/get_balances.rs +++ b/ldk-server/src/api/get_balances.rs @@ -12,9 +12,10 @@ use ldk_server_protos::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 std::sync::Arc; -pub(crate) fn handle_get_balances_request( - context: Context, _request: GetBalancesRequest, +pub(crate) async fn handle_get_balances_request( + context: Arc, _request: GetBalancesRequest, ) -> Result { let balance_details = context.node.list_balances(); diff --git a/ldk-server/src/api/get_node_info.rs b/ldk-server/src/api/get_node_info.rs index c1b81279..a84a4408 100644 --- a/ldk-server/src/api/get_node_info.rs +++ b/ldk-server/src/api/get_node_info.rs @@ -12,9 +12,10 @@ use ldk_server_protos::types::BestBlock; use crate::api::error::LdkServerError; use crate::service::Context; +use std::sync::Arc; -pub(crate) fn handle_get_node_info_request( - context: Context, _request: GetNodeInfoRequest, +pub(crate) async fn handle_get_node_info_request( + context: Arc, _request: GetNodeInfoRequest, ) -> Result { let node_status = context.node.status(); diff --git a/ldk-server/src/api/get_payment_details.rs b/ldk-server/src/api/get_payment_details.rs index 1d7b266a..bfa1badf 100644 --- a/ldk-server/src/api/get_payment_details.rs +++ b/ldk-server/src/api/get_payment_details.rs @@ -15,9 +15,10 @@ use crate::api::error::LdkServerError; use crate::api::error::LdkServerErrorCode::InvalidRequestError; use crate::service::Context; use crate::util::proto_adapter::payment_to_proto; +use std::sync::Arc; -pub(crate) fn handle_get_payment_details_request( - context: Context, request: GetPaymentDetailsRequest, +pub(crate) async fn handle_get_payment_details_request( + context: Arc, request: GetPaymentDetailsRequest, ) -> Result { let payment_id_bytes = <[u8; PaymentId::LENGTH]>::from_hex(&request.payment_id).map_err(|_| { diff --git a/ldk-server/src/api/graph_get_channel.rs b/ldk-server/src/api/graph_get_channel.rs index 3e20a2a0..4266a5bd 100644 --- a/ldk-server/src/api/graph_get_channel.rs +++ b/ldk-server/src/api/graph_get_channel.rs @@ -13,9 +13,10 @@ 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 std::sync::Arc; -pub(crate) fn handle_graph_get_channel_request( - context: Context, request: GraphGetChannelRequest, +pub(crate) async fn handle_graph_get_channel_request( + context: Arc, request: GraphGetChannelRequest, ) -> Result { let channel_info = context.node.network_graph().channel(request.short_channel_id).ok_or_else(|| { diff --git a/ldk-server/src/api/graph_get_node.rs b/ldk-server/src/api/graph_get_node.rs index 9ddb0eac..71595592 100644 --- a/ldk-server/src/api/graph_get_node.rs +++ b/ldk-server/src/api/graph_get_node.rs @@ -14,9 +14,10 @@ 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 std::sync::Arc; -pub(crate) fn handle_graph_get_node_request( - context: Context, request: GraphGetNodeRequest, +pub(crate) async fn handle_graph_get_node_request( + context: Arc, request: GraphGetNodeRequest, ) -> Result { let node_id: NodeId = request.node_id.parse().map_err(|_| { LdkServerError::new( diff --git a/ldk-server/src/api/graph_list_channels.rs b/ldk-server/src/api/graph_list_channels.rs index 60543292..6f82cba7 100644 --- a/ldk-server/src/api/graph_list_channels.rs +++ b/ldk-server/src/api/graph_list_channels.rs @@ -11,9 +11,10 @@ use ldk_server_protos::api::{GraphListChannelsRequest, GraphListChannelsResponse use crate::api::error::LdkServerError; use crate::service::Context; +use std::sync::Arc; -pub(crate) fn handle_graph_list_channels_request( - context: Context, _request: GraphListChannelsRequest, +pub(crate) async fn handle_graph_list_channels_request( + context: Arc, _request: GraphListChannelsRequest, ) -> Result { let short_channel_ids = context.node.network_graph().list_channels(); diff --git a/ldk-server/src/api/graph_list_nodes.rs b/ldk-server/src/api/graph_list_nodes.rs index 64ccd295..fb9827b3 100644 --- a/ldk-server/src/api/graph_list_nodes.rs +++ b/ldk-server/src/api/graph_list_nodes.rs @@ -11,9 +11,10 @@ use ldk_server_protos::api::{GraphListNodesRequest, GraphListNodesResponse}; use crate::api::error::LdkServerError; use crate::service::Context; +use std::sync::Arc; -pub(crate) fn handle_graph_list_nodes_request( - context: Context, _request: GraphListNodesRequest, +pub(crate) async fn handle_graph_list_nodes_request( + context: Arc, _request: GraphListNodesRequest, ) -> Result { let node_ids = context.node.network_graph().list_nodes().into_iter().map(|n| n.to_string()).collect(); diff --git a/ldk-server/src/api/list_channels.rs b/ldk-server/src/api/list_channels.rs index 84d1584b..1792c2dc 100644 --- a/ldk-server/src/api/list_channels.rs +++ b/ldk-server/src/api/list_channels.rs @@ -12,9 +12,10 @@ use ldk_server_protos::api::{ListChannelsRequest, ListChannelsResponse}; use crate::api::error::LdkServerError; use crate::service::Context; use crate::util::proto_adapter::channel_to_proto; +use std::sync::Arc; -pub(crate) fn handle_list_channels_request( - context: Context, _request: ListChannelsRequest, +pub(crate) async fn handle_list_channels_request( + context: Arc, _request: ListChannelsRequest, ) -> Result { let channels = context.node.list_channels().into_iter().map(channel_to_proto).collect(); diff --git a/ldk-server/src/api/list_forwarded_payments.rs b/ldk-server/src/api/list_forwarded_payments.rs index 78ce3dc4..25e7238e 100644 --- a/ldk-server/src/api/list_forwarded_payments.rs +++ b/ldk-server/src/api/list_forwarded_payments.rs @@ -19,9 +19,10 @@ use crate::io::persist::{ FORWARDED_PAYMENTS_PERSISTENCE_SECONDARY_NAMESPACE, }; use crate::service::Context; +use std::sync::Arc; -pub(crate) fn handle_list_forwarded_payments_request( - context: Context, request: ListForwardedPaymentsRequest, +pub(crate) async fn handle_list_forwarded_payments_request( + context: Arc, request: ListForwardedPaymentsRequest, ) -> Result { let page_token = request.page_token.map(|p| (p.token, p.index)); let list_response = context diff --git a/ldk-server/src/api/list_payments.rs b/ldk-server/src/api/list_payments.rs index fbaf7c55..e5dabd3a 100644 --- a/ldk-server/src/api/list_payments.rs +++ b/ldk-server/src/api/list_payments.rs @@ -18,9 +18,10 @@ use crate::io::persist::{ PAYMENTS_PERSISTENCE_PRIMARY_NAMESPACE, PAYMENTS_PERSISTENCE_SECONDARY_NAMESPACE, }; use crate::service::Context; +use std::sync::Arc; -pub(crate) fn handle_list_payments_request( - context: Context, request: ListPaymentsRequest, +pub(crate) async fn handle_list_payments_request( + context: Arc, request: ListPaymentsRequest, ) -> Result { let page_token = request.page_token.map(|p| (p.token, p.index)); let list_response = context diff --git a/ldk-server/src/api/list_peers.rs b/ldk-server/src/api/list_peers.rs index c75e119e..3d4032f5 100644 --- a/ldk-server/src/api/list_peers.rs +++ b/ldk-server/src/api/list_peers.rs @@ -12,9 +12,10 @@ use ldk_server_protos::api::{ListPeersRequest, ListPeersResponse}; use crate::api::error::LdkServerError; use crate::service::Context; use crate::util::proto_adapter::peer_to_proto; +use std::sync::Arc; -pub(crate) fn handle_list_peers_request( - context: Context, _request: ListPeersRequest, +pub(crate) async fn handle_list_peers_request( + context: Arc, _request: ListPeersRequest, ) -> Result { let peers = context.node.list_peers().into_iter().map(peer_to_proto).collect(); diff --git a/ldk-server/src/api/onchain_receive.rs b/ldk-server/src/api/onchain_receive.rs index cad2837e..15414141 100644 --- a/ldk-server/src/api/onchain_receive.rs +++ b/ldk-server/src/api/onchain_receive.rs @@ -11,9 +11,10 @@ use ldk_server_protos::api::{OnchainReceiveRequest, OnchainReceiveResponse}; use crate::api::error::LdkServerError; use crate::service::Context; +use std::sync::Arc; -pub(crate) fn handle_onchain_receive_request( - context: Context, _request: OnchainReceiveRequest, +pub(crate) async fn handle_onchain_receive_request( + context: Arc, _request: OnchainReceiveRequest, ) -> Result { let response = OnchainReceiveResponse { address: context.node.onchain_payment().new_address()?.to_string(), diff --git a/ldk-server/src/api/onchain_send.rs b/ldk-server/src/api/onchain_send.rs index 6eb1d63a..c6b0011f 100644 --- a/ldk-server/src/api/onchain_send.rs +++ b/ldk-server/src/api/onchain_send.rs @@ -15,9 +15,10 @@ use ldk_server_protos::api::{OnchainSendRequest, OnchainSendResponse}; use crate::api::error::LdkServerError; use crate::api::error::LdkServerErrorCode::InvalidRequestError; use crate::service::Context; +use std::sync::Arc; -pub(crate) fn handle_onchain_send_request( - context: Context, request: OnchainSendRequest, +pub(crate) async fn handle_onchain_send_request( + context: Arc, request: OnchainSendRequest, ) -> Result { let address = Address::from_str(&request.address) .map_err(|_| ldk_node::NodeError::InvalidAddress)? diff --git a/ldk-server/src/api/open_channel.rs b/ldk-server/src/api/open_channel.rs index 6c470b71..89215cb1 100644 --- a/ldk-server/src/api/open_channel.rs +++ b/ldk-server/src/api/open_channel.rs @@ -17,9 +17,10 @@ use ldk_server_protos::api::{OpenChannelRequest, OpenChannelResponse}; use crate::api::build_channel_config_from_proto; use crate::api::error::LdkServerError; use crate::service::Context; +use std::sync::Arc; -pub(crate) fn handle_open_channel( - context: Context, request: OpenChannelRequest, +pub(crate) async fn handle_open_channel( + context: Arc, request: OpenChannelRequest, ) -> Result { let node_id = PublicKey::from_str(&request.node_pubkey) .map_err(|_| ldk_node::NodeError::InvalidPublicKey)?; diff --git a/ldk-server/src/api/sign_message.rs b/ldk-server/src/api/sign_message.rs index 8ef0e015..86cd78ea 100644 --- a/ldk-server/src/api/sign_message.rs +++ b/ldk-server/src/api/sign_message.rs @@ -11,9 +11,10 @@ use ldk_server_protos::api::{SignMessageRequest, SignMessageResponse}; use crate::api::error::LdkServerError; use crate::service::Context; +use std::sync::Arc; -pub(crate) fn handle_sign_message_request( - context: Context, request: SignMessageRequest, +pub(crate) async fn handle_sign_message_request( + context: Arc, request: SignMessageRequest, ) -> Result { let signature = context.node.sign_message(&request.message); diff --git a/ldk-server/src/api/splice_channel.rs b/ldk-server/src/api/splice_channel.rs index 5d6edfec..30496120 100644 --- a/ldk-server/src/api/splice_channel.rs +++ b/ldk-server/src/api/splice_channel.rs @@ -19,9 +19,10 @@ use ldk_server_protos::api::{ use crate::api::error::LdkServerError; use crate::api::error::LdkServerErrorCode::InvalidRequestError; use crate::service::Context; +use std::sync::Arc; -pub(crate) fn handle_splice_in_request( - context: Context, request: SpliceInRequest, +pub(crate) async fn handle_splice_in_request( + context: Arc, request: SpliceInRequest, ) -> Result { let user_channel_id = parse_user_channel_id(&request.user_channel_id)?; let counterparty_node_id = parse_counterparty_node_id(&request.counterparty_node_id)?; @@ -31,8 +32,8 @@ pub(crate) fn handle_splice_in_request( Ok(SpliceInResponse {}) } -pub(crate) fn handle_splice_out_request( - context: Context, request: SpliceOutRequest, +pub(crate) async fn handle_splice_out_request( + context: Arc, request: SpliceOutRequest, ) -> Result { let user_channel_id = parse_user_channel_id(&request.user_channel_id)?; let counterparty_node_id = parse_counterparty_node_id(&request.counterparty_node_id)?; diff --git a/ldk-server/src/api/spontaneous_send.rs b/ldk-server/src/api/spontaneous_send.rs index 77fe3b8a..43c9997d 100644 --- a/ldk-server/src/api/spontaneous_send.rs +++ b/ldk-server/src/api/spontaneous_send.rs @@ -16,9 +16,10 @@ use crate::api::build_route_parameters_config_from_proto; use crate::api::error::LdkServerError; use crate::api::error::LdkServerErrorCode::InvalidRequestError; use crate::service::Context; +use std::sync::Arc; -pub(crate) fn handle_spontaneous_send_request( - context: Context, request: SpontaneousSendRequest, +pub(crate) async fn handle_spontaneous_send_request( + context: Arc, request: SpontaneousSendRequest, ) -> Result { let node_id = PublicKey::from_str(&request.node_id).map_err(|_| { LdkServerError::new(InvalidRequestError, "Invalid node_id provided.".to_string()) diff --git a/ldk-server/src/api/unified_send.rs b/ldk-server/src/api/unified_send.rs index 3f7807b4..a0a1b4c5 100644 --- a/ldk-server/src/api/unified_send.rs +++ b/ldk-server/src/api/unified_send.rs @@ -10,24 +10,22 @@ use ldk_node::payment::UnifiedPaymentResult; use ldk_server_protos::api::unified_send_response::PaymentResult; use ldk_server_protos::api::{UnifiedSendRequest, UnifiedSendResponse}; -use tokio::runtime::Handle; use crate::api::build_route_parameters_config_from_proto; use crate::api::error::LdkServerError; use crate::service::Context; +use std::sync::Arc; -pub(crate) fn handle_unified_send_request( - context: Context, request: UnifiedSendRequest, +pub(crate) async fn handle_unified_send_request( + context: Arc, request: UnifiedSendRequest, ) -> Result { let route_parameters = build_route_parameters_config_from_proto(request.route_parameters)?; - let result = tokio::task::block_in_place(|| { - Handle::current().block_on(context.node.unified_payment().send( - &request.uri, - request.amount_msat, - route_parameters, - )) - })?; + let result = context + .node + .unified_payment() + .send(&request.uri, request.amount_msat, route_parameters) + .await?; let payment_result = match result { UnifiedPaymentResult::Onchain { txid } => PaymentResult::Txid(txid.to_string()), diff --git a/ldk-server/src/api/update_channel_config.rs b/ldk-server/src/api/update_channel_config.rs index 780374c5..207d3b04 100644 --- a/ldk-server/src/api/update_channel_config.rs +++ b/ldk-server/src/api/update_channel_config.rs @@ -17,9 +17,10 @@ use crate::api::build_channel_config_from_proto; use crate::api::error::LdkServerError; use crate::api::error::LdkServerErrorCode::{InvalidRequestError, LightningError}; use crate::service::Context; +use std::sync::Arc; -pub(crate) fn handle_update_channel_config_request( - context: Context, request: UpdateChannelConfigRequest, +pub(crate) async fn handle_update_channel_config_request( + context: Arc, request: UpdateChannelConfigRequest, ) -> Result { let user_channel_id: u128 = request .user_channel_id diff --git a/ldk-server/src/api/verify_signature.rs b/ldk-server/src/api/verify_signature.rs index 9b7551ea..5b1125a0 100644 --- a/ldk-server/src/api/verify_signature.rs +++ b/ldk-server/src/api/verify_signature.rs @@ -15,9 +15,10 @@ use ldk_server_protos::api::{VerifySignatureRequest, VerifySignatureResponse}; use crate::api::error::LdkServerError; use crate::api::error::LdkServerErrorCode::InvalidRequestError; use crate::service::Context; +use std::sync::Arc; -pub(crate) fn handle_verify_signature_request( - context: Context, request: VerifySignatureRequest, +pub(crate) async fn handle_verify_signature_request( + context: Arc, request: VerifySignatureRequest, ) -> Result { let public_key = PublicKey::from_str(&request.public_key).map_err(|_| { LdkServerError::new(InvalidRequestError, "Invalid public_key provided.".to_string()) diff --git a/ldk-server/src/service.rs b/ldk-server/src/service.rs index 679cb894..ceaed478 100644 --- a/ldk-server/src/service.rs +++ b/ldk-server/src/service.rs @@ -501,7 +501,8 @@ impl Service> for NodeService { async fn handle_request< T: Message + Default, R: Message, - F: Fn(Context, T) -> Result, + Fut: Future> + Send, + F: Fn(Arc, T) -> Fut + Send, >( context: Context, request: Request, auth_params: AuthParams, api_key: String, handler: F, @@ -536,7 +537,7 @@ async fn handle_request< } match T::decode(bytes) { - Ok(request) => match handler(context, request) { + 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. From 12ad04490ca3c7bdb924828965fa487f4a249354 Mon Sep 17 00:00:00 2001 From: benthecarman Date: Fri, 27 Mar 2026 13:33:48 -0500 Subject: [PATCH 3/8] Rename ldk-server-protos to ldk-server-grpc Rename the crate and move shared gRPC wire protocol primitives (framing, status codes, percent encode/decode, timeout parsing) into ldk-server-grpc so both server and client can reuse them without duplicating code or pulling in each other's dependencies. Server-specific helpers (GrpcBody, response builders, request validation) remain in the server crate and re-export the shared items so existing imports are unaffected. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/check-protos.yml | 8 +- CLAUDE.md | 2 +- CONTRIBUTING.md | 4 +- Cargo.lock | 1161 +++++++++++------ Cargo.toml | 2 +- e2e-tests/Cargo.lock | 414 +++--- e2e-tests/Cargo.toml | 2 +- e2e-tests/build.rs | 4 +- e2e-tests/src/lib.rs | 4 +- e2e-tests/tests/e2e.rs | 6 +- ldk-server-client/Cargo.toml | 4 +- .../Cargo.toml | 17 +- .../build.rs | 0 .../src/api.rs | 117 +- .../src/endpoints.rs | 4 + .../src/error.rs | 0 .../src/events.rs | 0 ldk-server-grpc/src/grpc.rs | 796 +++++++++++ .../src/lib.rs | 1 + .../src/proto/api.proto | 0 .../src/proto/error.proto | 0 .../src/proto/events.proto | 0 .../src/proto/types.proto | 0 .../src/serde_utils.rs | 0 .../src/types.rs | 0 ldk-server/Cargo.toml | 2 +- ldk-server/src/api/bolt11_claim_for_hash.rs | 2 +- ldk-server/src/api/bolt11_fail_for_hash.rs | 2 +- ldk-server/src/api/bolt11_receive.rs | 2 +- ldk-server/src/api/bolt11_receive_for_hash.rs | 2 +- .../src/api/bolt11_receive_via_jit_channel.rs | 2 +- ldk-server/src/api/bolt11_send.rs | 2 +- ldk-server/src/api/bolt12_receive.rs | 2 +- ldk-server/src/api/bolt12_send.rs | 2 +- ldk-server/src/api/close_channel.rs | 2 +- ldk-server/src/api/connect_peer.rs | 2 +- ldk-server/src/api/decode_invoice.rs | 4 +- ldk-server/src/api/decode_offer.rs | 8 +- ldk-server/src/api/disconnect_peer.rs | 2 +- .../src/api/export_pathfinding_scores.rs | 2 +- ldk-server/src/api/get_balances.rs | 2 +- ldk-server/src/api/get_node_info.rs | 4 +- ldk-server/src/api/get_payment_details.rs | 2 +- ldk-server/src/api/graph_get_channel.rs | 2 +- ldk-server/src/api/graph_get_node.rs | 2 +- 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 | 2 +- ldk-server/src/api/list_forwarded_payments.rs | 4 +- ldk-server/src/api/list_payments.rs | 4 +- ldk-server/src/api/list_peers.rs | 2 +- ldk-server/src/api/mod.rs | 8 +- ldk-server/src/api/onchain_receive.rs | 2 +- ldk-server/src/api/onchain_send.rs | 2 +- ldk-server/src/api/open_channel.rs | 2 +- ldk-server/src/api/sign_message.rs | 2 +- ldk-server/src/api/splice_channel.rs | 2 +- ldk-server/src/api/spontaneous_send.rs | 2 +- ldk-server/src/api/unified_send.rs | 4 +- ldk-server/src/api/update_channel_config.rs | 2 +- ldk-server/src/api/verify_signature.rs | 2 +- ldk-server/src/main.rs | 7 +- ldk-server/src/util/proto_adapter.rs | 140 +- 63 files changed, 1976 insertions(+), 811 deletions(-) rename {ldk-server-protos => ldk-server-grpc}/Cargo.toml (54%) rename {ldk-server-protos => ldk-server-grpc}/build.rs (100%) rename {ldk-server-protos => ldk-server-grpc}/src/api.rs (88%) rename {ldk-server-protos => ldk-server-grpc}/src/endpoints.rs (93%) rename {ldk-server-protos => ldk-server-grpc}/src/error.rs (100%) rename {ldk-server-protos => ldk-server-grpc}/src/events.rs (100%) create mode 100644 ldk-server-grpc/src/grpc.rs rename {ldk-server-protos => ldk-server-grpc}/src/lib.rs (97%) rename {ldk-server-protos => ldk-server-grpc}/src/proto/api.proto (100%) rename {ldk-server-protos => ldk-server-grpc}/src/proto/error.proto (100%) rename {ldk-server-protos => ldk-server-grpc}/src/proto/events.proto (100%) rename {ldk-server-protos => ldk-server-grpc}/src/proto/types.proto (100%) rename {ldk-server-protos => ldk-server-grpc}/src/serde_utils.rs (100%) rename {ldk-server-protos => ldk-server-grpc}/src/types.rs (100%) diff --git a/.github/workflows/check-protos.yml b/.github/workflows/check-protos.yml index e2ad8661..23a7b594 100644 --- a/.github/workflows/check-protos.yml +++ b/.github/workflows/check-protos.yml @@ -3,10 +3,10 @@ name: Check Generated Protos on: push: paths: - - 'ldk-server-protos/**' + - 'ldk-server-grpc/**' pull_request: paths: - - 'ldk-server-protos/**' + - 'ldk-server-grpc/**' workflow_dispatch: permissions: @@ -28,12 +28,12 @@ jobs: - name: Install protoc run: sudo apt-get install -y protobuf-compiler - name: Generate protos - run: RUSTFLAGS="--cfg genproto" cargo build -p ldk-server-protos + run: RUSTFLAGS="--cfg genproto" cargo build -p ldk-server-grpc - 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" + echo "error: Generated protobuf files are out of date. Run: RUSTFLAGS=\"--cfg genproto\" cargo build -p ldk-server-grpc && cargo fmt --all" exit 1 fi diff --git a/CLAUDE.md b/CLAUDE.md index aa1da645..49fb0361 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-grpc** - Protocol buffer definitions, generated Rust code, and shared gRPC primitives ## Development Rules diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9a783dfb..ecaa51ec 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -40,13 +40,13 @@ cargo clippy --all-features -- -D warnings -A clippy::drop_non_drop # Lint (CI ## Protocol Buffer Generation ```bash -RUSTFLAGS="--cfg genproto" cargo build -p ldk-server-protos +RUSTFLAGS="--cfg genproto" cargo build -p ldk-server-grpc cargo fmt --all ``` ## Adding a New API Endpoint -1. Define request/response messages in `ldk-server-protos/src/proto/api.proto` +1. Define request/response messages in `ldk-server-grpc/src/proto/api.proto` 2. Regenerate protos (see above) 3. Create handler in `ldk-server/src/api/` (follow existing patterns) 4. Add route in `ldk-server/src/service.rs` diff --git a/Cargo.lock b/Cargo.lock index 2861a649..7ba5aea5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -93,15 +93,15 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" [[package]] name = "anyhow" -version = "1.0.100" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "arrayvec" @@ -133,7 +133,7 @@ checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.117", "synstructure", ] @@ -145,7 +145,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.117", ] [[package]] @@ -162,13 +162,13 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.13.3" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497c00e0fd83a72a79a39fcbd8e3e2f055d6f6c7e025f3b3d91f4f8e76527fb8" +checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" dependencies = [ "async-task", "concurrent-queue", - "fastrand 2.3.0", + "fastrand 2.4.1", "futures-lite 2.6.1", "pin-project-lite", "slab", @@ -183,7 +183,7 @@ dependencies = [ "async-channel", "async-executor", "async-io 2.6.0", - "async-lock 3.4.1", + "async-lock 3.4.2", "blocking", "futures-lite 2.6.1", ] @@ -232,7 +232,7 @@ dependencies = [ "futures-lite 2.6.1", "parking", "polling 3.11.0", - "rustix 1.1.2", + "rustix 1.1.4", "slab", "windows-sys 0.61.2", ] @@ -248,9 +248,9 @@ dependencies = [ [[package]] name = "async-lock" -version = "3.4.1" +version = "3.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" dependencies = [ "event-listener 5.4.1", "event-listener-strategy", @@ -283,7 +283,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.117", ] [[package]] @@ -298,14 +298,59 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "axum" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +dependencies = [ + "async-trait", + "axum-core", + "bitflags 1.3.2", + "bytes", + "futures-util", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.32", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "sync_wrapper 0.1.2", + "tower 0.4.13", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 0.2.12", + "http-body 0.4.6", + "mime", + "rustversion", + "tower-layer", + "tower-service", +] + [[package]] name = "base58ck" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c8d66485a3a2ea485c1913c4572ce0256067a5377ac8c75c4960e1cda98605f" dependencies = [ - "bitcoin-internals 0.3.0", - "bitcoin_hashes 0.14.0", + "bitcoin-internals", + "bitcoin_hashes", ] [[package]] @@ -322,15 +367,15 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.8.0" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "bdk_chain" -version = "0.23.2" +version = "0.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b5d691fd092aacec7e05046b7d04897d58d6d65ed3152cb6cf65dababcfabed" +checksum = "c290eff038799a8ac0c5a82b6160a9ca456baa299a6f22b262c771342d2846c0" dependencies = [ "bdk_core", "bitcoin", @@ -340,9 +385,9 @@ dependencies = [ [[package]] name = "bdk_core" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dbbe4aad0c898bfeb5253c222be3ea3dccfb380a07e72c87e3e4ed6664a6753" +checksum = "cb3028782f6bf14a6df987244333d34e6b272b5a40a53e4879ec2dfd82275a3a" dependencies = [ "bitcoin", "hashbrown 0.14.5", @@ -361,9 +406,9 @@ dependencies = [ [[package]] name = "bdk_esplora" -version = "0.22.1" +version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9f5961444b5f51b9c3937e729a212363d0e4cde6390ded6e01e16292078df4" +checksum = "83986307ea92997c3d051e8c306af8115a05add601e22acb7c1903008e6b614e" dependencies = [ "async-trait", "bdk_core", @@ -388,9 +433,9 @@ dependencies = [ [[package]] name = "bech32" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" +checksum = "32637268377fc7b10a8c6d51de3e7fba1ce5dd371a96e342b34e6078db558e7f" [[package]] name = "bip21" @@ -404,11 +449,11 @@ dependencies = [ [[package]] name = "bip39" -version = "2.2.0" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d193de1f7487df1914d3a568b772458861d33f9c54249612cc2893d6915054" +checksum = "90dbd31c98227229239363921e60fcf5e558e43ec69094d46fc4996f08d1d5bc" dependencies = [ - "bitcoin_hashes 0.13.0", + "bitcoin_hashes", "rand 0.8.5", "rand_core 0.6.4", "serde", @@ -417,29 +462,23 @@ dependencies = [ [[package]] name = "bitcoin" -version = "0.32.7" +version = "0.32.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda569d741b895131a88ee5589a467e73e9c4718e958ac9308e4f7dc44b6945" +checksum = "1e499f9fc0407f50fe98af744ab44fa67d409f76b6772e1689ec8485eb0c0f66" dependencies = [ "base58ck", "base64 0.21.7", "bech32", - "bitcoin-internals 0.3.0", + "bitcoin-internals", "bitcoin-io", "bitcoin-units", - "bitcoin_hashes 0.14.0", - "hex-conservative 0.2.1", + "bitcoin_hashes", + "hex-conservative", "hex_lit", "secp256k1", "serde", ] -[[package]] -name = "bitcoin-internals" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9425c3bf7089c983facbae04de54513cce73b41c7f9ff8c845b54e7bc64ebbfb" - [[package]] name = "bitcoin-internals" version = "0.3.0" @@ -451,9 +490,9 @@ dependencies = [ [[package]] name = "bitcoin-io" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf" +checksum = "2dee39a0ee5b4095224a0cfc6bf4cc1baf0f9624b96b367e53b66d974e51d953" [[package]] name = "bitcoin-payment-instructions" @@ -473,28 +512,18 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5285c8bcaa25876d07f37e3d30c303f2609179716e11d688f51e8f1fe70063e2" dependencies = [ - "bitcoin-internals 0.3.0", + "bitcoin-internals", "serde", ] [[package]] name = "bitcoin_hashes" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1930a4dabfebb8d7d9992db18ebe3ae2876f0a305fab206fd168df931ede293b" -dependencies = [ - "bitcoin-internals 0.2.0", - "hex-conservative 0.1.2", -] - -[[package]] -name = "bitcoin_hashes" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" +checksum = "26ec84b80c482df901772e931a9a681e26a1b9ee2302edeff23cb30328745c8b" dependencies = [ "bitcoin-io", - "hex-conservative 0.2.1", + "hex-conservative", "serde", ] @@ -506,9 +535,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" [[package]] name = "bitreq" @@ -558,9 +587,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] name = "byteorder" @@ -570,9 +599,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.10.1" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" dependencies = [ "serde", ] @@ -588,9 +617,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.43" +version = "1.2.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "739eb0f94557554b3ca9a86d2d37bebd49c5e6d0c1d2bda35ba5bdac830befc2" +checksum = "b7a4d3ec6524d28a329fc53654bbadc9bdd7b0431f5d65f1a56ffb28a1ee5283" dependencies = [ "find-msvc-tools", "shlex", @@ -616,9 +645,9 @@ checksum = "4b4b0fc281743d80256607bd65e8beedc42cb0787ea119c85b81b4c0eab85e5f" [[package]] name = "chrono" -version = "0.4.42" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" dependencies = [ "iana-time-zone", "num-traits", @@ -638,9 +667,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.51" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5" +checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351" dependencies = [ "clap_builder", "clap_derive", @@ -648,9 +677,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.51" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" dependencies = [ "anstyle", "clap_lex", @@ -659,30 +688,30 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.65" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "430b4dc2b5e3861848de79627b2bedc9f3342c7da5173a14eaa5d0f8dc18ae5d" +checksum = "19c9f1dde76b736e3681f28cec9d5a61299cbaae0fce80a68e43724ad56031eb" dependencies = [ "clap", ] [[package]] name = "clap_derive" -version = "4.5.49" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" +checksum = "1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.117", ] [[package]] name = "clap_lex" -version = "0.7.6" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" [[package]] name = "cms" @@ -750,9 +779,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", "typenum", @@ -760,9 +789,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" +checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" [[package]] name = "der" @@ -799,14 +828,14 @@ checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.117", ] [[package]] name = "deranged" -version = "0.5.5" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ "powerfmt", ] @@ -839,7 +868,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.117", ] [[package]] @@ -848,7 +877,7 @@ version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec4f825369fc7134da70ca4040fddc8e03b80a46d249ae38d9c1c39b7b4476bf" dependencies = [ - "bitcoin_hashes 0.14.0", + "bitcoin_hashes", "tokio", ] @@ -866,15 +895,15 @@ checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "electrum-client" -version = "0.24.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7b07e2578a6df0093b101915c79dca0119d7f7810099ad9eef11341d2ae57" +checksum = "a5059f13888a90486e7268bbce59b175f5f76b1c55e5b9c568ceaa42d2b8507c" dependencies = [ "bitcoin", "byteorder", "libc", "log", - "rustls 0.23.34", + "rustls 0.23.37", "serde", "serde_json", "webpki-roots 0.25.4", @@ -908,15 +937,16 @@ dependencies = [ [[package]] name = "esplora-client" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0af349d96a5d9ad77ba59f1437aa6f348b03c5865d4f7d6e7a662d60aedce39" +checksum = "f19e3ea99dbfbef0c1ec26d83e69de0c579f6aa6aaac4f44597805fcc27e97af" dependencies = [ "bitcoin", - "hex-conservative 0.2.1", + "hex-conservative", "log", - "reqwest 0.12.24", + "reqwest 0.12.28", "serde", + "serde_json", "tokio", ] @@ -979,15 +1009,15 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.3.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" [[package]] name = "find-msvc-tools" -version = "0.1.4" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "fixedbitset" @@ -1018,6 +1048,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "form_urlencoded" version = "1.2.2" @@ -1029,9 +1065,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" dependencies = [ "futures-channel", "futures-core", @@ -1044,9 +1080,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ "futures-core", "futures-sink", @@ -1054,15 +1090,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] name = "futures-executor" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" dependencies = [ "futures-core", "futures-task", @@ -1071,9 +1107,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" [[package]] name = "futures-lite" @@ -1096,7 +1132,7 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" dependencies = [ - "fastrand 2.3.0", + "fastrand 2.4.1", "futures-core", "futures-io", "parking", @@ -1105,32 +1141,32 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.117", ] [[package]] name = "futures-sink" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" [[package]] name = "futures-task" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-util" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-channel", "futures-core", @@ -1140,15 +1176,14 @@ dependencies = [ "futures-task", "memchr", "pin-project-lite", - "pin-utils", "slab", ] [[package]] name = "generic-array" -version = "0.14.9" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -1156,9 +1191,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", "js-sys", @@ -1176,11 +1211,24 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "r-efi", + "r-efi 5.3.0", "wasip2", "wasm-bindgen", ] +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", + "wasip2", + "wasip3", +] + [[package]] name = "h2" version = "0.3.27" @@ -1193,13 +1241,38 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap", + "indexmap 2.13.1", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "h2" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.4.0", + "indexmap 2.13.1", "slab", "tokio", "tokio-util", "tracing", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "hashbrown" version = "0.13.2" @@ -1218,9 +1291,18 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.16.0" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" [[package]] name = "hashlink" @@ -1263,15 +1345,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hex-conservative" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212ab92002354b4819390025006c897e8140934349e8635c9b077f47b4dcbd20" - -[[package]] -name = "hex-conservative" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd" +checksum = "fda06d18ac606267c40c04e41b9947729bf8b9efe74bd4e82b61a5f26a510b9f" dependencies = [ "arrayvec", ] @@ -1313,12 +1389,11 @@ dependencies = [ [[package]] name = "http" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" dependencies = [ "bytes", - "fnv", "itoa", ] @@ -1340,7 +1415,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.3.1", + "http 1.4.0", ] [[package]] @@ -1351,7 +1426,7 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "pin-project-lite", ] @@ -1378,7 +1453,7 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", + "h2 0.3.27", "http 0.2.12", "http-body 0.4.6", "httparse", @@ -1394,21 +1469,21 @@ dependencies = [ [[package]] name = "hyper" -version = "1.7.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" +checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" dependencies = [ "atomic-waker", "bytes", "futures-channel", "futures-core", - "http 1.3.1", + "h2 0.4.13", + "http 1.4.0", "http-body 1.0.1", "httparse", "httpdate", "itoa", "pin-project-lite", - "pin-utils", "smallvec", "tokio", "want", @@ -1434,36 +1509,47 @@ version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ - "http 1.3.1", - "hyper 1.7.0", + "http 1.4.0", + "hyper 1.9.0", "hyper-util", - "rustls 0.23.34", + "rustls 0.23.37", "rustls-pki-types", "tokio", "tokio-rustls 0.26.4", "tower-service", - "webpki-roots 1.0.3", + "webpki-roots 1.0.6", +] + +[[package]] +name = "hyper-timeout" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +dependencies = [ + "hyper 0.14.32", + "pin-project-lite", + "tokio", + "tokio-io-timeout", ] [[package]] name = "hyper-util" -version = "0.1.17" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" dependencies = [ "base64 0.22.1", "bytes", "futures-channel", - "futures-core", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", - "hyper 1.7.0", + "hyper 1.9.0", "ipnet", "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.1", + "socket2 0.6.3", "tokio", "tower-service", "tracing", @@ -1471,9 +1557,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.64" +version = "0.1.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -1495,12 +1581,13 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" dependencies = [ "displaydoc", "potential_utf", + "utf8_iter", "yoke", "zerofrom", "zerovec", @@ -1508,9 +1595,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" dependencies = [ "displaydoc", "litemap", @@ -1521,9 +1608,9 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" dependencies = [ "icu_collections", "icu_normalizer_data", @@ -1535,15 +1622,15 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" [[package]] name = "icu_properties" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" dependencies = [ "icu_collections", "icu_locale_core", @@ -1555,15 +1642,15 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" [[package]] name = "icu_provider" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" dependencies = [ "displaydoc", "icu_locale_core", @@ -1574,6 +1661,12 @@ dependencies = [ "zerovec", ] +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + [[package]] name = "idna" version = "1.1.0" @@ -1597,12 +1690,24 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.12.0" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" +checksum = "45a8a2b9cb3e0b0c1803dbb0758ffac5de2f425b23c28f518faabd9d805342ff" dependencies = [ "equivalent", - "hashbrown 0.16.0", + "hashbrown 0.16.1", + "serde", + "serde_core", ] [[package]] @@ -1637,15 +1742,15 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.11.0" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" [[package]] name = "iri-string" -version = "0.7.8" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +checksum = "25e659a4bb38e810ebc252e53b5814ff908a8c58c2a9ce2fae1bbec24cbf4e20" dependencies = [ "memchr", "serde", @@ -1662,16 +1767,18 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "js-sys" -version = "0.3.82" +version = "0.3.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" +checksum = "2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9" dependencies = [ + "cfg-if", + "futures-util", "once_cell", "wasm-bindgen", ] @@ -1739,7 +1846,7 @@ dependencies = [ "log", "prost", "rusqlite", - "rustls 0.23.34", + "rustls 0.23.37", "serde", "serde_json", "tokio", @@ -1757,14 +1864,14 @@ dependencies = [ "chrono", "clap", "futures-util", - "getrandom 0.2.16", - "hex-conservative 0.2.1", + "getrandom 0.2.17", + "hex-conservative", "http-body-util", - "hyper 1.7.0", + "hyper 1.9.0", "hyper-util", "lapin", "ldk-node", - "ldk-server-protos", + "ldk-server-grpc", "log", "prost", "ring", @@ -1781,7 +1888,7 @@ version = "0.1.0" dependencies = [ "clap", "clap_complete", - "hex-conservative 0.2.1", + "hex-conservative", "ldk-server-client", "serde", "serde_json", @@ -1793,33 +1900,47 @@ dependencies = [ name = "ldk-server-client" version = "0.1.0" dependencies = [ - "bitcoin_hashes 0.14.0", - "ldk-server-protos", + "bitcoin_hashes", + "ldk-server-grpc", "prost", "reqwest 0.11.27", ] [[package]] -name = "ldk-server-protos" +name = "ldk-server-grpc" version = "0.1.0" dependencies = [ "bytes", + "http 1.4.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.9.0", + "hyper-util", "prost", "prost-build", "serde", + "tokio", + "tokio-stream", + "tonic", ] +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + [[package]] name = "libc" -version = "0.2.177" +version = "0.2.184" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" [[package]] name = "libm" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libsqlite3-sys" @@ -1856,7 +1977,7 @@ source = "git+https://github.com/lightningdevkit/rust-lightning?rev=dcf0c203e166 dependencies = [ "bitcoin", "bitcoin-io", - "bitcoin_hashes 0.14.0", + "bitcoin_hashes", "lightning", "lightning-liquidity", "lightning-rapid-gossip-sync", @@ -1908,7 +2029,7 @@ source = "git+https://github.com/lightningdevkit/rust-lightning?rev=dcf0c203e166 dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.117", ] [[package]] @@ -1939,7 +2060,7 @@ source = "git+https://github.com/lightningdevkit/rust-lightning?rev=dcf0c203e166 dependencies = [ "bitcoin", "bitcoin-io", - "bitcoin_hashes 0.14.0", + "bitcoin_hashes", "lightning", ] @@ -1978,15 +2099,15 @@ checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "linux-raw-sys" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" [[package]] name = "lock_api" @@ -2009,11 +2130,17 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + [[package]] name = "memchr" -version = "2.7.6" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "mime" @@ -2040,9 +2167,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" dependencies = [ "libc", "wasi", @@ -2085,9 +2212,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.1.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" [[package]] name = "num-integer" @@ -2118,9 +2245,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.21.3" +version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] name = "openssl-probe" @@ -2217,20 +2344,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap", + "indexmap 2.13.1", ] [[package]] -name = "pin-project-lite" -version = "0.2.16" +name = "pin-project" +version = "1.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517" +dependencies = [ + "pin-project-internal", +] [[package]] -name = "pin-utils" -version = "0.1.0" +name = "pin-project-internal" +version = "1.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] name = "pinky-swear" @@ -2246,12 +2387,12 @@ dependencies = [ [[package]] name = "piper" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" dependencies = [ "atomic-waker", - "fastrand 2.3.0", + "fastrand 2.4.1", "futures-io", ] @@ -2317,7 +2458,7 @@ dependencies = [ "concurrent-queue", "hermit-abi 0.5.2", "pin-project-lite", - "rustix 1.1.2", + "rustix 1.1.4", "windows-sys 0.61.2", ] @@ -2326,14 +2467,14 @@ name = "possiblyrandom" version = "0.2.0" source = "git+https://github.com/lightningdevkit/rust-lightning?rev=dcf0c203e166da2348bef12b2e5eff4a250cdec7#dcf0c203e166da2348bef12b2e5eff4a250cdec7" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", ] [[package]] name = "potential_utf" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" dependencies = [ "zerovec", ] @@ -2363,11 +2504,21 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.117", +] + [[package]] name = "proc-macro2" -version = "1.0.103" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] @@ -2395,7 +2546,7 @@ dependencies = [ "log", "multimap", "petgraph", - "prettyplease", + "prettyplease 0.1.25", "prost", "prost-types", "regex", @@ -2438,8 +2589,8 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash", - "rustls 0.23.34", - "socket2 0.6.1", + "rustls 0.23.37", + "socket2 0.6.3", "thiserror", "tokio", "tracing", @@ -2458,7 +2609,7 @@ dependencies = [ "rand 0.9.2", "ring", "rustc-hash", - "rustls 0.23.34", + "rustls 0.23.37", "rustls-pki-types", "slab", "thiserror", @@ -2476,16 +2627,16 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.1", + "socket2 0.6.3", "tracing", "windows-sys 0.60.2", ] [[package]] name = "quote" -version = "1.0.41" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] @@ -2496,6 +2647,12 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + [[package]] name = "rand" version = "0.8.5" @@ -2514,7 +2671,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] @@ -2534,7 +2691,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] @@ -2543,14 +2700,14 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", ] [[package]] name = "rand_core" -version = "0.9.3" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" dependencies = [ "getrandom 0.3.4", ] @@ -2581,14 +2738,14 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", ] [[package]] name = "regex" -version = "1.12.2" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", @@ -2598,9 +2755,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", @@ -2609,9 +2766,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.8" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "reqwest" @@ -2624,7 +2781,7 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "h2", + "h2 0.3.27", "http 0.2.12", "http-body 0.4.6", "hyper 0.14.32", @@ -2656,17 +2813,17 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.24" +version = "0.12.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" dependencies = [ "base64 0.22.1", "bytes", "futures-core", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "http-body-util", - "hyper 1.7.0", + "hyper 1.9.0", "hyper-rustls 0.27.7", "hyper-util", "js-sys", @@ -2674,7 +2831,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.34", + "rustls 0.23.37", "rustls-pki-types", "serde", "serde_json", @@ -2682,14 +2839,14 @@ dependencies = [ "sync_wrapper 1.0.2", "tokio", "tokio-rustls 0.26.4", - "tower", + "tower 0.5.3", "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots 1.0.3", + "webpki-roots 1.0.6", ] [[package]] @@ -2700,7 +2857,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.16", + "getrandom 0.2.17", "libc", "untrusted", "windows-sys 0.52.0", @@ -2712,7 +2869,7 @@ version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b838eba278d213a8beaf485bd313fd580ca4505a00d5871caeb1457c55322cae" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -2722,9 +2879,9 @@ dependencies = [ [[package]] name = "rustc-hash" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" [[package]] name = "rusticata-macros" @@ -2755,7 +2912,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "errno", "libc", "linux-raw-sys 0.4.15", @@ -2764,14 +2921,14 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.2" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "errno", "libc", - "linux-raw-sys 0.11.0", + "linux-raw-sys 0.12.1", "windows-sys 0.61.2", ] @@ -2789,15 +2946,15 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.34" +version = "0.23.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9586e9ee2b4f8fab52a0048ca7334d7024eef48e2cb9407e3497bb7cab7fa7" +checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" dependencies = [ "log", "once_cell", "ring", "rustls-pki-types", - "rustls-webpki 0.103.8", + "rustls-webpki 0.103.10", "subtle", "zeroize", ] @@ -2809,10 +2966,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70cc376c6ba1823ae229bacf8ad93c136d93524eab0e4e5e0e4f96b9c4e5b212" dependencies = [ "log", - "rustls 0.23.34", + "rustls 0.23.37", "rustls-native-certs", "rustls-pki-types", - "rustls-webpki 0.103.8", + "rustls-webpki 0.103.10", ] [[package]] @@ -2848,9 +3005,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.13.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" dependencies = [ "web-time", "zeroize", @@ -2868,9 +3025,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.8" +version = "0.103.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" +checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef" dependencies = [ "ring", "rustls-pki-types", @@ -2885,9 +3042,9 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" -version = "1.0.20" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" [[package]] name = "salsa20" @@ -2900,9 +3057,9 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.28" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" dependencies = [ "windows-sys 0.61.2", ] @@ -2940,7 +3097,7 @@ version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" dependencies = [ - "bitcoin_hashes 0.14.0", + "bitcoin_hashes", "rand 0.8.5", "secp256k1-sys", "serde", @@ -2961,7 +3118,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "core-foundation", "core-foundation-sys", "libc", @@ -2970,14 +3127,20 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.15.0" +version = "2.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" dependencies = [ "core-foundation-sys", "libc", ] +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + [[package]] name = "serde" version = "1.0.228" @@ -3005,20 +3168,20 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.117", ] [[package]] name = "serde_json" -version = "1.0.145" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "itoa", "memchr", - "ryu", "serde", "serde_core", + "zmij", ] [[package]] @@ -3072,18 +3235,19 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.6" +version = "1.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" dependencies = [ + "errno", "libc", ] [[package]] name = "slab" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" @@ -3113,12 +3277,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.1" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -3171,9 +3335,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.108" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -3203,7 +3367,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.117", ] [[package]] @@ -3241,63 +3405,63 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.23.0" +version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ - "fastrand 2.3.0", - "getrandom 0.3.4", + "fastrand 2.4.1", + "getrandom 0.4.2", "once_cell", - "rustix 1.1.2", + "rustix 1.1.4", "windows-sys 0.61.2", ] [[package]] name = "thiserror" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.117", ] [[package]] name = "time" -version = "0.3.44" +version = "0.3.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" dependencies = [ "deranged", "itoa", "num-conv", "powerfmt", - "serde", + "serde_core", "time-core", "time-macros", ] [[package]] name = "time-core" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" [[package]] name = "time-macros" -version = "0.2.24" +version = "0.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" dependencies = [ "num-conv", "time-core", @@ -3305,9 +3469,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" dependencies = [ "displaydoc", "zerovec", @@ -3315,9 +3479,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" dependencies = [ "tinyvec_macros", ] @@ -3330,29 +3494,39 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.48.0" +version = "1.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +checksum = "2bd1c4c0fc4a7ab90fc15ef6daaa3ec3b893f004f915f2392557ed23237820cd" dependencies = [ "bytes", "libc", "mio", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.1", + "socket2 0.6.3", "tokio-macros", "windows-sys 0.61.2", ] +[[package]] +name = "tokio-io-timeout" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bd86198d9ee903fedd2f9a2e72014287c0d9167e4ae43b5853007205dda1b76" +dependencies = [ + "pin-project-lite", + "tokio", +] + [[package]] name = "tokio-macros" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.117", ] [[package]] @@ -3371,15 +3545,26 @@ version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ - "rustls 0.23.34", + "rustls 0.23.37", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +dependencies = [ + "futures-core", + "pin-project-lite", "tokio", ] [[package]] name = "tokio-util" -version = "0.7.16" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" dependencies = [ "bytes", "futures-core", @@ -3415,18 +3600,66 @@ version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap", + "indexmap 2.13.1", "serde", "serde_spanned", "toml_datetime", "winnow", ] +[[package]] +name = "tonic" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3082666a3a6433f7f511c7192923fa1fe07c69332d3c6a2e6bb040b569199d5a" +dependencies = [ + "async-trait", + "axum", + "base64 0.21.7", + "bytes", + "futures-core", + "futures-util", + "h2 0.3.27", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.32", + "hyper-timeout", + "percent-encoding", + "pin-project", + "prost", + "tokio", + "tokio-stream", + "tower 0.4.13", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "tower" -version = "0.5.2" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "indexmap 1.9.3", + "pin-project", + "pin-project-lite", + "rand 0.8.5", + "slab", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" dependencies = [ "futures-core", "futures-util", @@ -3439,18 +3672,18 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "bytes", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "iri-string", "pin-project-lite", - "tower", + "tower 0.5.3", "tower-layer", "tower-service", ] @@ -3469,19 +3702,31 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "pin-project-lite", + "tracing-attributes", "tracing-core", ] +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "tracing-core" -version = "0.1.34" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", ] @@ -3500,19 +3745,25 @@ checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "unicode-ident" -version = "1.0.20" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "462eeb75aeb73aea900253ce739c8e18a67423fadf006037cd3ff27e82748a06" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-normalization" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "untrusted" version = "0.9.0" @@ -3521,9 +3772,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.7" +version = "2.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" dependencies = [ "form_urlencoded", "idna", @@ -3558,7 +3809,7 @@ dependencies = [ "async-trait", "base64 0.22.1", "bitcoin", - "bitcoin_hashes 0.14.0", + "bitcoin_hashes", "bitreq", "chacha20-poly1305", "log", @@ -3593,18 +3844,27 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.1+wasi-0.2.4" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" dependencies = [ "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.105" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" +checksum = "0551fc1bb415591e3372d0bc4780db7e587d84e2a7e79da121051c5c4b89d0b0" dependencies = [ "cfg-if", "once_cell", @@ -3615,22 +3875,19 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.55" +version = "0.4.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "551f88106c6d5e7ccc7cd9a16f312dd3b5d36ea8b4954304657d5dfba115d4a0" +checksum = "03623de6905b7206edd0a75f69f747f134b7f0a2323392d664448bf2d3c5d87e" dependencies = [ - "cfg-if", "js-sys", - "once_cell", "wasm-bindgen", - "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.105" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" +checksum = "7fbdf9a35adf44786aecd5ff89b4563a90325f9da0923236f6104e603c7e86be" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3638,31 +3895,65 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.105" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" +checksum = "dca9693ef2bab6d4e6707234500350d8dad079eb508dca05530c85dc3a529ff2" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.117", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.105" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" +checksum = "39129a682a6d2d841b6c429d0c51e5cb0ed1a03829d8b3d1e69a011e62cb3d3b" dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap 2.13.1", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags 2.11.0", + "hashbrown 0.15.5", + "indexmap 2.13.1", + "semver", +] + [[package]] name = "web-sys" -version = "0.3.82" +version = "0.3.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" +checksum = "cd70027e39b12f0849461e08ffc50b9cd7688d942c1c8e3c7b22273236b4dd0a" dependencies = [ "js-sys", "wasm-bindgen", @@ -3686,9 +3977,9 @@ checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "webpki-roots" -version = "1.0.3" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b130c0d2d49f8b6889abc456e795e82525204f27c42cf767cf0d7734e089b8" +checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" dependencies = [ "rustls-pki-types", ] @@ -3748,7 +4039,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.117", ] [[package]] @@ -3759,7 +4050,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.117", ] [[package]] @@ -4019,9 +4310,9 @@ checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winnow" -version = "0.7.13" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" dependencies = [ "memchr", ] @@ -4038,15 +4329,97 @@ dependencies = [ [[package]] name = "wit-bindgen" -version = "0.46.0" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck 0.5.0", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck 0.5.0", + "indexmap 2.13.1", + "prettyplease 0.2.37", + "syn 2.0.117", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease 0.2.37", + "proc-macro2", + "quote", + "syn 2.0.117", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags 2.11.0", + "indexmap 2.13.1", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap 2.13.1", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] [[package]] name = "writeable" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" [[package]] name = "x509-cert" @@ -4078,9 +4451,9 @@ dependencies = [ [[package]] name = "yoke" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" dependencies = [ "stable_deref_trait", "yoke-derive", @@ -4089,54 +4462,54 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.117", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.27" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.27" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.117", ] [[package]] name = "zerofrom" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.117", "synstructure", ] @@ -4148,9 +4521,9 @@ checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" [[package]] name = "zerotrie" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" dependencies = [ "displaydoc", "yoke", @@ -4159,9 +4532,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" dependencies = [ "yoke", "zerofrom", @@ -4170,11 +4543,17 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.117", ] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml index 2a8c8b9b..6b09eb62 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-grpc", "ldk-server"] exclude = ["e2e-tests"] [profile.release] diff --git a/e2e-tests/Cargo.lock b/e2e-tests/Cargo.lock index 823ebcc8..a65d2c21 100644 --- a/e2e-tests/Cargo.lock +++ b/e2e-tests/Cargo.lock @@ -99,9 +99,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.101" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "arrayvec" @@ -133,7 +133,7 @@ checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.116", + "syn 2.0.117", "synstructure", ] @@ -145,7 +145,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.116", + "syn 2.0.117", ] [[package]] @@ -168,7 +168,7 @@ checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" dependencies = [ "async-task", "concurrent-queue", - "fastrand 2.3.0", + "fastrand 2.4.1", "futures-lite 2.6.1", "pin-project-lite", "slab", @@ -232,7 +232,7 @@ dependencies = [ "futures-lite 2.6.1", "parking", "polling 3.11.0", - "rustix 1.1.3", + "rustix 1.1.4", "slab", "windows-sys 0.61.2", ] @@ -283,7 +283,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.116", + "syn 2.0.117", ] [[package]] @@ -334,9 +334,9 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "bdk_chain" -version = "0.23.2" +version = "0.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b5d691fd092aacec7e05046b7d04897d58d6d65ed3152cb6cf65dababcfabed" +checksum = "c290eff038799a8ac0c5a82b6160a9ca456baa299a6f22b262c771342d2846c0" dependencies = [ "bdk_core", "bitcoin", @@ -346,9 +346,9 @@ dependencies = [ [[package]] name = "bdk_core" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dbbe4aad0c898bfeb5253c222be3ea3dccfb380a07e72c87e3e4ed6664a6753" +checksum = "cb3028782f6bf14a6df987244333d34e6b272b5a40a53e4879ec2dfd82275a3a" dependencies = [ "bitcoin", "hashbrown 0.14.5", @@ -367,9 +367,9 @@ dependencies = [ [[package]] name = "bdk_esplora" -version = "0.22.1" +version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9f5961444b5f51b9c3937e729a212363d0e4cde6390ded6e01e16292078df4" +checksum = "83986307ea92997c3d051e8c306af8115a05add601e22acb7c1903008e6b614e" dependencies = [ "async-trait", "bdk_core", @@ -548,9 +548,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.20.1" +version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6f81257d10a0f602a294ae4182251151ff97dbb504ef9afcdda4a64b24d9b4" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] name = "byteorder" @@ -598,9 +598,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.56" +version = "1.2.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" +checksum = "b7a4d3ec6524d28a329fc53654bbadc9bdd7b0431f5d65f1a56ffb28a1ee5283" dependencies = [ "find-msvc-tools", "shlex", @@ -626,9 +626,9 @@ checksum = "4b4b0fc281743d80256607bd65e8beedc42cb0787ea119c85b81b4c0eab85e5f" [[package]] name = "chrono" -version = "0.4.43" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" dependencies = [ "iana-time-zone", "num-traits", @@ -814,14 +814,14 @@ checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" dependencies = [ "proc-macro2", "quote", - "syn 2.0.116", + "syn 2.0.117", ] [[package]] name = "deranged" -version = "0.5.6" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc3dc5ad92c2e2d1c193bbbbdf2ea477cb81331de4f3103f267ca18368b988c4" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ "powerfmt", ] @@ -854,7 +854,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.116", + "syn 2.0.117", ] [[package]] @@ -883,7 +883,7 @@ dependencies = [ "lapin", "ldk-node", "ldk-server-client", - "ldk-server-protos", + "ldk-server-grpc", "prost", "serde_json", "tempfile", @@ -906,7 +906,7 @@ dependencies = [ "byteorder", "libc", "log", - "rustls 0.23.36", + "rustls 0.23.37", "serde", "serde_json", "webpki-roots 0.25.4", @@ -1012,9 +1012,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.3.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" [[package]] name = "filetime" @@ -1156,7 +1156,7 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" dependencies = [ - "fastrand 2.3.0", + "fastrand 2.4.1", "futures-core", "futures-io", "parking", @@ -1171,7 +1171,7 @@ checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.116", + "syn 2.0.117", ] [[package]] @@ -1235,20 +1235,20 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "r-efi", + "r-efi 5.3.0", "wasip2", "wasm-bindgen", ] [[package]] name = "getrandom" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" dependencies = [ "cfg-if", "libc", - "r-efi", + "r-efi 6.0.0", "wasip2", "wasip3", ] @@ -1468,9 +1468,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" dependencies = [ "atomic-waker", "bytes", @@ -1481,7 +1481,6 @@ dependencies = [ "httparse", "itoa", "pin-project-lite", - "pin-utils", "smallvec", "tokio", "want", @@ -1508,9 +1507,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ "http 1.4.0", - "hyper 1.8.1", + "hyper 1.9.0", "hyper-util", - "rustls 0.23.36", + "rustls 0.23.37", "rustls-pki-types", "tokio", "tokio-rustls 0.26.4", @@ -1530,12 +1529,12 @@ dependencies = [ "futures-util", "http 1.4.0", "http-body 1.0.1", - "hyper 1.8.1", + "hyper 1.9.0", "ipnet", "libc", "percent-encoding", "pin-project-lite", - "socket2 0.5.10", + "socket2 0.6.3", "tokio", "tower-service", "tracing", @@ -1567,12 +1566,13 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" dependencies = [ "displaydoc", "potential_utf", + "utf8_iter", "yoke", "zerofrom", "zerovec", @@ -1580,9 +1580,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" dependencies = [ "displaydoc", "litemap", @@ -1593,9 +1593,9 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" dependencies = [ "icu_collections", "icu_normalizer_data", @@ -1607,15 +1607,15 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" [[package]] name = "icu_properties" -version = "2.1.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" dependencies = [ "icu_collections", "icu_locale_core", @@ -1627,15 +1627,15 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.1.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" [[package]] name = "icu_provider" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" dependencies = [ "displaydoc", "icu_locale_core", @@ -1675,9 +1675,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.13.0" +version = "2.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +checksum = "45a8a2b9cb3e0b0c1803dbb0758ffac5de2f425b23c28f518faabd9d805342ff" dependencies = [ "equivalent", "hashbrown 0.16.1", @@ -1717,15 +1717,15 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.11.0" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" [[package]] name = "iri-string" -version = "0.7.10" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" +checksum = "25e659a4bb38e810ebc252e53b5814ff908a8c58c2a9ce2fae1bbec24cbf4e20" dependencies = [ "memchr", "serde", @@ -1742,16 +1742,18 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "js-sys" -version = "0.3.85" +version = "0.3.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" +checksum = "2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9" dependencies = [ + "cfg-if", + "futures-util", "once_cell", "wasm-bindgen", ] @@ -1831,7 +1833,7 @@ dependencies = [ "log", "prost", "rusqlite", - "rustls 0.23.36", + "rustls 0.23.37", "serde", "serde_json", "tokio", @@ -1844,19 +1846,22 @@ name = "ldk-server-client" version = "0.1.0" dependencies = [ "bitcoin_hashes", - "ldk-server-protos", + "ldk-server-grpc", "prost", "reqwest 0.11.27", ] [[package]] -name = "ldk-server-protos" +name = "ldk-server-grpc" version = "0.1.0" dependencies = [ "bytes", + "http 1.4.0", + "http-body 1.0.1", "prost", "prost-build", "serde", + "tokio", ] [[package]] @@ -1867,9 +1872,9 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "libc" -version = "0.2.182" +version = "0.2.184" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" +checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" [[package]] name = "libm" @@ -1879,13 +1884,14 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.12" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" +checksum = "7ddbf48fd451246b1f8c2610bd3b4ac0cc6e149d89832867093ab69a17194f08" dependencies = [ "bitflags 2.11.0", "libc", - "redox_syscall 0.7.1", + "plain", + "redox_syscall 0.7.3", ] [[package]] @@ -1975,7 +1981,7 @@ source = "git+https://github.com/lightningdevkit/rust-lightning?rev=dcf0c203e166 dependencies = [ "proc-macro2", "quote", - "syn 2.0.116", + "syn 2.0.117", ] [[package]] @@ -2045,15 +2051,15 @@ checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "linux-raw-sys" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" [[package]] name = "lock_api" @@ -2130,9 +2136,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" dependencies = [ "libc", "wasi", @@ -2175,9 +2181,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" +checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" [[package]] name = "num-integer" @@ -2208,9 +2214,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.21.3" +version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] name = "openssl-probe" @@ -2312,15 +2318,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" - -[[package]] -name = "pin-utils" -version = "0.1.0" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] name = "pinky-swear" @@ -2336,12 +2336,12 @@ dependencies = [ [[package]] name = "piper" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" dependencies = [ "atomic-waker", - "fastrand 2.3.0", + "fastrand 2.4.1", "futures-io", ] @@ -2381,6 +2381,12 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + [[package]] name = "polling" version = "2.8.0" @@ -2407,7 +2413,7 @@ dependencies = [ "concurrent-queue", "hermit-abi 0.5.2", "pin-project-lite", - "rustix 1.1.3", + "rustix 1.1.4", "windows-sys 0.61.2", ] @@ -2421,9 +2427,9 @@ dependencies = [ [[package]] name = "potential_utf" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" dependencies = [ "zerovec", ] @@ -2460,7 +2466,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.116", + "syn 2.0.117", ] [[package]] @@ -2538,8 +2544,8 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash", - "rustls 0.23.36", - "socket2 0.5.10", + "rustls 0.23.37", + "socket2 0.6.3", "thiserror", "tokio", "tracing", @@ -2558,7 +2564,7 @@ dependencies = [ "rand 0.9.2", "ring", "rustc-hash", - "rustls 0.23.36", + "rustls 0.23.37", "rustls-pki-types", "slab", "thiserror", @@ -2576,16 +2582,16 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.5.10", + "socket2 0.6.3", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "quote" -version = "1.0.44" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] @@ -2596,6 +2602,12 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + [[package]] name = "rand" version = "0.8.5" @@ -2686,9 +2698,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.7.1" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35985aa610addc02e24fc232012c86fd11f14111180f902b67e2d5331f8ebf2b" +checksum = "6ce70a74e890531977d37e532c34d45e9055d2409ed08ddba14529471ed0be16" dependencies = [ "bitflags 2.11.0", ] @@ -2718,9 +2730,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.9" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "reqwest" @@ -2775,7 +2787,7 @@ dependencies = [ "http 1.4.0", "http-body 1.0.1", "http-body-util", - "hyper 1.8.1", + "hyper 1.9.0", "hyper-rustls 0.27.7", "hyper-util", "js-sys", @@ -2783,7 +2795,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.36", + "rustls 0.23.37", "rustls-pki-types", "serde", "serde_json", @@ -2831,9 +2843,9 @@ dependencies = [ [[package]] name = "rustc-hash" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" [[package]] name = "rusticata-macros" @@ -2873,14 +2885,14 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ "bitflags 2.11.0", "errno", "libc", - "linux-raw-sys 0.11.0", + "linux-raw-sys 0.12.1", "windows-sys 0.61.2", ] @@ -2898,15 +2910,15 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.36" +version = "0.23.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" +checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" dependencies = [ "log", "once_cell", "ring", "rustls-pki-types", - "rustls-webpki 0.103.9", + "rustls-webpki 0.103.10", "subtle", "zeroize", ] @@ -2918,10 +2930,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70cc376c6ba1823ae229bacf8ad93c136d93524eab0e4e5e0e4f96b9c4e5b212" dependencies = [ "log", - "rustls 0.23.36", + "rustls 0.23.37", "rustls-native-certs", "rustls-pki-types", - "rustls-webpki 0.103.9", + "rustls-webpki 0.103.10", ] [[package]] @@ -2977,9 +2989,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.9" +version = "0.103.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" +checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef" dependencies = [ "ring", "rustls-pki-types", @@ -3009,9 +3021,9 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.28" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" dependencies = [ "windows-sys 0.61.2", ] @@ -3079,9 +3091,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.16.0" +version = "2.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "321c8673b092a9a42605034a9879d73cb79101ed5fd117bc9a597b89b4e9e61a" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" dependencies = [ "core-foundation-sys", "libc", @@ -3089,9 +3101,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] name = "serde" @@ -3120,7 +3132,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.116", + "syn 2.0.117", ] [[package]] @@ -3178,9 +3190,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "simd-adler32" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" [[package]] name = "slab" @@ -3216,12 +3228,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -3268,9 +3280,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.116" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3df424c70518695237746f84cede799c9c58fcb37450d7b23716568cc8bc69cb" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -3300,7 +3312,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.116", + "syn 2.0.117", ] [[package]] @@ -3326,9 +3338,9 @@ dependencies = [ [[package]] name = "tar" -version = "0.4.44" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" +checksum = "22692a6476a21fa75fdfc11d452fda482af402c008cdbaf3476414e122040973" dependencies = [ "filetime", "libc", @@ -3349,14 +3361,14 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.25.0" +version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0136791f7c95b1f6dd99f9cc786b91bb81c3800b639b3478e561ddb7be95e5f1" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ - "fastrand 2.3.0", - "getrandom 0.4.1", + "fastrand 2.4.1", + "getrandom 0.4.2", "once_cell", - "rustix 1.1.3", + "rustix 1.1.4", "windows-sys 0.61.2", ] @@ -3377,7 +3389,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.116", + "syn 2.0.117", ] [[package]] @@ -3413,9 +3425,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" dependencies = [ "displaydoc", "zerovec", @@ -3423,9 +3435,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" dependencies = [ "tinyvec_macros", ] @@ -3438,28 +3450,28 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.49.0" +version = "1.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" +checksum = "2bd1c4c0fc4a7ab90fc15ef6daaa3ec3b893f004f915f2392557ed23237820cd" dependencies = [ "bytes", "libc", "mio", "pin-project-lite", - "socket2 0.6.2", + "socket2 0.6.3", "tokio-macros", "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", - "syn 2.0.116", + "syn 2.0.117", ] [[package]] @@ -3478,7 +3490,7 @@ version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ - "rustls 0.23.36", + "rustls 0.23.37", "tokio", ] @@ -3690,9 +3702,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.108" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" +checksum = "0551fc1bb415591e3372d0bc4780db7e587d84e2a7e79da121051c5c4b89d0b0" dependencies = [ "cfg-if", "once_cell", @@ -3703,23 +3715,19 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.58" +version = "0.4.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" +checksum = "03623de6905b7206edd0a75f69f747f134b7f0a2323392d664448bf2d3c5d87e" dependencies = [ - "cfg-if", - "futures-util", "js-sys", - "once_cell", "wasm-bindgen", - "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.108" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" +checksum = "7fbdf9a35adf44786aecd5ff89b4563a90325f9da0923236f6104e603c7e86be" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3727,22 +3735,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.108" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" +checksum = "dca9693ef2bab6d4e6707234500350d8dad079eb508dca05530c85dc3a529ff2" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.116", + "syn 2.0.117", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.108" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +checksum = "39129a682a6d2d841b6c429d0c51e5cb0ed1a03829d8b3d1e69a011e62cb3d3b" dependencies = [ "unicode-ident", ] @@ -3783,9 +3791,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.85" +version = "0.3.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" +checksum = "cd70027e39b12f0849461e08ffc50b9cd7688d942c1c8e3c7b22273236b4dd0a" dependencies = [ "js-sys", "wasm-bindgen", @@ -3880,7 +3888,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.116", + "syn 2.0.117", ] [[package]] @@ -3891,7 +3899,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.116", + "syn 2.0.117", ] [[package]] @@ -4189,7 +4197,7 @@ dependencies = [ "heck 0.5.0", "indexmap", "prettyplease 0.2.37", - "syn 2.0.116", + "syn 2.0.117", "wasm-metadata", "wit-bindgen-core", "wit-component", @@ -4205,7 +4213,7 @@ dependencies = [ "prettyplease 0.2.37", "proc-macro2", "quote", - "syn 2.0.116", + "syn 2.0.117", "wit-bindgen-core", "wit-bindgen-rust", ] @@ -4249,9 +4257,9 @@ dependencies = [ [[package]] name = "writeable" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" [[package]] name = "x509-cert" @@ -4288,14 +4296,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" dependencies = [ "libc", - "rustix 1.1.3", + "rustix 1.1.4", ] [[package]] name = "yoke" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" dependencies = [ "stable_deref_trait", "yoke-derive", @@ -4304,54 +4312,54 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.116", + "syn 2.0.117", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.39" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.39" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.116", + "syn 2.0.117", ] [[package]] name = "zerofrom" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.116", + "syn 2.0.117", "synstructure", ] @@ -4363,9 +4371,9 @@ checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" [[package]] name = "zerotrie" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" dependencies = [ "displaydoc", "yoke", @@ -4374,9 +4382,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" dependencies = [ "yoke", "zerofrom", @@ -4385,13 +4393,13 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", - "syn 2.0.116", + "syn 2.0.117", ] [[package]] diff --git a/e2e-tests/Cargo.toml b/e2e-tests/Cargo.toml index 56072c6c..6b070470 100644 --- a/e2e-tests/Cargo.toml +++ b/e2e-tests/Cargo.toml @@ -8,7 +8,7 @@ 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-grpc = { path = "../ldk-server-grpc", features = ["serde"] } serde_json = "1.0" hex-conservative = { version = "0.2", features = ["std"] } lapin = { version = "2.4.0", features = ["rustls"], default-features = false } diff --git a/e2e-tests/build.rs b/e2e-tests/build.rs index 6701b3d0..ac995049 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-grpc/src"); + println!("cargo:rerun-if-changed=../ldk-server-grpc/Cargo.toml"); } diff --git a/e2e-tests/src/lib.rs b/e2e-tests/src/lib.rs index 8d0553a6..eef1d2bc 100644 --- a/e2e-tests/src/lib.rs +++ b/e2e-tests/src/lib.rs @@ -16,8 +16,8 @@ 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::{ +use ldk_server_client::ldk_server_grpc::api::{GetNodeInfoRequest, GetNodeInfoResponse}; +use ldk_server_grpc::api::{ GetBalancesRequest, ListChannelsRequest, OnchainReceiveRequest, OpenChannelRequest, }; diff --git a/e2e-tests/tests/e2e.rs b/e2e-tests/tests/e2e.rs index 807a977e..adbe6c07 100644 --- a/e2e-tests/tests/e2e.rs +++ b/e2e-tests/tests/e2e.rs @@ -20,13 +20,13 @@ use ldk_node::bitcoin::hashes::{sha256, Hash}; 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_client::ldk_server_grpc::api::{ Bolt11ReceiveRequest, Bolt12ReceiveRequest, OnchainReceiveRequest, }; -use ldk_server_client::ldk_server_protos::types::{ +use ldk_server_client::ldk_server_grpc::types::{ bolt11_invoice_description, Bolt11InvoiceDescription, }; -use ldk_server_protos::events::event_envelope::Event; +use ldk_server_grpc::events::event_envelope::Event; #[tokio::test] async fn test_cli_get_node_info() { diff --git a/ldk-server-client/Cargo.toml b/ldk-server-client/Cargo.toml index 13916fa3..ed2a61d5 100644 --- a/ldk-server-client/Cargo.toml +++ b/ldk-server-client/Cargo.toml @@ -5,10 +5,10 @@ edition = "2021" [features] default = [] -serde = ["ldk-server-protos/serde"] +serde = ["ldk-server-grpc/serde"] [dependencies] -ldk-server-protos = { path = "../ldk-server-protos" } +ldk-server-grpc = { path = "../ldk-server-grpc" } reqwest = { version = "0.11.13", default-features = false, features = ["rustls-tls"] } prost = { version = "0.11.6", default-features = false, features = ["std", "prost-derive"] } bitcoin_hashes = "0.14" diff --git a/ldk-server-protos/Cargo.toml b/ldk-server-grpc/Cargo.toml similarity index 54% rename from ldk-server-protos/Cargo.toml rename to ldk-server-grpc/Cargo.toml index c971d6f5..d735b398 100644 --- a/ldk-server-protos/Cargo.toml +++ b/ldk-server-grpc/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "ldk-server-protos" +name = "ldk-server-grpc" version = "0.1.0" edition = "2021" @@ -14,12 +14,23 @@ unexpected_cfgs = { level = "warn", check-cfg = ['cfg(genproto)'] } [features] default = [] -serde = ["dep:serde", "dep:bytes"] +serde = ["dep:serde", "bytes/serde"] [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 } +bytes = { version = "1", default-features = false } +http = { version = "1", default-features = false } +http-body = { version = "1", default-features = false } +tokio = { version = "1", default-features = false, features = ["sync"] } + +[dev-dependencies] +tonic = { version = "0.9", default-features = false, features = ["transport", "codegen", "prost"] } +tokio = { version = "1", features = ["macros", "rt-multi-thread", "net"] } +tokio-stream = "0.1" +hyper = { version = "1", features = ["server", "http2"] } +hyper-util = { version = "0.1", features = ["tokio"] } +http-body-util = "0.1" [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-grpc/build.rs similarity index 100% rename from ldk-server-protos/build.rs rename to ldk-server-grpc/build.rs diff --git a/ldk-server-protos/src/api.rs b/ldk-server-grpc/src/api.rs similarity index 88% rename from ldk-server-protos/src/api.rs rename to ldk-server-grpc/src/api.rs index d8d8034c..e8ed552d 100644 --- a/ldk-server-protos/src/api.rs +++ b/ldk-server-grpc/src/api.rs @@ -16,8 +16,7 @@ #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] 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`. +/// The response for the `GetNodeInfo` RPC. On failure, a gRPC error status is returned. #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[allow(clippy::derive_partial_eq_without_eq)] @@ -88,8 +87,7 @@ pub struct GetNodeInfoResponse { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] 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`.. +/// The response for the `OnchainReceive` RPC. On failure, a gRPC error status is returned. #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[allow(clippy::derive_partial_eq_without_eq)] @@ -128,8 +126,7 @@ pub struct OnchainSendRequest { #[prost(uint64, optional, tag = "4")] pub fee_rate_sat_per_vb: ::core::option::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`. +/// The response for the `OnchainSend` RPC. On failure, a gRPC error status is returned. #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[allow(clippy::derive_partial_eq_without_eq)] @@ -161,8 +158,7 @@ pub struct Bolt11ReceiveRequest { #[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`. +/// The response for the `Bolt11Receive` RPC. On failure, a gRPC error status is returned. #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[allow(clippy::derive_partial_eq_without_eq)] @@ -206,8 +202,7 @@ pub struct Bolt11ReceiveForHashRequest { #[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`. +/// The response for the `Bolt11ReceiveForHash` RPC. On failure, a gRPC error status is returned. #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[allow(clippy::derive_partial_eq_without_eq)] @@ -239,8 +234,7 @@ pub struct Bolt11ClaimForHashRequest { #[prost(string, tag = "3")] pub preimage: ::prost::alloc::string::String, } -/// 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`. +/// The response for the `Bolt11ClaimForHash` RPC. On failure, a gRPC error status is returned. #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[allow(clippy::derive_partial_eq_without_eq)] @@ -258,8 +252,7 @@ pub struct Bolt11FailForHashRequest { #[prost(string, tag = "1")] pub payment_hash: ::prost::alloc::string::String, } -/// 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`. +/// The response for the `Bolt11FailForHash` RPC. On failure, a gRPC error status is returned. #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[allow(clippy::derive_partial_eq_without_eq)] @@ -287,8 +280,7 @@ pub struct Bolt11ReceiveViaJitChannelRequest { #[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`. +/// The response for the `Bolt11ReceiveViaJitChannel` RPC. On failure, a gRPC error status is returned. #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[allow(clippy::derive_partial_eq_without_eq)] @@ -318,8 +310,7 @@ pub struct Bolt11ReceiveVariableAmountViaJitChannelRequest { #[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`. +/// The response for the `Bolt11ReceiveVariableAmountViaJitChannel` RPC. On failure, a gRPC error status is returned. #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[allow(clippy::derive_partial_eq_without_eq)] @@ -348,8 +339,7 @@ pub struct Bolt11SendRequest { #[prost(message, optional, tag = "3")] pub route_parameters: ::core::option::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`. +/// The response for the `Bolt11Send` RPC. On failure, a gRPC error status is returned. #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[allow(clippy::derive_partial_eq_without_eq)] @@ -383,8 +373,7 @@ pub struct Bolt12ReceiveRequest { #[prost(uint64, optional, tag = "4")] pub quantity: ::core::option::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`. +/// The response for the `Bolt12Receive` RPC. On failure, a gRPC error status is returned. #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[allow(clippy::derive_partial_eq_without_eq)] @@ -426,8 +415,7 @@ pub struct Bolt12SendRequest { #[prost(message, optional, tag = "5")] pub route_parameters: ::core::option::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`. +/// The response for the `Bolt12Send` RPC. On failure, a gRPC error status is returned. #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[allow(clippy::derive_partial_eq_without_eq)] @@ -454,8 +442,7 @@ pub struct SpontaneousSendRequest { #[prost(message, optional, tag = "3")] pub route_parameters: ::core::option::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`. +/// The response for the `SpontaneousSend` RPC. On failure, a gRPC error status is returned. #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[allow(clippy::derive_partial_eq_without_eq)] @@ -492,8 +479,7 @@ pub struct OpenChannelRequest { #[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`. +/// The response for the `OpenChannel` RPC. On failure, a gRPC error status is returned. #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[allow(clippy::derive_partial_eq_without_eq)] @@ -520,8 +506,7 @@ pub struct SpliceInRequest { #[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`. +/// The response for the `SpliceIn` RPC. On failure, a gRPC error status is returned. #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[allow(clippy::derive_partial_eq_without_eq)] @@ -549,8 +534,7 @@ pub struct SpliceOutRequest { #[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`. +/// The response for the `SpliceOut` RPC. On failure, a gRPC error status is returned. #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[allow(clippy::derive_partial_eq_without_eq)] @@ -577,8 +561,7 @@ pub struct UpdateChannelConfigRequest { #[prost(message, optional, tag = "3")] pub channel_config: ::core::option::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`. +/// The response for the `UpdateChannelConfig` RPC. On failure, a gRPC error status is returned. #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[allow(clippy::derive_partial_eq_without_eq)] @@ -598,8 +581,7 @@ pub struct CloseChannelRequest { #[prost(string, tag = "2")] pub counterparty_node_id: ::prost::alloc::string::String, } -/// 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`. +/// The response for the `CloseChannel` RPC. On failure, a gRPC error status is returned. #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[allow(clippy::derive_partial_eq_without_eq)] @@ -622,8 +604,7 @@ pub struct ForceCloseChannelRequest { #[prost(string, optional, tag = "3")] pub force_close_reason: ::core::option::Option<::prost::alloc::string::String>, } -/// 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`. +/// The response for the `ForceCloseChannel` RPC. On failure, a gRPC error status is returned. #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[allow(clippy::derive_partial_eq_without_eq)] @@ -636,8 +617,7 @@ pub struct ForceCloseChannelResponse {} #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] 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`. +/// The response for the `ListChannels` RPC. On failure, a gRPC error status is returned. #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[allow(clippy::derive_partial_eq_without_eq)] @@ -658,8 +638,7 @@ pub struct GetPaymentDetailsRequest { #[prost(string, tag = "1")] pub payment_id: ::prost::alloc::string::String, } -/// 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`. +/// The response for the `GetPaymentDetails` RPC. On failure, a gRPC error status is returned. #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[allow(clippy::derive_partial_eq_without_eq)] @@ -686,8 +665,7 @@ pub struct ListPaymentsRequest { #[prost(message, optional, tag = "1")] pub page_token: ::core::option::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`. +/// The response for the `ListPayments` RPC. On failure, a gRPC error status is returned. #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[allow(clippy::derive_partial_eq_without_eq)] @@ -728,8 +706,7 @@ pub struct ListForwardedPaymentsRequest { #[prost(message, optional, tag = "1")] pub page_token: ::core::option::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`. +/// The response for the `ListForwardedPayments` RPC. On failure, a gRPC error status is returned. #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[allow(clippy::derive_partial_eq_without_eq)] @@ -765,8 +742,7 @@ pub struct SignMessageRequest { #[prost(bytes = "bytes", tag = "1")] pub message: ::prost::bytes::Bytes, } -/// 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`. +/// The response for the `SignMessage` RPC. On failure, a gRPC error status is returned. #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[allow(clippy::derive_partial_eq_without_eq)] @@ -793,8 +769,7 @@ pub struct VerifySignatureRequest { #[prost(string, tag = "3")] pub public_key: ::prost::alloc::string::String, } -/// 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`. +/// The response for the `VerifySignature` RPC. On failure, a gRPC error status is returned. #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[allow(clippy::derive_partial_eq_without_eq)] @@ -811,8 +786,7 @@ pub struct VerifySignatureResponse { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] 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`. +/// The response for the `ExportPathfindingScores` RPC. On failure, a gRPC error status is returned. #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[allow(clippy::derive_partial_eq_without_eq)] @@ -829,8 +803,7 @@ pub struct ExportPathfindingScoresResponse { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] 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`. +/// The response for the `GetBalances` RPC. On failure, a gRPC error status is returned. #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[allow(clippy::derive_partial_eq_without_eq)] @@ -894,8 +867,7 @@ pub struct ConnectPeerRequest { #[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`. +/// The response for the `ConnectPeer` RPC. On failure, a gRPC error status is returned. #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[allow(clippy::derive_partial_eq_without_eq)] @@ -912,8 +884,7 @@ pub struct DisconnectPeerRequest { #[prost(string, tag = "1")] pub node_pubkey: ::prost::alloc::string::String, } -/// 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`. +/// The response for the `DisconnectPeer` RPC. On failure, a gRPC error status is returned. #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[allow(clippy::derive_partial_eq_without_eq)] @@ -926,8 +897,7 @@ pub struct DisconnectPeerResponse {} #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] 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`. +/// The response for the `ListPeers` RPC. On failure, a gRPC error status is returned. #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[allow(clippy::derive_partial_eq_without_eq)] @@ -944,8 +914,7 @@ pub struct ListPeersResponse { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] 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`. +/// The response for the `GraphListChannels` RPC. On failure, a gRPC error status is returned. #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[allow(clippy::derive_partial_eq_without_eq)] @@ -966,8 +935,7 @@ pub struct GraphGetChannelRequest { #[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`. +/// The response for the `GraphGetChannel` RPC. On failure, a gRPC error status is returned. #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[allow(clippy::derive_partial_eq_without_eq)] @@ -984,8 +952,7 @@ pub struct GraphGetChannelResponse { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] 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`. +/// The response for the `GraphListNodes` RPC. On failure, a gRPC error status is returned. #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[allow(clippy::derive_partial_eq_without_eq)] @@ -1016,8 +983,7 @@ pub struct UnifiedSendRequest { #[prost(message, optional, tag = "3")] pub route_parameters: ::core::option::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`. +/// The response for the `UnifiedSend` RPC. On failure, a gRPC error status is returned. #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[allow(clippy::derive_partial_eq_without_eq)] @@ -1056,8 +1022,7 @@ pub struct GraphGetNodeRequest { #[prost(string, tag = "1")] pub node_id: ::prost::alloc::string::String, } -/// 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`. +/// The response for the `GraphGetNode` RPC. On failure, a gRPC error status is returned. #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[allow(clippy::derive_partial_eq_without_eq)] @@ -1078,8 +1043,7 @@ pub struct DecodeInvoiceRequest { #[prost(string, tag = "1")] pub invoice: ::prost::alloc::string::String, } -/// The response `content` for the `DecodeInvoice` API, when HttpStatusCode is OK (200). -/// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. +/// The response for the `DecodeInvoice` RPC. On failure, a gRPC error status is returned. #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[allow(clippy::derive_partial_eq_without_eq)] @@ -1142,8 +1106,7 @@ pub struct DecodeOfferRequest { #[prost(string, tag = "1")] pub offer: ::prost::alloc::string::String, } -/// The response `content` for the `DecodeOffer` API, when HttpStatusCode is OK (200). -/// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. +/// The response for the `DecodeOffer` RPC. On failure, a gRPC error status is returned. #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[allow(clippy::derive_partial_eq_without_eq)] @@ -1186,3 +1149,9 @@ pub struct DecodeOfferResponse { #[prost(bool, tag = "12")] pub is_expired: bool, } +/// Subscribe to a stream of server events. +#[cfg_attr(feature = "serde", derive(serde::Serialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SubscribeEventsRequest {} diff --git a/ldk-server-protos/src/endpoints.rs b/ldk-server-grpc/src/endpoints.rs similarity index 93% rename from ldk-server-protos/src/endpoints.rs rename to ldk-server-grpc/src/endpoints.rs index 83cc196f..9b20263f 100644 --- a/ldk-server-protos/src/endpoints.rs +++ b/ldk-server-grpc/src/endpoints.rs @@ -7,6 +7,9 @@ // You may not use this file except in accordance with one or both of these // licenses. +/// gRPC path prefix for the LightningNode service. +pub const GRPC_SERVICE_PREFIX: &str = "/api.LightningNode/"; + pub const GET_NODE_INFO_PATH: &str = "GetNodeInfo"; pub const GET_BALANCES_PATH: &str = "GetBalances"; pub const ONCHAIN_RECEIVE_PATH: &str = "OnchainReceive"; @@ -46,3 +49,4 @@ pub const GRAPH_GET_NODE_PATH: &str = "GraphGetNode"; pub const DECODE_INVOICE_PATH: &str = "DecodeInvoice"; pub const DECODE_OFFER_PATH: &str = "DecodeOffer"; pub const GET_METRICS_PATH: &str = "metrics"; +pub const SUBSCRIBE_EVENTS_PATH: &str = "SubscribeEvents"; diff --git a/ldk-server-protos/src/error.rs b/ldk-server-grpc/src/error.rs similarity index 100% rename from ldk-server-protos/src/error.rs rename to ldk-server-grpc/src/error.rs diff --git a/ldk-server-protos/src/events.rs b/ldk-server-grpc/src/events.rs similarity index 100% rename from ldk-server-protos/src/events.rs rename to ldk-server-grpc/src/events.rs diff --git a/ldk-server-grpc/src/grpc.rs b/ldk-server-grpc/src/grpc.rs new file mode 100644 index 00000000..c3d8c715 --- /dev/null +++ b/ldk-server-grpc/src/grpc.rs @@ -0,0 +1,796 @@ +// 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. + +//! gRPC wire protocol primitives implemented directly on HTTP/2, +//! without depending on tonic or any gRPC framework. +//! +//! Reference: + +use bytes::{BufMut, Bytes, BytesMut}; + +// gRPC status codes (a subset — only those we use). +pub const GRPC_STATUS_INVALID_ARGUMENT: u32 = 3; +pub const GRPC_STATUS_DEADLINE_EXCEEDED: u32 = 4; +pub const GRPC_STATUS_FAILED_PRECONDITION: u32 = 9; +pub const GRPC_STATUS_UNIMPLEMENTED: u32 = 12; +pub const GRPC_STATUS_INTERNAL: u32 = 13; +pub const GRPC_STATUS_UNAVAILABLE: u32 = 14; +pub const GRPC_STATUS_UNAUTHENTICATED: u32 = 16; + +/// A gRPC status with code and human-readable message. +#[derive(Debug)] +pub struct GrpcStatus { + pub code: u32, + pub message: String, +} + +impl GrpcStatus { + pub fn new(code: u32, message: impl Into) -> Self { + Self { code, message: message.into() } + } +} + +/// Decode a gRPC-framed request body, returning the inner protobuf bytes. +/// +/// gRPC framing: 1 byte compressed flag + 4 bytes big-endian length + payload. +pub fn decode_grpc_body(bytes: &[u8]) -> Result<&[u8], GrpcStatus> { + if bytes.len() < 5 { + return Err(GrpcStatus::new( + GRPC_STATUS_INVALID_ARGUMENT, + "Request body too short for gRPC frame", + )); + } + + // gRPC Compressed-Flag: 0 = uncompressed, 1 = compressed per grpc-encoding header. + // We don't support compression because our RPCs exchange small protobuf messages where + // compression overhead would outweigh savings. Returning UNIMPLEMENTED causes compliant + // clients to retry without compression. + let compressed = bytes[0]; + if compressed != 0 { + return Err(GrpcStatus::new( + GRPC_STATUS_UNIMPLEMENTED, + "gRPC compression is not supported", + )); + } + + let len = u32::from_be_bytes([bytes[1], bytes[2], bytes[3], bytes[4]]) as usize; + if bytes.len() < 5 + len { + return Err(GrpcStatus::new( + GRPC_STATUS_INVALID_ARGUMENT, + "gRPC frame length exceeds body size", + )); + } + + if bytes.len() > 5 + len { + return Err(GrpcStatus::new( + GRPC_STATUS_INVALID_ARGUMENT, + "Trailing data after gRPC frame", + )); + } + + Ok(&bytes[5..5 + len]) +} + +/// Encode a protobuf message into a gRPC-framed `Bytes`. +/// +/// gRPC framing: 1 byte compressed flag (0) + 4 bytes big-endian length + payload. +pub fn encode_grpc_frame(proto_bytes: &[u8]) -> Bytes { + debug_assert!( + proto_bytes.len() <= u32::MAX as usize, + "gRPC message exceeds maximum frame size (4 GB)" + ); + let mut buf = BytesMut::with_capacity(5 + proto_bytes.len()); + buf.put_u8(0); // no compression + buf.put_u32(proto_bytes.len() as u32); + buf.put_slice(proto_bytes); + buf.freeze() +} + +/// A response body type for gRPC over HTTP/2. +/// +/// Implements `http_body::Body` to deliver gRPC-framed data followed by trailers. +pub enum GrpcBody { + /// A single gRPC-framed message followed by OK trailers. + Unary { data: Option, trailers_sent: bool }, + /// Empty body for Trailers-Only responses (error status is in the HTTP response headers). + Empty, + /// Multiple gRPC-framed messages streamed from a channel, followed by trailers. + /// Send `Err(GrpcStatus)` to terminate the stream with an error status. + Stream { rx: tokio::sync::mpsc::Receiver>, done: bool }, + /// Plain (non-gRPC) response body with no trailers, used for non-RPC endpoints like metrics. + Plain { data: Option }, +} + +impl http_body::Body for GrpcBody { + type Data = Bytes; + type Error = std::convert::Infallible; + + fn poll_frame( + self: std::pin::Pin<&mut Self>, _cx: &mut std::task::Context<'_>, + ) -> std::task::Poll, Self::Error>>> { + use std::task::Poll; + + let this = self.get_mut(); + match this { + GrpcBody::Unary { data, trailers_sent } => { + if let Some(bytes) = data.take() { + Poll::Ready(Some(Ok(http_body::Frame::data(bytes)))) + } else if !*trailers_sent { + *trailers_sent = true; + Poll::Ready(Some(Ok(http_body::Frame::trailers(ok_trailers())))) + } else { + Poll::Ready(None) + } + }, + GrpcBody::Empty => Poll::Ready(None), + GrpcBody::Stream { rx, done } => { + if *done { + return Poll::Ready(None); + } + match rx.poll_recv(_cx) { + Poll::Ready(Some(Ok(bytes))) => { + Poll::Ready(Some(Ok(http_body::Frame::data(bytes)))) + }, + Poll::Ready(Some(Err(status))) => { + *done = true; + Poll::Ready(Some(Ok(http_body::Frame::trailers(error_trailers(&status))))) + }, + Poll::Ready(None) => { + // Channel closed normally — send OK trailers + *done = true; + Poll::Ready(Some(Ok(http_body::Frame::trailers(ok_trailers())))) + }, + Poll::Pending => Poll::Pending, + } + }, + GrpcBody::Plain { data } => match data.take() { + Some(bytes) => Poll::Ready(Some(Ok(http_body::Frame::data(bytes)))), + None => Poll::Ready(None), + }, + } + } +} + +/// Build trailers for a successful gRPC response. +fn ok_trailers() -> http::HeaderMap { + let mut trailers = http::HeaderMap::with_capacity(1); + trailers.insert("grpc-status", http::HeaderValue::from_static("0")); + trailers +} + +/// Build trailers for a gRPC error response. +fn error_trailers(status: &GrpcStatus) -> http::HeaderMap { + let mut trailers = http::HeaderMap::with_capacity(2); + trailers.insert("grpc-status", http::HeaderValue::from_str(&status.code.to_string()).unwrap()); + if !status.message.is_empty() { + // Percent-encode the message per gRPC spec. + let encoded = percent_encode(&status.message); + if let Ok(val) = http::HeaderValue::from_str(&encoded) { + trailers.insert("grpc-message", val); + } + } + trailers +} + +/// Build a Trailers-Only gRPC error response. +/// +/// Per the gRPC spec, error responses with no body encode `grpc-status` and `grpc-message` +/// in the HTTP response headers so the entire response is a single HEADERS frame with +/// END_STREAM. This is required for compatibility with strict client implementations +/// (grpc-go, grpc-java). +pub fn grpc_error_response(status: GrpcStatus) -> http::Response { + let mut builder = http::Response::builder() + .status(200) + .header("content-type", "application/grpc+proto") + .header("grpc-accept-encoding", "identity") + .header("grpc-status", status.code.to_string()); + if !status.message.is_empty() { + let encoded = percent_encode(&status.message); + if let Ok(val) = http::HeaderValue::from_str(&encoded) { + builder = builder.header("grpc-message", val); + } + } + builder.body(GrpcBody::Empty).unwrap() +} + +/// Build an HTTP 200 response with gRPC content-type and the given body. +pub fn grpc_response(body: GrpcBody) -> http::Response { + http::Response::builder() + .status(200) + .header("content-type", "application/grpc+proto") + .header("grpc-accept-encoding", "identity") + .body(body) + .unwrap() +} + +/// Validate that the request looks like a gRPC call. +pub fn validate_grpc_request(req: &http::Request) -> Result<(), GrpcStatus> { + if req.method() != http::Method::POST { + return Err(GrpcStatus::new(GRPC_STATUS_UNIMPLEMENTED, "gRPC requires POST method")); + } + + let content_type = + req.headers().get("content-type").and_then(|v| v.to_str().ok()).unwrap_or(""); + + if content_type != "application/grpc" && content_type != "application/grpc+proto" { + return Err(GrpcStatus::new(GRPC_STATUS_INVALID_ARGUMENT, "Invalid content-type for gRPC")); + } + + Ok(()) +} + +/// Minimal percent-encoding for grpc-message (RFC 3986 unreserved chars pass through). +pub fn percent_encode(s: &str) -> String { + let mut out = String::with_capacity(s.len()); + for b in s.bytes() { + match b { + b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'.' | b'~' | b' ' => { + out.push(b as char) + }, + _ => { + out.push('%'); + out.push(char::from(b"0123456789ABCDEF"[(b >> 4) as usize])); + out.push(char::from(b"0123456789ABCDEF"[(b & 0xf) as usize])); + }, + } + } + out +} + +/// Minimal percent-decoding for grpc-message values. +pub fn percent_decode(s: &str) -> String { + let mut out = String::with_capacity(s.len()); + let mut chars = s.bytes(); + while let Some(b) = chars.next() { + if b == b'%' { + let hi = chars.next().and_then(hex_val); + let lo = chars.next().and_then(hex_val); + if let (Some(h), Some(l)) = (hi, lo) { + out.push(((h << 4) | l) as char); + } + } else { + out.push(b as char); + } + } + out +} + +fn hex_val(b: u8) -> Option { + match b { + b'0'..=b'9' => Some(b - b'0'), + b'A'..=b'F' => Some(b - b'A' + 10), + b'a'..=b'f' => Some(b - b'a' + 10), + _ => None, + } +} + +/// Parse the `grpc-timeout` header value into a `Duration`. +/// +/// Format: `` where unit is one of: +/// `H` (hours), `M` (minutes), `S` (seconds), `m` (milliseconds), +/// `u` (microseconds), `n` (nanoseconds). +pub fn parse_grpc_timeout(value: &str) -> Result { + if !(2..=9).contains(&value.len()) { + return Err(GrpcStatus::new(GRPC_STATUS_INVALID_ARGUMENT, "Invalid grpc-timeout header")); + } + + let (num_str, unit) = value.split_at(value.len() - 1); + let num: u64 = num_str.parse().map_err(|_| { + GrpcStatus::new(GRPC_STATUS_INVALID_ARGUMENT, "Invalid grpc-timeout header") + })?; + + let duration = match unit { + "H" => num.checked_mul(3600).map(std::time::Duration::from_secs), + "M" => num.checked_mul(60).map(std::time::Duration::from_secs), + "S" => Some(std::time::Duration::from_secs(num)), + "m" => Some(std::time::Duration::from_millis(num)), + "u" => Some(std::time::Duration::from_micros(num)), + "n" => Some(std::time::Duration::from_nanos(num)), + _ => None, + }; + + duration + .ok_or_else(|| GrpcStatus::new(GRPC_STATUS_INVALID_ARGUMENT, "Invalid grpc-timeout header")) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_encode_decode_roundtrip() { + let payload = b"hello world"; + let encoded = encode_grpc_frame(payload); + let decoded = decode_grpc_body(&encoded).unwrap(); + assert_eq!(decoded, payload); + } + + #[test] + fn test_encode_empty() { + let encoded = encode_grpc_frame(b""); + assert_eq!(encoded.len(), 5); + assert_eq!(&encoded[..5], &[0, 0, 0, 0, 0]); + let decoded = decode_grpc_body(&encoded).unwrap(); + assert!(decoded.is_empty()); + } + + #[test] + fn test_decode_too_short() { + assert!(decode_grpc_body(&[0, 0, 0]).is_err()); + } + + #[test] + fn test_decode_compressed_rejected() { + let data = vec![1u8, 0, 0, 0, 1, 42]; // compressed flag = 1 + let result = decode_grpc_body(&data); + assert!(result.is_err()); + assert_eq!(result.unwrap_err().code, GRPC_STATUS_UNIMPLEMENTED); + } + + #[test] + fn test_decode_length_exceeds_body() { + let data = vec![0u8, 0, 0, 0, 10, 1, 2]; // claims 10 bytes, only 2 present + let result = decode_grpc_body(&data); + assert!(result.is_err()); + assert_eq!(result.unwrap_err().code, GRPC_STATUS_INVALID_ARGUMENT); + } + + #[test] + fn test_decode_trailing_data_rejected() { + let data = vec![0u8, 0, 0, 0, 1, 42, 99]; // 1-byte payload + trailing byte + let result = decode_grpc_body(&data); + assert!(result.is_err()); + assert_eq!(result.unwrap_err().code, GRPC_STATUS_INVALID_ARGUMENT); + } + + #[test] + fn test_decode_rejects_all_short_inputs() { + for len in 0..5 { + let data = vec![0u8; len]; + assert!(decode_grpc_body(&data).is_err(), "should reject {len}-byte input"); + } + } + + #[test] + fn test_percent_encode() { + assert_eq!(percent_encode("hello"), "hello"); + assert_eq!(percent_encode("a/b"), "a%2Fb"); + assert_eq!(percent_encode("100%"), "100%25"); + } + + #[test] + fn test_percent_encode_ascii_range() { + // Verify every ASCII byte is either passed through or percent-encoded + for b in 0u8..=127 { + let s = String::from(b as char); + let encoded = percent_encode(&s); + let is_unreserved = b.is_ascii_alphanumeric() + || b == b'-' || b == b'_' + || b == b'.' || b == b'~' + || b == b' '; + if is_unreserved { + assert_eq!(encoded, s, "byte {b:#04x} ({}) should pass through", b as char); + } else { + assert_eq!(encoded, format!("%{b:02X}"), "byte {b:#04x} should be percent-encoded"); + } + } + } + + #[test] + fn test_percent_encode_multibyte_utf8() { + // Each byte of multi-byte UTF-8 chars should be individually encoded + let encoded = percent_encode("café"); + assert_eq!(encoded, "caf%C3%A9"); + } + + #[test] + fn test_percent_decode_plain_text() { + assert_eq!(percent_decode("hello"), "hello"); + assert_eq!(percent_decode(""), ""); + } + + #[test] + fn test_percent_decode_encoded_chars() { + assert_eq!(percent_decode("hello%20world"), "hello world"); + assert_eq!(percent_decode("100%25"), "100%"); + assert_eq!(percent_decode("%2F"), "/"); + assert_eq!(percent_decode("%2f"), "/"); + } + + #[test] + fn test_percent_decode_roundtrip_with_server_encode() { + assert_eq!(percent_decode("a%2Fb"), "a/b"); + assert_eq!(percent_decode("caf%C3%A9"), "caf\u{00c3}\u{00a9}"); + assert_eq!(percent_decode("hello%20world%21"), "hello world!"); + } + + #[test] + fn test_percent_decode_truncated_sequence() { + assert_eq!(percent_decode("abc%2"), "abc"); + assert_eq!(percent_decode("abc%"), "abc"); + } + + #[test] + fn test_percent_decode_invalid_hex() { + assert_eq!(percent_decode("%GG"), ""); + assert_eq!(percent_decode("%ZZ"), ""); + } + + #[test] + fn test_percent_decode_all_ascii_values() { + for byte in 0u8..=127 { + let encoded = format!("%{byte:02X}"); + let decoded = percent_decode(&encoded); + assert_eq!(decoded.as_bytes(), &[byte], "failed for %{byte:02X}"); + } + } + + #[test] + fn test_hex_val() { + for b in b'0'..=b'9' { + assert_eq!(hex_val(b), Some(b - b'0')); + } + for (i, b) in (b'A'..=b'F').enumerate() { + assert_eq!(hex_val(b), Some(10 + i as u8)); + } + for (i, b) in (b'a'..=b'f').enumerate() { + assert_eq!(hex_val(b), Some(10 + i as u8)); + } + assert_eq!(hex_val(b'G'), None); + assert_eq!(hex_val(b'g'), None); + assert_eq!(hex_val(b' '), None); + assert_eq!(hex_val(b'/'), None); + assert_eq!(hex_val(b':'), None); + assert_eq!(hex_val(b'@'), None); + } + + #[test] + fn test_parse_grpc_timeout() { + use std::time::Duration; + assert_eq!(parse_grpc_timeout("5S").unwrap(), Duration::from_secs(5)); + assert_eq!(parse_grpc_timeout("500m").unwrap(), Duration::from_millis(500)); + assert_eq!(parse_grpc_timeout("1H").unwrap(), Duration::from_secs(3600)); + assert_eq!(parse_grpc_timeout("30M").unwrap(), Duration::from_secs(1800)); + assert_eq!(parse_grpc_timeout("100u").unwrap(), Duration::from_micros(100)); + assert_eq!(parse_grpc_timeout("1000n").unwrap(), Duration::from_nanos(1000)); + assert!(parse_grpc_timeout("").is_err()); + assert!(parse_grpc_timeout("S").is_err()); + assert!(parse_grpc_timeout("5x").is_err()); + } + + #[test] + fn test_parse_grpc_timeout_boundary_values() { + use std::time::Duration; + // Zero values + assert_eq!(parse_grpc_timeout("0S").unwrap(), Duration::from_secs(0)); + assert_eq!(parse_grpc_timeout("0m").unwrap(), Duration::from_millis(0)); + // Large values + assert_eq!(parse_grpc_timeout("99999999S").unwrap(), Duration::from_secs(99_999_999)); + // Various invalid formats + assert!(parse_grpc_timeout("5").is_err()); // no unit + assert!(parse_grpc_timeout("abc").is_err()); // non-numeric + assert!(parse_grpc_timeout("5X").is_err()); // unknown unit + assert!(parse_grpc_timeout("5 S").is_err()); // space before unit + } + + #[test] + fn test_parse_grpc_timeout_rejects_too_many_digits() { + let err = parse_grpc_timeout("100000000S").unwrap_err(); + assert_eq!(err.code, GRPC_STATUS_INVALID_ARGUMENT); + + let err = parse_grpc_timeout("18446744073709551615H").unwrap_err(); + assert_eq!(err.code, GRPC_STATUS_INVALID_ARGUMENT); + } + + mod tonic_compat { + use std::convert::Infallible; + use std::future::Future; + use std::net::SocketAddr; + use std::pin::Pin; + + use http_body_util::BodyExt; + use hyper::body::Incoming; + use hyper::server::conn::http2; + use hyper::service::Service; + use hyper::{Request, Response}; + use hyper_util::rt::TokioExecutor; + use prost::Message; + use tokio::net::TcpListener; + use tokio::sync::mpsc as tokio_mpsc; + + use super::*; + use crate::api::{GetNodeInfoRequest, GetNodeInfoResponse}; + use crate::endpoints::{GET_NODE_INFO_PATH, GRPC_SERVICE_PREFIX, SUBSCRIBE_EVENTS_PATH}; + use crate::events::EventEnvelope; + + const TEST_NODE_ID: &str = + "02deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefde"; + + /// Build a full gRPC path from a method name constant. + fn grpc_path(method: &str) -> String { + format!("{GRPC_SERVICE_PREFIX}{method}") + } + + /// Minimal gRPC service for testing. Uses the real GrpcBody, grpc_response, + /// grpc_error_response, and validate_grpc_request from the server code. + #[derive(Clone)] + struct TestGrpcService; + + impl Service> for TestGrpcService { + type Response = Response; + type Error = Infallible; + type Future = Pin> + Send>>; + + fn call(&self, req: Request) -> Self::Future { + Box::pin(async move { + // Use the real gRPC validation + if let Err(status) = validate_grpc_request(&req) { + return Ok(grpc_error_response(status)); + } + + let path = req.uri().path().to_string(); + let method = match path.strip_prefix(GRPC_SERVICE_PREFIX) { + Some(m) => m.to_string(), + None => { + return Ok(grpc_error_response(GrpcStatus::new( + GRPC_STATUS_UNIMPLEMENTED, + format!("Unknown path: {path}"), + ))); + }, + }; + + match method.as_str() { + GET_NODE_INFO_PATH => { + // Read body and decode gRPC frame + let body_bytes = match req.into_body().collect().await { + Ok(collected) => collected.to_bytes(), + Err(_) => { + return Ok(grpc_error_response(GrpcStatus::new( + GRPC_STATUS_INTERNAL, + "Failed to read body", + ))); + }, + }; + + let proto_bytes = match decode_grpc_body(&body_bytes) { + Ok(b) => b, + Err(status) => return Ok(grpc_error_response(status)), + }; + + if GetNodeInfoRequest::decode(proto_bytes).is_err() { + return Ok(grpc_error_response(GrpcStatus::new( + GRPC_STATUS_INVALID_ARGUMENT, + "Malformed request", + ))); + } + + // Return a hardcoded response + let response = GetNodeInfoResponse { + node_id: TEST_NODE_ID.to_string(), + ..Default::default() + }; + let encoded = encode_grpc_frame(&response.encode_to_vec()); + Ok(grpc_response(GrpcBody::Unary { + data: Some(encoded), + trailers_sent: false, + })) + }, + SUBSCRIBE_EVENTS_PATH => { + let (tx, rx) = tokio_mpsc::channel(16); + + // Spawn a task that sends a few events then closes + tokio::spawn(async move { + for _ in 0..3 { + let event = EventEnvelope::default(); + let frame = encode_grpc_frame(&event.encode_to_vec()); + if tx.send(Ok(frame)).await.is_err() { + return; + } + } + // Channel closes => OK trailers + }); + + Ok(grpc_response(GrpcBody::Stream { rx, done: false })) + }, + "SubscribeEventsError" => { + let (tx, rx) = tokio_mpsc::channel(16); + + tokio::spawn(async move { + // Send one event then an error + let event = EventEnvelope::default(); + let frame = encode_grpc_frame(&event.encode_to_vec()); + let _ = tx.send(Ok(frame)).await; + let _ = tx + .send(Err(GrpcStatus::new( + GRPC_STATUS_UNAVAILABLE, + "Server shutting down", + ))) + .await; + }); + + Ok(grpc_response(GrpcBody::Stream { rx, done: false })) + }, + "ErrorWithSpecialChars" => Ok(grpc_error_response(GrpcStatus::new( + GRPC_STATUS_INVALID_ARGUMENT, + "bad request: field/value has special chars (100%)", + ))), + _ => Ok(grpc_error_response(GrpcStatus::new( + GRPC_STATUS_UNIMPLEMENTED, + format!("Unknown method: {method}"), + ))), + } + }) + } + } + + /// Start a plaintext h2c test server on a random port. + async fn start_test_server() -> SocketAddr { + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); + + tokio::spawn(async move { + loop { + let (stream, _) = match listener.accept().await { + Ok(conn) => conn, + Err(_) => continue, + }; + let io = hyper_util::rt::TokioIo::new(stream); + tokio::spawn(async move { + let _ = http2::Builder::new(TokioExecutor::new()) + .serve_connection(io, TestGrpcService) + .await; + }); + } + }); + + addr + } + + /// Connect a tonic Grpc client to the given address. + async fn connect_client( + addr: SocketAddr, + ) -> tonic::client::Grpc { + let endpoint = tonic::transport::Endpoint::from_shared(format!("http://{addr}")) + .unwrap() + .connect_timeout(std::time::Duration::from_secs(5)); + let channel = endpoint.connect().await.unwrap(); + let mut client = tonic::client::Grpc::new(channel); + client.ready().await.unwrap(); + client + } + + #[tokio::test] + async fn test_tonic_unary_success() { + let addr = start_test_server().await; + let mut client = connect_client(addr).await; + + let response: tonic::Response = client + .unary( + tonic::Request::new(GetNodeInfoRequest {}), + grpc_path(GET_NODE_INFO_PATH).parse().unwrap(), + tonic::codec::ProstCodec::default(), + ) + .await + .unwrap(); + + assert_eq!(response.get_ref().node_id, TEST_NODE_ID); + } + + #[tokio::test] + async fn test_tonic_unimplemented_method() { + let addr = start_test_server().await; + let mut client = connect_client(addr).await; + + let result: Result, tonic::Status> = client + .unary( + tonic::Request::new(GetNodeInfoRequest {}), + grpc_path("NoSuchMethod").parse().unwrap(), + tonic::codec::ProstCodec::default(), + ) + .await; + + let status = result.unwrap_err(); + assert_eq!(status.code(), tonic::Code::Unimplemented); + assert!(status.message().contains("Unknown method")); + } + + #[tokio::test] + async fn test_tonic_error_message_with_special_chars() { + let addr = start_test_server().await; + let mut client = connect_client(addr).await; + + let result: Result, tonic::Status> = client + .unary( + tonic::Request::new(GetNodeInfoRequest {}), + grpc_path("ErrorWithSpecialChars").parse().unwrap(), + tonic::codec::ProstCodec::default(), + ) + .await; + + let status = result.unwrap_err(); + assert_eq!(status.code(), tonic::Code::InvalidArgument); + assert_eq!(status.message(), "bad request: field/value has special chars (100%)"); + } + + #[tokio::test] + async fn test_tonic_empty_request_response() { + // GetNodeInfoRequest is an empty message — tests zero-length gRPC frame + let addr = start_test_server().await; + let mut client = connect_client(addr).await; + + let response: tonic::Response = client + .unary( + tonic::Request::new(GetNodeInfoRequest {}), + grpc_path(GET_NODE_INFO_PATH).parse().unwrap(), + tonic::codec::ProstCodec::default(), + ) + .await + .unwrap(); + + // Response is non-empty but request was empty — validates empty frame encoding + assert!(!response.get_ref().node_id.is_empty()); + } + + #[tokio::test] + async fn test_tonic_server_streaming_success() { + use tokio_stream::StreamExt; + + let addr = start_test_server().await; + let mut client = connect_client(addr).await; + + let response = client + .server_streaming( + tonic::Request::new(crate::api::SubscribeEventsRequest {}), + grpc_path(SUBSCRIBE_EVENTS_PATH).parse().unwrap(), + tonic::codec::ProstCodec::default(), + ) + .await + .unwrap(); + + let mut stream = response.into_inner(); + let mut events: Vec = Vec::new(); + while let Some(msg) = stream.next().await { + events.push(msg.unwrap()); + } + + assert_eq!(events.len(), 3); + for event in &events { + assert_eq!(*event, EventEnvelope::default()); + } + } + + #[tokio::test] + async fn test_tonic_server_streaming_error_termination() { + use tokio_stream::StreamExt; + + let addr = start_test_server().await; + let mut client = connect_client(addr).await; + + let response = client + .server_streaming( + tonic::Request::new(crate::api::SubscribeEventsRequest {}), + grpc_path("SubscribeEventsError").parse().unwrap(), + tonic::codec::ProstCodec::default(), + ) + .await + .unwrap(); + + let mut stream = response.into_inner(); + + // First message should succeed + let first: EventEnvelope = stream.next().await.unwrap().unwrap(); + assert_eq!(first, EventEnvelope::default()); + + // Next should be the terminal error + let err = stream.next().await.unwrap().unwrap_err(); + assert_eq!(err.code(), tonic::Code::Unavailable); + assert_eq!(err.message(), "Server shutting down"); + } + } +} diff --git a/ldk-server-protos/src/lib.rs b/ldk-server-grpc/src/lib.rs similarity index 97% rename from ldk-server-protos/src/lib.rs rename to ldk-server-grpc/src/lib.rs index f76f2f73..56dc2228 100644 --- a/ldk-server-protos/src/lib.rs +++ b/ldk-server-grpc/src/lib.rs @@ -11,6 +11,7 @@ pub mod api; pub mod endpoints; pub mod error; pub mod events; +pub mod grpc; #[cfg(feature = "serde")] pub mod serde_utils; pub mod types; diff --git a/ldk-server-protos/src/proto/api.proto b/ldk-server-grpc/src/proto/api.proto similarity index 100% rename from ldk-server-protos/src/proto/api.proto rename to ldk-server-grpc/src/proto/api.proto diff --git a/ldk-server-protos/src/proto/error.proto b/ldk-server-grpc/src/proto/error.proto similarity index 100% rename from ldk-server-protos/src/proto/error.proto rename to ldk-server-grpc/src/proto/error.proto diff --git a/ldk-server-protos/src/proto/events.proto b/ldk-server-grpc/src/proto/events.proto similarity index 100% rename from ldk-server-protos/src/proto/events.proto rename to ldk-server-grpc/src/proto/events.proto diff --git a/ldk-server-protos/src/proto/types.proto b/ldk-server-grpc/src/proto/types.proto similarity index 100% rename from ldk-server-protos/src/proto/types.proto rename to ldk-server-grpc/src/proto/types.proto diff --git a/ldk-server-protos/src/serde_utils.rs b/ldk-server-grpc/src/serde_utils.rs similarity index 100% rename from ldk-server-protos/src/serde_utils.rs rename to ldk-server-grpc/src/serde_utils.rs diff --git a/ldk-server-protos/src/types.rs b/ldk-server-grpc/src/types.rs similarity index 100% rename from ldk-server-protos/src/types.rs rename to ldk-server-grpc/src/types.rs diff --git a/ldk-server/Cargo.toml b/ldk-server/Cargo.toml index 33c099d5..75355a71 100644 --- a/ldk-server/Cargo.toml +++ b/ldk-server/Cargo.toml @@ -14,7 +14,7 @@ tokio-rustls = { version = "0.26", default-features = false, features = ["ring"] ring = { version = "0.17", default-features = false } getrandom = { version = "0.2", default-features = false } prost = { version = "0.11.6", default-features = false, features = ["std"] } -ldk-server-protos = { path = "../ldk-server-protos" } +ldk-server-grpc = { path = "../ldk-server-grpc" } bytes = { version = "1.4.0", default-features = false } hex = { package = "hex-conservative", version = "0.2.1", default-features = false } rusqlite = { version = "0.31.0", features = ["bundled"] } diff --git a/ldk-server/src/api/bolt11_claim_for_hash.rs b/ldk-server/src/api/bolt11_claim_for_hash.rs index 52a668da..eb8c477d 100644 --- a/ldk-server/src/api/bolt11_claim_for_hash.rs +++ b/ldk-server/src/api/bolt11_claim_for_hash.rs @@ -10,7 +10,7 @@ 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_grpc::api::{Bolt11ClaimForHashRequest, Bolt11ClaimForHashResponse}; use crate::api::error::LdkServerError; use crate::api::error::LdkServerErrorCode::InvalidRequestError; diff --git a/ldk-server/src/api/bolt11_fail_for_hash.rs b/ldk-server/src/api/bolt11_fail_for_hash.rs index 61b05814..f25b1125 100644 --- a/ldk-server/src/api/bolt11_fail_for_hash.rs +++ b/ldk-server/src/api/bolt11_fail_for_hash.rs @@ -9,7 +9,7 @@ use hex::FromHex; use ldk_node::lightning_types::payment::PaymentHash; -use ldk_server_protos::api::{Bolt11FailForHashRequest, Bolt11FailForHashResponse}; +use ldk_server_grpc::api::{Bolt11FailForHashRequest, Bolt11FailForHashResponse}; use crate::api::error::LdkServerError; use crate::api::error::LdkServerErrorCode::InvalidRequestError; diff --git a/ldk-server/src/api/bolt11_receive.rs b/ldk-server/src/api/bolt11_receive.rs index 0291bc8f..4ae78bd5 100644 --- a/ldk-server/src/api/bolt11_receive.rs +++ b/ldk-server/src/api/bolt11_receive.rs @@ -8,7 +8,7 @@ // licenses. use hex::DisplayHex; -use ldk_server_protos::api::{Bolt11ReceiveRequest, Bolt11ReceiveResponse}; +use ldk_server_grpc::api::{Bolt11ReceiveRequest, Bolt11ReceiveResponse}; use crate::api::error::LdkServerError; use crate::service::Context; diff --git a/ldk-server/src/api/bolt11_receive_for_hash.rs b/ldk-server/src/api/bolt11_receive_for_hash.rs index 80ec8e5e..b03154d7 100644 --- a/ldk-server/src/api/bolt11_receive_for_hash.rs +++ b/ldk-server/src/api/bolt11_receive_for_hash.rs @@ -9,7 +9,7 @@ use hex::FromHex; use ldk_node::lightning_types::payment::PaymentHash; -use ldk_server_protos::api::{Bolt11ReceiveForHashRequest, Bolt11ReceiveForHashResponse}; +use ldk_server_grpc::api::{Bolt11ReceiveForHashRequest, Bolt11ReceiveForHashResponse}; use crate::api::error::LdkServerError; use crate::api::error::LdkServerErrorCode::InvalidRequestError; 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 5f79e197..8078e5f0 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_grpc::api::{ Bolt11ReceiveVariableAmountViaJitChannelRequest, Bolt11ReceiveVariableAmountViaJitChannelResponse, Bolt11ReceiveViaJitChannelRequest, Bolt11ReceiveViaJitChannelResponse, diff --git a/ldk-server/src/api/bolt11_send.rs b/ldk-server/src/api/bolt11_send.rs index 981290f0..36bdb957 100644 --- a/ldk-server/src/api/bolt11_send.rs +++ b/ldk-server/src/api/bolt11_send.rs @@ -10,7 +10,7 @@ use std::str::FromStr; use ldk_node::lightning_invoice::Bolt11Invoice; -use ldk_server_protos::api::{Bolt11SendRequest, Bolt11SendResponse}; +use ldk_server_grpc::api::{Bolt11SendRequest, Bolt11SendResponse}; use crate::api::build_route_parameters_config_from_proto; use crate::api::error::LdkServerError; diff --git a/ldk-server/src/api/bolt12_receive.rs b/ldk-server/src/api/bolt12_receive.rs index 5c0e3051..e0e6659d 100644 --- a/ldk-server/src/api/bolt12_receive.rs +++ b/ldk-server/src/api/bolt12_receive.rs @@ -8,7 +8,7 @@ // licenses. use hex::DisplayHex; -use ldk_server_protos::api::{Bolt12ReceiveRequest, Bolt12ReceiveResponse}; +use ldk_server_grpc::api::{Bolt12ReceiveRequest, Bolt12ReceiveResponse}; use crate::api::error::LdkServerError; use crate::service::Context; diff --git a/ldk-server/src/api/bolt12_send.rs b/ldk-server/src/api/bolt12_send.rs index d4503a82..ae72623a 100644 --- a/ldk-server/src/api/bolt12_send.rs +++ b/ldk-server/src/api/bolt12_send.rs @@ -10,7 +10,7 @@ use std::str::FromStr; use ldk_node::lightning::offers::offer::Offer; -use ldk_server_protos::api::{Bolt12SendRequest, Bolt12SendResponse}; +use ldk_server_grpc::api::{Bolt12SendRequest, Bolt12SendResponse}; use crate::api::build_route_parameters_config_from_proto; use crate::api::error::LdkServerError; diff --git a/ldk-server/src/api/close_channel.rs b/ldk-server/src/api/close_channel.rs index 6067e2b6..5afa6405 100644 --- a/ldk-server/src/api/close_channel.rs +++ b/ldk-server/src/api/close_channel.rs @@ -11,7 +11,7 @@ use std::str::FromStr; use ldk_node::bitcoin::secp256k1::PublicKey; use ldk_node::UserChannelId; -use ldk_server_protos::api::{ +use ldk_server_grpc::api::{ CloseChannelRequest, CloseChannelResponse, ForceCloseChannelRequest, ForceCloseChannelResponse, }; diff --git a/ldk-server/src/api/connect_peer.rs b/ldk-server/src/api/connect_peer.rs index 54fb87ba..b6373f0f 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_grpc::api::{ConnectPeerRequest, ConnectPeerResponse}; use crate::api::error::LdkServerError; use crate::service::Context; diff --git a/ldk-server/src/api/decode_invoice.rs b/ldk-server/src/api/decode_invoice.rs index 863ec0ea..4478b31b 100644 --- a/ldk-server/src/api/decode_invoice.rs +++ b/ldk-server/src/api/decode_invoice.rs @@ -12,8 +12,8 @@ use std::str::FromStr; use hex::prelude::*; use ldk_node::lightning_invoice::Bolt11Invoice; use ldk_node::lightning_types::features::Bolt11InvoiceFeatures; -use ldk_server_protos::api::{DecodeInvoiceRequest, DecodeInvoiceResponse}; -use ldk_server_protos::types::{Bolt11HopHint, Bolt11RouteHint}; +use ldk_server_grpc::api::{DecodeInvoiceRequest, DecodeInvoiceResponse}; +use ldk_server_grpc::types::{Bolt11HopHint, Bolt11RouteHint}; use crate::api::decode_features; use crate::api::error::LdkServerError; diff --git a/ldk-server/src/api/decode_offer.rs b/ldk-server/src/api/decode_offer.rs index e44dbf6b..b206f485 100644 --- a/ldk-server/src/api/decode_offer.rs +++ b/ldk-server/src/api/decode_offer.rs @@ -14,10 +14,10 @@ use ldk_node::lightning::bitcoin::blockdata::constants::ChainHash; use ldk_node::lightning::bitcoin::Network; use ldk_node::lightning::offers::offer::Offer; use ldk_node::lightning_types::features::OfferFeatures; -use ldk_server_protos::api::{DecodeOfferRequest, DecodeOfferResponse}; -use ldk_server_protos::types::offer_amount::Amount; -use ldk_server_protos::types::offer_quantity::Quantity; -use ldk_server_protos::types::{BlindedPath, CurrencyAmount, OfferAmount, OfferQuantity}; +use ldk_server_grpc::api::{DecodeOfferRequest, DecodeOfferResponse}; +use ldk_server_grpc::types::offer_amount::Amount; +use ldk_server_grpc::types::offer_quantity::Quantity; +use ldk_server_grpc::types::{BlindedPath, CurrencyAmount, OfferAmount, OfferQuantity}; use crate::api::decode_features; use crate::api::error::LdkServerError; diff --git a/ldk-server/src/api/disconnect_peer.rs b/ldk-server/src/api/disconnect_peer.rs index 3e1bbfc6..9b4556aa 100644 --- a/ldk-server/src/api/disconnect_peer.rs +++ b/ldk-server/src/api/disconnect_peer.rs @@ -10,7 +10,7 @@ use std::str::FromStr; use ldk_node::bitcoin::secp256k1::PublicKey; -use ldk_server_protos::api::{DisconnectPeerRequest, DisconnectPeerResponse}; +use ldk_server_grpc::api::{DisconnectPeerRequest, DisconnectPeerResponse}; use crate::api::error::LdkServerError; use crate::service::Context; diff --git a/ldk-server/src/api/export_pathfinding_scores.rs b/ldk-server/src/api/export_pathfinding_scores.rs index a38c2989..c81c0569 100644 --- a/ldk-server/src/api/export_pathfinding_scores.rs +++ b/ldk-server/src/api/export_pathfinding_scores.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::{ExportPathfindingScoresRequest, ExportPathfindingScoresResponse}; +use ldk_server_grpc::api::{ExportPathfindingScoresRequest, ExportPathfindingScoresResponse}; use crate::api::error::LdkServerError; use crate::service::Context; diff --git a/ldk-server/src/api/get_balances.rs b/ldk-server/src/api/get_balances.rs index 626f632f..8dec0f65 100644 --- a/ldk-server/src/api/get_balances.rs +++ b/ldk-server/src/api/get_balances.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::{GetBalancesRequest, GetBalancesResponse}; +use ldk_server_grpc::api::{GetBalancesRequest, GetBalancesResponse}; use crate::api::error::LdkServerError; use crate::service::Context; diff --git a/ldk-server/src/api/get_node_info.rs b/ldk-server/src/api/get_node_info.rs index a84a4408..e6a30dcc 100644 --- a/ldk-server/src/api/get_node_info.rs +++ b/ldk-server/src/api/get_node_info.rs @@ -7,8 +7,8 @@ // 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_server_grpc::api::{GetNodeInfoRequest, GetNodeInfoResponse}; +use ldk_server_grpc::types::BestBlock; use crate::api::error::LdkServerError; use crate::service::Context; diff --git a/ldk-server/src/api/get_payment_details.rs b/ldk-server/src/api/get_payment_details.rs index bfa1badf..cf388e67 100644 --- a/ldk-server/src/api/get_payment_details.rs +++ b/ldk-server/src/api/get_payment_details.rs @@ -9,7 +9,7 @@ use hex::FromHex; use ldk_node::lightning::ln::channelmanager::PaymentId; -use ldk_server_protos::api::{GetPaymentDetailsRequest, GetPaymentDetailsResponse}; +use ldk_server_grpc::api::{GetPaymentDetailsRequest, GetPaymentDetailsResponse}; use crate::api::error::LdkServerError; use crate::api::error::LdkServerErrorCode::InvalidRequestError; diff --git a/ldk-server/src/api/graph_get_channel.rs b/ldk-server/src/api/graph_get_channel.rs index 4266a5bd..2fc5b296 100644 --- a/ldk-server/src/api/graph_get_channel.rs +++ b/ldk-server/src/api/graph_get_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::{GraphGetChannelRequest, GraphGetChannelResponse}; +use ldk_server_grpc::api::{GraphGetChannelRequest, GraphGetChannelResponse}; use crate::api::error::LdkServerError; use crate::api::error::LdkServerErrorCode::InvalidRequestError; diff --git a/ldk-server/src/api/graph_get_node.rs b/ldk-server/src/api/graph_get_node.rs index 71595592..40db9894 100644 --- a/ldk-server/src/api/graph_get_node.rs +++ b/ldk-server/src/api/graph_get_node.rs @@ -8,7 +8,7 @@ // licenses. use ldk_node::lightning::routing::gossip::NodeId; -use ldk_server_protos::api::{GraphGetNodeRequest, GraphGetNodeResponse}; +use ldk_server_grpc::api::{GraphGetNodeRequest, GraphGetNodeResponse}; use crate::api::error::LdkServerError; use crate::api::error::LdkServerErrorCode::InvalidRequestError; diff --git a/ldk-server/src/api/graph_list_channels.rs b/ldk-server/src/api/graph_list_channels.rs index 6f82cba7..1236b539 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_grpc::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 fb9827b3..24ed8f7a 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_grpc::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 1792c2dc..e7c832e3 100644 --- a/ldk-server/src/api/list_channels.rs +++ b/ldk-server/src/api/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::{ListChannelsRequest, ListChannelsResponse}; +use ldk_server_grpc::api::{ListChannelsRequest, ListChannelsResponse}; use crate::api::error::LdkServerError; use crate::service::Context; diff --git a/ldk-server/src/api/list_forwarded_payments.rs b/ldk-server/src/api/list_forwarded_payments.rs index 25e7238e..7d79fda7 100644 --- a/ldk-server/src/api/list_forwarded_payments.rs +++ b/ldk-server/src/api/list_forwarded_payments.rs @@ -8,8 +8,8 @@ // licenses. use bytes::Bytes; -use ldk_server_protos::api::{ListForwardedPaymentsRequest, ListForwardedPaymentsResponse}; -use ldk_server_protos::types::{ForwardedPayment, PageToken}; +use ldk_server_grpc::api::{ListForwardedPaymentsRequest, ListForwardedPaymentsResponse}; +use ldk_server_grpc::types::{ForwardedPayment, PageToken}; use prost::Message; use crate::api::error::LdkServerError; diff --git a/ldk-server/src/api/list_payments.rs b/ldk-server/src/api/list_payments.rs index e5dabd3a..8d58068a 100644 --- a/ldk-server/src/api/list_payments.rs +++ b/ldk-server/src/api/list_payments.rs @@ -8,8 +8,8 @@ // licenses. use bytes::Bytes; -use ldk_server_protos::api::{ListPaymentsRequest, ListPaymentsResponse}; -use ldk_server_protos::types::{PageToken, Payment}; +use ldk_server_grpc::api::{ListPaymentsRequest, ListPaymentsResponse}; +use ldk_server_grpc::types::{PageToken, Payment}; use prost::Message; use crate::api::error::LdkServerError; diff --git a/ldk-server/src/api/list_peers.rs b/ldk-server/src/api/list_peers.rs index 3d4032f5..9c452e43 100644 --- a/ldk-server/src/api/list_peers.rs +++ b/ldk-server/src/api/list_peers.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::{ListPeersRequest, ListPeersResponse}; +use ldk_server_grpc::api::{ListPeersRequest, ListPeersResponse}; use crate::api::error::LdkServerError; use crate::service::Context; diff --git a/ldk-server/src/api/mod.rs b/ldk-server/src/api/mod.rs index 78bd7222..ec0a03f6 100644 --- a/ldk-server/src/api/mod.rs +++ b/ldk-server/src/api/mod.rs @@ -11,8 +11,8 @@ use std::collections::HashMap; use ldk_node::config::{ChannelConfig, MaxDustHTLCExposure}; use ldk_node::lightning::routing::router::RouteParametersConfig; -use ldk_server_protos::types::channel_config::MaxDustHtlcExposure; -use ldk_server_protos::types::Bolt11Feature; +use ldk_server_grpc::types::channel_config::MaxDustHtlcExposure; +use ldk_server_grpc::types::Bolt11Feature; use crate::api::error::LdkServerError; use crate::api::error::LdkServerErrorCode::InvalidRequestError; @@ -54,7 +54,7 @@ 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, + default_config: ChannelConfig, proto_channel_config: ldk_server_grpc::types::ChannelConfig, ) -> Result { let max_dust_htlc_exposure = proto_channel_config .max_dust_htlc_exposure @@ -98,7 +98,7 @@ pub(crate) fn build_channel_config_from_proto( } pub(crate) fn build_route_parameters_config_from_proto( - proto_route_params: Option, + proto_route_params: Option, ) -> Result, LdkServerError> { match proto_route_params { Some(params) => { diff --git a/ldk-server/src/api/onchain_receive.rs b/ldk-server/src/api/onchain_receive.rs index 15414141..8dd367c3 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_grpc::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 c6b0011f..3a52c22a 100644 --- a/ldk-server/src/api/onchain_send.rs +++ b/ldk-server/src/api/onchain_send.rs @@ -10,7 +10,7 @@ use std::str::FromStr; use ldk_node::bitcoin::{Address, FeeRate}; -use ldk_server_protos::api::{OnchainSendRequest, OnchainSendResponse}; +use ldk_server_grpc::api::{OnchainSendRequest, OnchainSendResponse}; use crate::api::error::LdkServerError; use crate::api::error::LdkServerErrorCode::InvalidRequestError; diff --git a/ldk-server/src/api/open_channel.rs b/ldk-server/src/api/open_channel.rs index 89215cb1..9069fde9 100644 --- a/ldk-server/src/api/open_channel.rs +++ b/ldk-server/src/api/open_channel.rs @@ -12,7 +12,7 @@ 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_grpc::api::{OpenChannelRequest, OpenChannelResponse}; use crate::api::build_channel_config_from_proto; use crate::api::error::LdkServerError; diff --git a/ldk-server/src/api/sign_message.rs b/ldk-server/src/api/sign_message.rs index 86cd78ea..73d5ed5a 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_grpc::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 30496120..51b85688 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_grpc::api::{ SpliceInRequest, SpliceInResponse, SpliceOutRequest, SpliceOutResponse, }; diff --git a/ldk-server/src/api/spontaneous_send.rs b/ldk-server/src/api/spontaneous_send.rs index 43c9997d..45d5b3f1 100644 --- a/ldk-server/src/api/spontaneous_send.rs +++ b/ldk-server/src/api/spontaneous_send.rs @@ -10,7 +10,7 @@ use std::str::FromStr; use ldk_node::bitcoin::secp256k1::PublicKey; -use ldk_server_protos::api::{SpontaneousSendRequest, SpontaneousSendResponse}; +use ldk_server_grpc::api::{SpontaneousSendRequest, SpontaneousSendResponse}; use crate::api::build_route_parameters_config_from_proto; use crate::api::error::LdkServerError; diff --git a/ldk-server/src/api/unified_send.rs b/ldk-server/src/api/unified_send.rs index a0a1b4c5..d5038315 100644 --- a/ldk-server/src/api/unified_send.rs +++ b/ldk-server/src/api/unified_send.rs @@ -8,8 +8,8 @@ // licenses. use ldk_node::payment::UnifiedPaymentResult; -use ldk_server_protos::api::unified_send_response::PaymentResult; -use ldk_server_protos::api::{UnifiedSendRequest, UnifiedSendResponse}; +use ldk_server_grpc::api::unified_send_response::PaymentResult; +use ldk_server_grpc::api::{UnifiedSendRequest, UnifiedSendResponse}; use crate::api::build_route_parameters_config_from_proto; use crate::api::error::LdkServerError; diff --git a/ldk-server/src/api/update_channel_config.rs b/ldk-server/src/api/update_channel_config.rs index 207d3b04..dd2ae61a 100644 --- a/ldk-server/src/api/update_channel_config.rs +++ b/ldk-server/src/api/update_channel_config.rs @@ -11,7 +11,7 @@ use std::str::FromStr; use ldk_node::bitcoin::secp256k1::PublicKey; use ldk_node::UserChannelId; -use ldk_server_protos::api::{UpdateChannelConfigRequest, UpdateChannelConfigResponse}; +use ldk_server_grpc::api::{UpdateChannelConfigRequest, UpdateChannelConfigResponse}; use crate::api::build_channel_config_from_proto; use crate::api::error::LdkServerError; diff --git a/ldk-server/src/api/verify_signature.rs b/ldk-server/src/api/verify_signature.rs index 5b1125a0..0eaec830 100644 --- a/ldk-server/src/api/verify_signature.rs +++ b/ldk-server/src/api/verify_signature.rs @@ -10,7 +10,7 @@ use std::str::FromStr; use ldk_node::bitcoin::secp256k1::PublicKey; -use ldk_server_protos::api::{VerifySignatureRequest, VerifySignatureResponse}; +use ldk_server_grpc::api::{VerifySignatureRequest, VerifySignatureResponse}; use crate::api::error::LdkServerError; use crate::api::error::LdkServerErrorCode::InvalidRequestError; diff --git a/ldk-server/src/main.rs b/ldk-server/src/main.rs index 8a7119a3..43720673 100644 --- a/ldk-server/src/main.rs +++ b/ldk-server/src/main.rs @@ -8,6 +8,7 @@ // licenses. mod api; +mod grpc; mod io; mod service; mod util; @@ -29,9 +30,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_grpc::events; +use ldk_server_grpc::events::{event_envelope, EventEnvelope}; +use ldk_server_grpc::types::Payment; use log::{debug, error, info}; use prost::Message; use tokio::net::TcpListener; diff --git a/ldk-server/src/util/proto_adapter.rs b/ldk-server/src/util/proto_adapter.rs index 77441dfd..8576752e 100644 --- a/ldk-server/src/util/proto_adapter.rs +++ b/ldk-server/src/util/proto_adapter.rs @@ -23,19 +23,19 @@ 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::{ +use ldk_server_grpc::error::{ErrorCode, ErrorResponse}; +use ldk_server_grpc::types::confirmation_status::Status::{Confirmed, Unconfirmed}; +use ldk_server_grpc::types::lightning_balance::BalanceType::{ ClaimableAwaitingConfirmations, ClaimableOnChannelClose, ContentiousClaimable, CounterpartyRevokedOutputClaimable, MaybePreimageClaimableHtlc, MaybeTimeoutClaimableHtlc, }; -use ldk_server_protos::types::payment_kind::Kind::{ +use ldk_server_grpc::types::payment_kind::Kind::{ Bolt11, Bolt11Jit, Bolt12Offer, Bolt12Refund, Onchain, Spontaneous, }; -use ldk_server_protos::types::pending_sweep_balance::BalanceType::{ +use ldk_server_grpc::types::pending_sweep_balance::BalanceType::{ AwaitingThresholdConfirmations, BroadcastAwaitingConfirmation, PendingBroadcast, }; -use ldk_server_protos::types::{ +use ldk_server_grpc::types::{ bolt11_invoice_description, Channel, ForwardedPayment, LspFeeLimits, OutPoint, Payment, Peer, }; @@ -92,8 +92,8 @@ pub(crate) fn channel_to_proto(channel: ChannelDetails) -> Channel { pub(crate) fn channel_config_to_proto( channel_config: ChannelConfig, -) -> ldk_server_protos::types::ChannelConfig { - ldk_server_protos::types::ChannelConfig { +) -> ldk_server_grpc::types::ChannelConfig { + ldk_server_grpc::types::ChannelConfig { forwarding_fee_proportional_millionths: Some( channel_config.forwarding_fee_proportional_millionths, ), @@ -105,12 +105,12 @@ 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( + Some(ldk_server_grpc::types::channel_config::MaxDustHtlcExposure::FixedLimitMsat( limit_msat, )) }, MaxDustHTLCExposure::FeeRateMultiplier { multiplier } => Some( - ldk_server_protos::types::channel_config::MaxDustHtlcExposure::FeeRateMultiplier( + ldk_server_grpc::types::channel_config::MaxDustHtlcExposure::FeeRateMultiplier( multiplier, ), ), @@ -135,15 +135,13 @@ pub(crate) fn payment_to_proto(payment: PaymentDetails) -> Payment { 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_grpc::types::PaymentDirection::Inbound.into(), + PaymentDirection::Outbound => ldk_server_grpc::types::PaymentDirection::Outbound.into(), }, 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_grpc::types::PaymentStatus::Pending.into(), + PaymentStatus::Succeeded => ldk_server_grpc::types::PaymentStatus::Succeeded.into(), + PaymentStatus::Failed => ldk_server_grpc::types::PaymentStatus::Failed.into(), }, latest_update_timestamp, } @@ -151,16 +149,16 @@ pub(crate) fn payment_to_proto(payment: PaymentDetails) -> Payment { pub(crate) fn payment_kind_to_proto( payment_kind: PaymentKind, -) -> ldk_server_protos::types::PaymentKind { +) -> ldk_server_grpc::types::PaymentKind { match payment_kind { - PaymentKind::Onchain { txid, status } => ldk_server_protos::types::PaymentKind { - kind: Some(Onchain(ldk_server_protos::types::Onchain { + PaymentKind::Onchain { txid, status } => ldk_server_grpc::types::PaymentKind { + kind: Some(Onchain(ldk_server_grpc::types::Onchain { txid: txid.to_string(), status: Some(confirmation_status_to_proto(status)), })), }, - PaymentKind::Bolt11 { hash, preimage, secret } => ldk_server_protos::types::PaymentKind { - kind: Some(Bolt11(ldk_server_protos::types::Bolt11 { + PaymentKind::Bolt11 { hash, preimage, secret } => ldk_server_grpc::types::PaymentKind { + kind: Some(Bolt11(ldk_server_grpc::types::Bolt11 { hash: hash.to_string(), preimage: preimage.map(|p| p.to_string()), secret: secret.map(|s| Bytes::copy_from_slice(&s.0)), @@ -172,8 +170,8 @@ 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 { + } => ldk_server_grpc::types::PaymentKind { + kind: Some(Bolt11Jit(ldk_server_grpc::types::Bolt11Jit { hash: hash.to_string(), preimage: preimage.map(|p| p.to_string()), secret: secret.map(|s| Bytes::copy_from_slice(&s.0)), @@ -186,8 +184,8 @@ pub(crate) fn payment_kind_to_proto( })), }, PaymentKind::Bolt12Offer { hash, preimage, secret, offer_id, payer_note, quantity } => { - ldk_server_protos::types::PaymentKind { - kind: Some(Bolt12Offer(ldk_server_protos::types::Bolt12Offer { + ldk_server_grpc::types::PaymentKind { + kind: Some(Bolt12Offer(ldk_server_grpc::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)), @@ -198,8 +196,8 @@ pub(crate) fn payment_kind_to_proto( } }, PaymentKind::Bolt12Refund { hash, preimage, secret, payer_note, quantity } => { - ldk_server_protos::types::PaymentKind { - kind: Some(Bolt12Refund(ldk_server_protos::types::Bolt12Refund { + ldk_server_grpc::types::PaymentKind { + kind: Some(Bolt12Refund(ldk_server_grpc::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)), @@ -208,8 +206,8 @@ pub(crate) fn payment_kind_to_proto( })), } }, - PaymentKind::Spontaneous { hash, preimage } => ldk_server_protos::types::PaymentKind { - kind: Some(Spontaneous(ldk_server_protos::types::Spontaneous { + PaymentKind::Spontaneous { hash, preimage } => ldk_server_grpc::types::PaymentKind { + kind: Some(Spontaneous(ldk_server_grpc::types::Spontaneous { hash: hash.to_string(), preimage: preimage.map(|p| p.to_string()), })), @@ -219,26 +217,26 @@ pub(crate) fn payment_kind_to_proto( pub(crate) fn confirmation_status_to_proto( confirmation_status: ConfirmationStatus, -) -> ldk_server_protos::types::ConfirmationStatus { +) -> ldk_server_grpc::types::ConfirmationStatus { match confirmation_status { ConfirmationStatus::Confirmed { block_hash, height, timestamp } => { - ldk_server_protos::types::ConfirmationStatus { - status: Some(Confirmed(ldk_server_protos::types::Confirmed { + ldk_server_grpc::types::ConfirmationStatus { + status: Some(Confirmed(ldk_server_grpc::types::Confirmed { block_hash: block_hash.to_string(), height, timestamp, })), } }, - ConfirmationStatus::Unconfirmed => ldk_server_protos::types::ConfirmationStatus { - status: Some(Unconfirmed(ldk_server_protos::types::Unconfirmed {})), + ConfirmationStatus::Unconfirmed => ldk_server_grpc::types::ConfirmationStatus { + status: Some(Unconfirmed(ldk_server_grpc::types::Unconfirmed {})), }, } } pub(crate) fn lightning_balance_to_proto( lightning_balance: LightningBalance, -) -> ldk_server_protos::types::LightningBalance { +) -> ldk_server_grpc::types::LightningBalance { match lightning_balance { LightningBalance::ClaimableOnChannelClose { channel_id, @@ -249,9 +247,9 @@ 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 { + } => ldk_server_grpc::types::LightningBalance { balance_type: Some(ClaimableOnChannelClose( - ldk_server_protos::types::ClaimableOnChannelClose { + ldk_server_grpc::types::ClaimableOnChannelClose { channel_id: channel_id.0.to_lower_hex_string(), counterparty_node_id: counterparty_node_id.to_string(), amount_satoshis, @@ -269,24 +267,24 @@ pub(crate) fn lightning_balance_to_proto( amount_satoshis, confirmation_height, source, - } => ldk_server_protos::types::LightningBalance { + } => ldk_server_grpc::types::LightningBalance { balance_type: Some(ClaimableAwaitingConfirmations( - ldk_server_protos::types::ClaimableAwaitingConfirmations { + ldk_server_grpc::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() + ldk_server_grpc::types::BalanceSource::HolderForceClosed.into() }, BalanceSource::CounterpartyForceClosed => { - ldk_server_protos::types::BalanceSource::CounterpartyForceClosed.into() + ldk_server_grpc::types::BalanceSource::CounterpartyForceClosed.into() }, BalanceSource::CoopClose => { - ldk_server_protos::types::BalanceSource::CoopClose.into() + ldk_server_grpc::types::BalanceSource::CoopClose.into() }, - BalanceSource::Htlc => ldk_server_protos::types::BalanceSource::Htlc.into(), + BalanceSource::Htlc => ldk_server_grpc::types::BalanceSource::Htlc.into(), }, }, )), @@ -298,9 +296,9 @@ pub(crate) fn lightning_balance_to_proto( timeout_height, payment_hash, payment_preimage, - } => ldk_server_protos::types::LightningBalance { + } => ldk_server_grpc::types::LightningBalance { balance_type: Some(ContentiousClaimable( - ldk_server_protos::types::ContentiousClaimable { + ldk_server_grpc::types::ContentiousClaimable { channel_id: channel_id.0.to_lower_hex_string(), counterparty_node_id: counterparty_node_id.to_string(), amount_satoshis, @@ -317,9 +315,9 @@ pub(crate) fn lightning_balance_to_proto( claimable_height, payment_hash, outbound_payment, - } => ldk_server_protos::types::LightningBalance { + } => ldk_server_grpc::types::LightningBalance { balance_type: Some(MaybeTimeoutClaimableHtlc( - ldk_server_protos::types::MaybeTimeoutClaimableHtlc { + ldk_server_grpc::types::MaybeTimeoutClaimableHtlc { channel_id: channel_id.0.to_lower_hex_string(), counterparty_node_id: counterparty_node_id.to_string(), amount_satoshis, @@ -335,9 +333,9 @@ pub(crate) fn lightning_balance_to_proto( amount_satoshis, expiry_height, payment_hash, - } => ldk_server_protos::types::LightningBalance { + } => ldk_server_grpc::types::LightningBalance { balance_type: Some(MaybePreimageClaimableHtlc( - ldk_server_protos::types::MaybePreimageClaimableHtlc { + ldk_server_grpc::types::MaybePreimageClaimableHtlc { channel_id: channel_id.0.to_lower_hex_string(), counterparty_node_id: counterparty_node_id.to_string(), amount_satoshis, @@ -350,9 +348,9 @@ pub(crate) fn lightning_balance_to_proto( channel_id, counterparty_node_id, amount_satoshis, - } => ldk_server_protos::types::LightningBalance { + } => ldk_server_grpc::types::LightningBalance { balance_type: Some(CounterpartyRevokedOutputClaimable( - ldk_server_protos::types::CounterpartyRevokedOutputClaimable { + ldk_server_grpc::types::CounterpartyRevokedOutputClaimable { channel_id: channel_id.0.to_lower_hex_string(), counterparty_node_id: counterparty_node_id.to_string(), amount_satoshis, @@ -364,11 +362,11 @@ pub(crate) fn lightning_balance_to_proto( pub(crate) fn pending_sweep_balance_to_proto( pending_sweep_balance: PendingSweepBalance, -) -> ldk_server_protos::types::PendingSweepBalance { +) -> ldk_server_grpc::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 { + ldk_server_grpc::types::PendingSweepBalance { + balance_type: Some(PendingBroadcast(ldk_server_grpc::types::PendingBroadcast { channel_id: channel_id.map(|c| c.0.to_lower_hex_string()), amount_satoshis, })), @@ -379,9 +377,9 @@ pub(crate) fn pending_sweep_balance_to_proto( latest_broadcast_height, latest_spending_txid, amount_satoshis, - } => ldk_server_protos::types::PendingSweepBalance { + } => ldk_server_grpc::types::PendingSweepBalance { balance_type: Some(BroadcastAwaitingConfirmation( - ldk_server_protos::types::BroadcastAwaitingConfirmation { + ldk_server_grpc::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(), @@ -395,9 +393,9 @@ pub(crate) fn pending_sweep_balance_to_proto( confirmation_hash, confirmation_height, amount_satoshis, - } => ldk_server_protos::types::PendingSweepBalance { + } => ldk_server_grpc::types::PendingSweepBalance { balance_type: Some(AwaitingThresholdConfirmations( - ldk_server_protos::types::AwaitingThresholdConfirmations { + ldk_server_grpc::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(), @@ -435,7 +433,7 @@ pub(crate) fn forwarded_payment_to_proto( } pub(crate) fn proto_to_bolt11_description( - description: Option, + description: Option, ) -> Result { Ok(match description.and_then(|d| d.kind) { Some(bolt11_invoice_description::Kind::Direct(s)) => { @@ -468,8 +466,8 @@ pub(crate) fn proto_to_bolt11_description( pub(crate) fn graph_routing_fees_to_proto( fees: RoutingFees, -) -> ldk_server_protos::types::GraphRoutingFees { - ldk_server_protos::types::GraphRoutingFees { +) -> ldk_server_grpc::types::GraphRoutingFees { + ldk_server_grpc::types::GraphRoutingFees { base_msat: fees.base_msat, proportional_millionths: fees.proportional_millionths, } @@ -477,8 +475,8 @@ pub(crate) fn graph_routing_fees_to_proto( pub(crate) fn graph_channel_update_to_proto( update: ChannelUpdateInfo, -) -> ldk_server_protos::types::GraphChannelUpdate { - ldk_server_protos::types::GraphChannelUpdate { +) -> ldk_server_grpc::types::GraphChannelUpdate { + ldk_server_grpc::types::GraphChannelUpdate { last_update: update.last_update, enabled: update.enabled, cltv_expiry_delta: update.cltv_expiry_delta as u32, @@ -488,10 +486,8 @@ pub(crate) fn graph_channel_update_to_proto( } } -pub(crate) fn graph_channel_to_proto( - channel: ChannelInfo, -) -> ldk_server_protos::types::GraphChannel { - ldk_server_protos::types::GraphChannel { +pub(crate) fn graph_channel_to_proto(channel: ChannelInfo) -> ldk_server_grpc::types::GraphChannel { + ldk_server_grpc::types::GraphChannel { node_one: channel.node_one.to_string(), node_two: channel.node_two.to_string(), capacity_sats: channel.capacity_sats, @@ -502,9 +498,9 @@ pub(crate) fn graph_channel_to_proto( pub(crate) fn graph_node_announcement_to_proto( announcement: NodeAnnouncementInfo, -) -> ldk_server_protos::types::GraphNodeAnnouncement { +) -> ldk_server_grpc::types::GraphNodeAnnouncement { let rgb = announcement.rgb(); - ldk_server_protos::types::GraphNodeAnnouncement { + ldk_server_grpc::types::GraphNodeAnnouncement { last_update: announcement.last_update(), alias: announcement.alias().to_string(), rgb: format!("{:02x}{:02x}{:02x}", rgb[0], rgb[1], rgb[2]), @@ -512,8 +508,8 @@ 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_proto(node: NodeInfo) -> ldk_server_grpc::types::GraphNode { + ldk_server_grpc::types::GraphNode { channels: node.channels, announcement_info: node.announcement_info.map(graph_node_announcement_to_proto), } From d161d4070727c3ff259e905f3a09a7d93f5b38cd Mon Sep 17 00:00:00 2001 From: benthecarman Date: Fri, 27 Mar 2026 13:51:38 -0500 Subject: [PATCH 4/8] Replace REST with gRPC on server MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switch from HTTP/1.1 protobuf-over-REST to gRPC over HTTP/2, implemented directly on hyper without tonic. The gRPC framing module handles encode/decode of the 5-byte length-prefixed wire format, status codes, and HTTP/2 trailers. Key changes: - hyper http1 → http2, add TokioExecutor for HTTP/2 connections - TLS ALPN set to h2 for HTTP/2 negotiation - HMAC auth simplified to timestamp-only (no body) since TLS guarantees integrity - Events delivered via tokio broadcast channel, replacing the RabbitMQ EventPublisher infrastructure - Config renamed rest_service_addr → grpc_service_addr - Removed lapin, async-trait, events-rabbitmq feature Co-Authored-By: Claude Opus 4.6 (1M context) --- ldk-server/Cargo.toml | 14 +- ldk-server/ldk-server-config.toml | 7 +- ldk-server/src/io/events/event_publisher.rs | 68 -- ldk-server/src/io/events/mod.rs | 26 - ldk-server/src/io/events/rabbitmq/mod.rs | 244 ------- ldk-server/src/io/mod.rs | 1 - ldk-server/src/main.rs | 92 +-- ldk-server/src/service.rs | 701 ++++++++------------ ldk-server/src/util/config.rs | 181 +---- ldk-server/src/util/proto_adapter.rs | 26 +- ldk-server/src/util/tls.rs | 6 +- 11 files changed, 338 insertions(+), 1028 deletions(-) delete mode 100644 ldk-server/src/io/events/event_publisher.rs delete mode 100644 ldk-server/src/io/events/mod.rs delete mode 100644 ldk-server/src/io/events/rabbitmq/mod.rs diff --git a/ldk-server/Cargo.toml b/ldk-server/Cargo.toml index 75355a71..bf4d7d94 100644 --- a/ldk-server/Cargo.toml +++ b/ldk-server/Cargo.toml @@ -6,10 +6,10 @@ edition = "2021" [dependencies] ldk-node = { git = "https://github.com/lightningdevkit/ldk-node", rev = "3aef2b39265ae60b29f4d60de8291895f12eb880" } serde = { version = "1.0.203", default-features = false, features = ["derive"] } -hyper = { version = "1", default-features = false, features = ["server", "http1"] } +hyper = { version = "1", default-features = false, features = ["server", "http2"] } http-body-util = { version = "0.1", default-features = false } -hyper-util = { version = "0.1", default-features = false, features = ["server-graceful"] } -tokio = { version = "1.38.0", default-features = false, features = ["time", "signal", "rt-multi-thread"] } +hyper-util = { version = "0.1", default-features = false, features = ["server-graceful", "tokio"] } +tokio = { version = "1.38.0", default-features = false, features = ["time", "signal", "rt-multi-thread", "sync"] } tokio-rustls = { version = "0.26", default-features = false, features = ["ring"] } ring = { version = "0.17", default-features = false } getrandom = { version = "0.2", default-features = false } @@ -18,25 +18,17 @@ ldk-server-grpc = { path = "../ldk-server-grpc" } bytes = { version = "1.4.0", default-features = false } hex = { package = "hex-conservative", version = "0.2.1", default-features = false } rusqlite = { version = "0.31.0", features = ["bundled"] } -async-trait = { version = "0.1.85", default-features = false } toml = { version = "0.8.9", default-features = false, features = ["parse"] } chrono = { version = "0.4", default-features = false, features = ["clock"] } log = "0.4.28" base64 = { version = "0.21", default-features = false, features = ["std"] } clap = { version = "4.0.5", default-features = false, features = ["derive", "std", "error-context", "suggestions", "help", "env"] } -# Required for RabittMQ based EventPublisher. Only enabled for `events-rabbitmq` feature. -lapin = { version = "2.4.0", features = ["rustls"], default-features = false, optional = true } - [features] default = [] -events-rabbitmq = ["dep:lapin"] # Experimental Features. experimental-lsps2-support = [] -# Feature-flags related to integration tests. -integration-tests-events-rabbitmq = ["events-rabbitmq"] - [dev-dependencies] futures-util = "0.3.31" diff --git a/ldk-server/ldk-server-config.toml b/ldk-server/ldk-server-config.toml index 1be13984..9e855fe4 100644 --- a/ldk-server/ldk-server-config.toml +++ b/ldk-server/ldk-server-config.toml @@ -3,7 +3,7 @@ network = "regtest" # Bitcoin network to use listening_addresses = ["localhost:3001"] # Lightning node listening addresses announcement_addresses = ["54.3.7.81:3001"] # Lightning node announcement addresses -#rest_service_address = "127.0.0.1:3536" # LDK Server REST address (optional, defaults to 127.0.0.1:3536) +#grpc_service_address = "127.0.0.1:3536" # LDK Server gRPC address (optional, defaults to 127.0.0.1:3536) alias = "ldk_server" # Lightning node alias #pathfinding_scores_source_url = "" # External Pathfinding Scores Source #rgs_server_url = "https://rapidsync.lightningdevkit.org/snapshot/v2/" # Optional: RGS URL for rapid gossip sync @@ -38,11 +38,6 @@ server_url = "ssl://electrum.blockstream.info:50002" # Electrum endpoint [esplora] server_url = "https://mempool.space/api" # Esplora endpoint -# RabbitMQ settings (only required if using events-rabbitmq feature) -[rabbitmq] -connection_string = "" # RabbitMQ connection string -exchange_name = "" - # LSPS2 Client Support [liquidity.lsps2_client] # The public key of the LSPS2 LSP we source just-in-time liquidity from. diff --git a/ldk-server/src/io/events/event_publisher.rs b/ldk-server/src/io/events/event_publisher.rs deleted file mode 100644 index 308bde25..00000000 --- a/ldk-server/src/io/events/event_publisher.rs +++ /dev/null @@ -1,68 +0,0 @@ -// This file is Copyright its original authors, visible in version control -// history. -// -// This file is licensed under the Apache License, Version 2.0 or the MIT license -// , at your option. -// You may not use this file except in accordance with one or both of these -// licenses. - -use async_trait::async_trait; -use ldk_server_protos::events::EventEnvelope; - -use crate::api::error::LdkServerError; - -/// A trait for publishing events or notifications from the LDK Server. -/// -/// Implementors of this trait define how events are sent to various messaging -/// systems. It provides a consistent, asynchronous interface for event publishing, while allowing -/// each implementation to manage its own initialization and configuration, typically sourced from -/// the `ldk-server.config` file. A no-op implementation is included by default, -/// with specific implementations enabled via feature flags. -/// -/// Events are represented as [`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. -/// -/// The underlying messaging system is expected to support durably buffered events, -/// enabling easy decoupling between the LDK Server and event consumers. -#[async_trait] -pub trait EventPublisher: Send + Sync { - /// Publishes an event to the underlying messaging system. - /// - /// # Arguments - /// * `event` - The event message to publish, provided as an [`EventEnvelope`] - /// defined in [`ldk_server_protos::events`]. Implementors must serialize - /// the whole [`EventEnvelope`] to bytes before publishing. - /// - /// In order to ensure no events are lost, implementors of this trait must publish events - /// durably to underlying messaging system. An event is considered published when - /// [`EventPublisher::publish`] returns `Ok(())`, thus implementors MUST durably persist/publish events *before* - /// returning `Ok(())`. - /// - /// # Errors - /// May return an [`LdkServerErrorCode::InternalServerError`] if the event cannot be published, - /// such as due to network failures, misconfiguration, or transport-specific issues. - /// If event publishing fails, the LDK Server will retry publishing the event indefinitely, which - /// may degrade performance until the underlying messaging system is operational again. - /// - /// [`LdkServerErrorCode::InternalServerError`]: crate::api::error::LdkServerErrorCode - async fn publish(&self, event: EventEnvelope) -> Result<(), LdkServerError>; -} - -/// A no-op implementation of the [`EventPublisher`] trait. -#[cfg(not(feature = "events-rabbitmq"))] -pub(crate) struct NoopEventPublisher; - -#[async_trait] -#[cfg(not(feature = "events-rabbitmq"))] -impl EventPublisher for NoopEventPublisher { - /// Publishes an event to a no-op sink, effectively discarding it. - /// - /// This implementation does nothing and always returns `Ok(())`, serving as a - /// default when no messaging system is configured. - async fn publish(&self, _event: EventEnvelope) -> Result<(), LdkServerError> { - Ok(()) - } -} diff --git a/ldk-server/src/io/events/mod.rs b/ldk-server/src/io/events/mod.rs deleted file mode 100644 index 4ec9a65a..00000000 --- a/ldk-server/src/io/events/mod.rs +++ /dev/null @@ -1,26 +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. - -pub(crate) mod event_publisher; - -#[cfg(feature = "events-rabbitmq")] -pub(crate) mod rabbitmq; - -use ldk_server_protos::events::event_envelope; - -/// Event variant to event name mapping. -pub(crate) fn get_event_name(event: &event_envelope::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", - } -} diff --git a/ldk-server/src/io/events/rabbitmq/mod.rs b/ldk-server/src/io/events/rabbitmq/mod.rs deleted file mode 100644 index a20b5bd9..00000000 --- a/ldk-server/src/io/events/rabbitmq/mod.rs +++ /dev/null @@ -1,244 +0,0 @@ -// This file is Copyright its original authors, visible in version control -// history. -// -// This file is licensed under the Apache License, Version 2.0 or the MIT license -// , at your option. -// You may not use this file except in accordance with one or both of these -// licenses. - -use std::sync::Arc; - -use ::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 tokio::sync::Mutex; - -use crate::api::error::LdkServerError; -use crate::api::error::LdkServerErrorCode::InternalServerError; -use crate::io::events::event_publisher::EventPublisher; - -/// A RabbitMQ-based implementation of the EventPublisher trait. -pub struct RabbitMqEventPublisher { - /// The RabbitMQ connection, used for reconnection logic. - connection: Arc>>, - /// The RabbitMQ channel used for publishing events. - channel: Arc>>, - /// Configuration details, including connection string and exchange name. - config: RabbitMqConfig, -} - -/// Configuration for the RabbitMQ event publisher. -#[derive(Debug, Clone)] -pub struct RabbitMqConfig { - pub connection_string: String, - pub exchange_name: String, -} - -/// Delivery mode for persistent messages (written to disk). -const DELIVERY_MODE_PERSISTENT: u8 = 2; - -impl RabbitMqEventPublisher { - /// Creates a new RabbitMqEventPublisher instance. - pub fn new(config: RabbitMqConfig) -> Self { - Self { connection: Arc::new(Mutex::new(None)), channel: Arc::new(Mutex::new(None)), config } - } - - async fn connect(config: &RabbitMqConfig) -> Result<(Connection, Channel), LdkServerError> { - let conn = Connection::connect(&config.connection_string, ConnectionProperties::default()) - .await - .map_err(|e| { - LdkServerError::new( - InternalServerError, - format!("Failed to connect to RabbitMQ: {}", e), - ) - })?; - - let channel = conn.create_channel().await.map_err(|e| { - LdkServerError::new(InternalServerError, format!("Failed to create channel: {}", e)) - })?; - - channel.confirm_select(ConfirmSelectOptions::default()).await.map_err(|e| { - LdkServerError::new(InternalServerError, format!("Failed to enable confirms: {}", e)) - })?; - - channel - .exchange_declare( - &config.exchange_name, - ExchangeKind::Fanout, - ExchangeDeclareOptions { durable: true, ..Default::default() }, - FieldTable::default(), - ) - .await - .map_err(|e| { - LdkServerError::new( - InternalServerError, - format!("Failed to declare exchange: {}", e), - ) - })?; - - Ok((conn, channel)) - } - - async fn ensure_connected(&self) -> Result<(), LdkServerError> { - { - let connection = self.connection.lock().await; - if let Some(connection) = &*connection { - if connection.status().state() == ConnectionState::Connected { - return Ok(()); - } - } - } - - // Connection is not alive, attempt reconnecting. - let (connection, channel) = Self::connect(&self.config) - .await - .map_err(|e| LdkServerError::new(InternalServerError, e.to_string()))?; - *self.connection.lock().await = Some(connection); - *self.channel.lock().await = Some(channel); - Ok(()) - } -} - -#[async_trait] -impl EventPublisher for RabbitMqEventPublisher { - /// Publishes an event to RabbitMQ. - /// - /// The event is published to a fanout exchange with persistent delivery mode, - /// and the method waits for confirmation from RabbitMQ to ensure durability. - async fn publish(&self, event: EventEnvelope) -> Result<(), LdkServerError> { - // Ensure connection is alive before proceeding - self.ensure_connected().await?; - - let channel_guard = self.channel.lock().await; - let channel = channel_guard.as_ref().ok_or_else(|| { - LdkServerError::new(InternalServerError, "Channel not initialized".to_string()) - })?; - - // Publish the event with persistent delivery mode - let confirm = channel - .basic_publish( - &self.config.exchange_name, - "", // Empty routing key should be used for fanout exchange, since it is ignored. - BasicPublishOptions::default(), - &event.encode_to_vec(), - BasicProperties::default().with_delivery_mode(DELIVERY_MODE_PERSISTENT), - ) - .await - .map_err(|e| { - LdkServerError::new( - InternalServerError, - format!("Failed to publish event, error: {}", e), - ) - })?; - - let confirmation = confirm.await.map_err(|e| { - LdkServerError::new(InternalServerError, format!("Failed to get confirmation: {}", e)) - })?; - - match confirmation { - lapin::publisher_confirm::Confirmation::Ack(_) => Ok(()), - lapin::publisher_confirm::Confirmation::Nack(_) => Err(LdkServerError::new( - InternalServerError, - "Message not acknowledged".to_string(), - )), - _ => { - Err(LdkServerError::new(InternalServerError, "Unexpected confirmation".to_string())) - }, - } - } -} - -#[cfg(test)] -#[cfg(feature = "integration-tests-events-rabbitmq")] -mod integration_tests_events_rabbitmq { - use std::io; - use std::time::Duration; - - use futures_util::stream::StreamExt; - use lapin::options::{ - BasicAckOptions, BasicConsumeOptions, QueueBindOptions, QueueDeclareOptions, - }; - use lapin::types::FieldTable; - use lapin::{Channel, Connection}; - use ldk_server_protos::events::event_envelope::Event; - use ldk_server_protos::events::PaymentForwarded; - use tokio; - - use super::*; - #[tokio::test] - async fn test_publish_and_consume_event() { - let config = RabbitMqConfig { - connection_string: "amqp://guest:guest@localhost:5672/%2f".to_string(), - exchange_name: "test_exchange".to_string(), - }; - - let publisher = RabbitMqEventPublisher::new(config.clone()); - - let conn = Connection::connect(&config.connection_string, ConnectionProperties::default()) - .await - .expect("Failed make rabbitmq connection"); - let channel = conn.create_channel().await.expect("Failed to create rabbitmq channel"); - - let queue_name = "test_queue"; - setup_queue(&queue_name, &channel, &config).await; - - let event = - EventEnvelope { event: Some(Event::PaymentForwarded(PaymentForwarded::default())) }; - publisher.publish(event.clone()).await.expect("Failed to publish event"); - - consume_event(&queue_name, &channel, &event).await.expect("Failed to consume event"); - } - - async fn setup_queue(queue_name: &str, channel: &Channel, config: &RabbitMqConfig) { - channel - .queue_declare(queue_name, QueueDeclareOptions::default(), FieldTable::default()) - .await - .unwrap(); - channel - .exchange_declare( - &config.exchange_name, - ExchangeKind::Fanout, - ExchangeDeclareOptions { durable: true, ..Default::default() }, - FieldTable::default(), - ) - .await - .unwrap(); - - channel - .queue_bind( - queue_name, - &config.exchange_name, - "", - QueueBindOptions::default(), - FieldTable::default(), - ) - .await - .unwrap(); - } - - async fn consume_event( - queue_name: &str, channel: &Channel, expected_event: &EventEnvelope, - ) -> io::Result<()> { - let mut consumer = channel - .basic_consume( - queue_name, - "test_consumer", - BasicConsumeOptions::default(), - FieldTable::default(), - ) - .await - .unwrap(); - let delivery = - tokio::time::timeout(Duration::from_secs(10), consumer.next()).await?.unwrap().unwrap(); - let received_event = EventEnvelope::decode(&*delivery.data)?; - assert_eq!(received_event, *expected_event, "Event mismatch"); - channel.basic_ack(delivery.delivery_tag, BasicAckOptions::default()).await.unwrap(); - Ok(()) - } -} diff --git a/ldk-server/src/io/mod.rs b/ldk-server/src/io/mod.rs index ab1df5f6..8f1ebca4 100644 --- a/ldk-server/src/io/mod.rs +++ b/ldk-server/src/io/mod.rs @@ -7,6 +7,5 @@ // You may not use this file except in accordance with one or both of these // licenses. -pub(crate) mod events; pub(crate) mod persist; pub(crate) mod utils; diff --git a/ldk-server/src/main.rs b/ldk-server/src/main.rs index 43720673..9d16d615 100644 --- a/ldk-server/src/main.rs +++ b/ldk-server/src/main.rs @@ -8,7 +8,6 @@ // licenses. mod api; -mod grpc; mod io; mod service; mod util; @@ -23,8 +22,8 @@ use base64::prelude::BASE64_STANDARD; use base64::Engine; use clap::Parser; use hex::DisplayHex; -use hyper::server::conn::http1; -use hyper_util::rt::TokioIo; +use hyper::server::conn::http2; +use hyper_util::rt::{TokioExecutor, TokioIo}; use ldk_node::bitcoin::Network; use ldk_node::config::Config; use ldk_node::entropy::NodeEntropy; @@ -38,11 +37,8 @@ use prost::Message; use tokio::net::TcpListener; use tokio::select; use tokio::signal::unix::SignalKind; +use tokio::sync::broadcast; -use crate::io::events::event_publisher::EventPublisher; -use crate::io::events::get_event_name; -#[cfg(feature = "events-rabbitmq")] -use crate::io::events::rabbitmq::{RabbitMqConfig, RabbitMqEventPublisher}; use crate::io::persist::paginated_kv_store::PaginatedKVStore; use crate::io::persist::sqlite_store::SqliteStore; use crate::io::persist::{ @@ -234,18 +230,7 @@ fn main() { }, }); - #[cfg(not(feature = "events-rabbitmq"))] - let event_publisher: Arc = - Arc::new(crate::io::events::event_publisher::NoopEventPublisher); - - #[cfg(feature = "events-rabbitmq")] - let event_publisher: Arc = { - let rabbitmq_config = RabbitMqConfig { - connection_string: config_file.rabbitmq_connection_string, - exchange_name: config_file.rabbitmq_exchange_name, - }; - Arc::new(RabbitMqEventPublisher::new(rabbitmq_config)) - }; + let (event_sender, _) = broadcast::channel::(1024); info!("Starting up..."); match node.start() { @@ -316,7 +301,7 @@ fn main() { None }; - let rest_svc_listener = TcpListener::bind(config_file.rest_service_addr) + let grpc_listener = TcpListener::bind(config_file.grpc_service_addr) .await .expect("Failed to bind listening port"); @@ -331,7 +316,7 @@ fn main() { } }; let tls_acceptor = tokio_rustls::TlsAcceptor::from(Arc::new(server_config)); - info!("TLS enabled for REST service on {}", config_file.rest_service_addr); + info!("gRPC service listening on {}", config_file.grpc_service_addr); systemd::notify_ready(); @@ -381,13 +366,13 @@ fn main() { ); let payment_id = payment_id.expect("PaymentId expected for ldk-server >=0.1"); - publish_event_and_upsert_payment(&payment_id, + send_event_and_upsert_payment(&payment_id, |payment_ref| event_envelope::Event::PaymentReceived(events::PaymentReceived { payment: Some(payment_ref.clone()), }), &event_node, - Arc::clone(&event_publisher), - Arc::clone(&paginated_store)).await; + &event_sender, + Arc::clone(&paginated_store)); if let Some(metrics) = &metrics { metrics.update_all_balances(&event_node); @@ -396,13 +381,13 @@ fn main() { Event::PaymentSuccessful {payment_id, ..} => { let payment_id = payment_id.expect("PaymentId expected for ldk-server >=0.1"); - publish_event_and_upsert_payment(&payment_id, + send_event_and_upsert_payment(&payment_id, |payment_ref| event_envelope::Event::PaymentSuccessful(events::PaymentSuccessful { payment: Some(payment_ref.clone()), }), &event_node, - Arc::clone(&event_publisher), - Arc::clone(&paginated_store)).await; + &event_sender, + Arc::clone(&paginated_store)); if let Some(metrics) = &metrics { metrics.update_payments_count(true); @@ -412,26 +397,26 @@ fn main() { Event::PaymentFailed {payment_id, ..} => { let payment_id = payment_id.expect("PaymentId expected for ldk-server >=0.1"); - publish_event_and_upsert_payment(&payment_id, + send_event_and_upsert_payment(&payment_id, |payment_ref| event_envelope::Event::PaymentFailed(events::PaymentFailed { payment: Some(payment_ref.clone()), }), &event_node, - Arc::clone(&event_publisher), - Arc::clone(&paginated_store)).await; + &event_sender, + Arc::clone(&paginated_store)); if let Some(metrics) = &metrics { metrics.update_payments_count(false); } }, Event::PaymentClaimable {payment_id, ..} => { - publish_event_and_upsert_payment(&payment_id, + send_event_and_upsert_payment(&payment_id, |payment_ref| event_envelope::Event::PaymentClaimable(events::PaymentClaimable { payment: Some(payment_ref.clone()), }), &event_node, - Arc::clone(&event_publisher), - Arc::clone(&paginated_store)).await; + &event_sender, + Arc::clone(&paginated_store)); }, Event::PaymentForwarded { prev_channel_id, @@ -463,25 +448,18 @@ fn main() { outbound_amount_forwarded_msat ); - // We don't expose this payment-id to the user, it is a temporary measure to generate - // some unique identifiers until we have forwarded-payment-id available in ldk. - // Currently, this is the expected user handling behaviour for forwarded payments. let mut forwarded_payment_id = [0u8; 32]; getrandom::getrandom(&mut forwarded_payment_id).expect("Failed to generate random bytes"); 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 { - Ok(_) => {}, - Err(e) => { - error!("Failed to publish 'PaymentForwarded' event: {}", e); - continue; - } - }; + if let Err(e) = event_sender.send(EventEnvelope { + event: Some(event_envelope::Event::PaymentForwarded(events::PaymentForwarded { + forwarded_payment: Some(forwarded_payment.clone()), + })), + }) { + debug!("No event subscribers connected, skipping event: {e}"); + } match paginated_store.write(FORWARDED_PAYMENTS_PERSISTENCE_PRIMARY_NAMESPACE,FORWARDED_PAYMENTS_PERSISTENCE_SECONDARY_NAMESPACE, &forwarded_payment_id.to_lower_hex_string(), @@ -505,7 +483,7 @@ fn main() { }, } }, - res = rest_svc_listener.accept() => { + res = grpc_listener.accept() => { match res { Ok((stream, _)) => { let node_service = NodeService::new( @@ -514,13 +492,14 @@ fn main() { api_key.clone(), metrics.clone(), metrics_auth_header.clone(), + event_sender.clone(), ); let acceptor = tls_acceptor.clone(); runtime.spawn(async move { match acceptor.accept(stream).await { Ok(tls_stream) => { let io_stream = TokioIo::new(tls_stream); - if let Err(err) = http1::Builder::new().serve_connection(io_stream, node_service).await { + if let Err(err) = http2::Builder::new(TokioExecutor::new()).serve_connection(io_stream, node_service).await { error!("Failed to serve TLS connection: {err}"); } }, @@ -553,23 +532,18 @@ fn main() { info!("Shutdown complete.."); } -async fn publish_event_and_upsert_payment( +fn send_event_and_upsert_payment( payment_id: &PaymentId, payment_to_event: fn(&Payment) -> event_envelope::Event, - event_node: &Node, event_publisher: Arc, + event_node: &Node, event_sender: &broadcast::Sender, paginated_store: Arc, ) { if let Some(payment_details) = event_node.payment(payment_id) { let payment = payment_to_proto(payment_details); let event = payment_to_event(&payment); - let event_name = get_event_name(&event); - match event_publisher.publish(EventEnvelope { event: Some(event) }).await { - Ok(_) => {}, - Err(e) => { - error!("Failed to publish '{event_name}' event, : {e}"); - return; - }, - }; + if let Err(e) = event_sender.send(EventEnvelope { event: Some(event) }) { + debug!("No event subscribers connected, skipping event: {e}"); + } upsert_payment_details(event_node, Arc::clone(&paginated_store), &payment); } else { diff --git a/ldk-server/src/service.rs b/ldk-server/src/service.rs index ceaed478..fe96942c 100644 --- a/ldk-server/src/service.rs +++ b/ldk-server/src/service.rs @@ -11,14 +11,14 @@ use std::future::Future; use std::pin::Pin; use std::sync::Arc; -use http_body_util::{BodyExt, Full, Limited}; -use hyper::body::{Bytes, Incoming}; +use http_body_util::{BodyExt, Limited}; +use hyper::body::Incoming; use hyper::service::Service; -use hyper::{Request, Response, StatusCode}; +use hyper::{Request, Response}; 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_grpc::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, @@ -31,7 +31,16 @@ use ldk_server_protos::endpoints::{ SPLICE_OUT_PATH, SPONTANEOUS_SEND_PATH, UNIFIED_SEND_PATH, UPDATE_CHANNEL_CONFIG_PATH, VERIFY_SIGNATURE_PATH, }; +use ldk_server_grpc::events::EventEnvelope; +use ldk_server_grpc::grpc::{ + decode_grpc_body, encode_grpc_frame, grpc_error_response, grpc_response, parse_grpc_timeout, + validate_grpc_request, GrpcBody, GrpcStatus, GRPC_STATUS_DEADLINE_EXCEEDED, + GRPC_STATUS_FAILED_PRECONDITION, GRPC_STATUS_INTERNAL, GRPC_STATUS_INVALID_ARGUMENT, + GRPC_STATUS_UNAUTHENTICATED, GRPC_STATUS_UNAVAILABLE, GRPC_STATUS_UNIMPLEMENTED, +}; use prost::Message; +use tokio::sync::broadcast; +use tokio::sync::mpsc; 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; @@ -49,8 +58,7 @@ use crate::api::connect_peer::handle_connect_peer; use crate::api::decode_invoice::handle_decode_invoice_request; use crate::api::decode_offer::handle_decode_offer_request; use crate::api::disconnect_peer::handle_disconnect_peer; -use crate::api::error::LdkServerError; -use crate::api::error::LdkServerErrorCode::{AuthError, InvalidRequestError}; +use crate::api::error::{LdkServerError, LdkServerErrorCode}; use crate::api::export_pathfinding_scores::handle_export_pathfinding_scores_request; use crate::api::get_balances::handle_get_balances_request; use crate::api::get_node_info::handle_get_node_info_request; @@ -74,94 +82,75 @@ 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::metrics::Metrics; -use crate::util::proto_adapter::to_error_response; + +/// gRPC path prefix for the LightningNode service. +const GRPC_SERVICE_PREFIX: &str = "/api.LightningNode/"; // Maximum request body size: 10 MB -// This prevents memory exhaustion from large requests const MAX_BODY_SIZE: usize = 10 * 1024 * 1024; #[derive(Clone)] -pub struct NodeService { - node: Arc, - paginated_kv_store: Arc, +pub(crate) struct NodeService { + context: Arc, api_key: String, metrics: Option>, metrics_auth_header: Option, + event_sender: broadcast::Sender, } impl NodeService { pub(crate) fn new( node: Arc, paginated_kv_store: Arc, api_key: String, metrics: Option>, metrics_auth_header: Option, + event_sender: broadcast::Sender, ) -> Self { - Self { node, paginated_kv_store, api_key, metrics, metrics_auth_header } + let context = Arc::new(Context { node, paginated_kv_store }); + Self { context, api_key, metrics, metrics_auth_header, event_sender } } } // Maximum allowed time difference between client timestamp and server time (1 minute) const AUTH_TIMESTAMP_TOLERANCE_SECS: u64 = 60; -#[derive(Debug, Clone)] -pub(crate) struct AuthParams { - timestamp: u64, - hmac_hex: String, -} +/// Validates HMAC authentication from request headers. +/// Uses timestamp-only HMAC (no body) since TLS guarantees integrity. +fn validate_auth(req: &Request, api_key: &str) -> Result<(), LdkServerError> { + let auth_err = |msg: &str| LdkServerError::new(LdkServerErrorCode::AuthError, msg.to_string()); -/// Extracts authentication parameters from request headers. -/// Returns (timestamp, hmac_hex) if valid format, or error. -fn extract_auth_params(req: &Request) -> Result { let auth_header = req .headers() - .get("X-Auth") + .get("x-auth") .and_then(|v| v.to_str().ok()) - .ok_or_else(|| LdkServerError::new(AuthError, "Missing X-Auth header"))?; + .ok_or_else(|| auth_err("Missing x-auth metadata"))?; - // Format: "HMAC :" - let auth_data = auth_header - .strip_prefix("HMAC ") - .ok_or_else(|| LdkServerError::new(AuthError, "Invalid X-Auth header format"))?; + let auth_data = + auth_header.strip_prefix("HMAC ").ok_or_else(|| auth_err("Invalid x-auth format"))?; - let (timestamp_str, hmac_hex) = auth_data - .split_once(':') - .ok_or_else(|| LdkServerError::new(AuthError, "Invalid X-Auth header format"))?; + let (timestamp_str, provided_hmac_hex) = + auth_data.split_once(':').ok_or_else(|| auth_err("Invalid x-auth format"))?; - let timestamp = timestamp_str - .parse::() - .map_err(|_| LdkServerError::new(AuthError, "Invalid timestamp in X-Auth header"))?; + let timestamp = timestamp_str.parse::().map_err(|_| auth_err("Invalid timestamp"))?; - // validate hmac_hex is valid hex - if hmac_hex.len() != 64 || !hmac_hex.chars().all(|c| c.is_ascii_hexdigit()) { - return Err(LdkServerError::new(AuthError, "Invalid HMAC in X-Auth header")); - } - - Ok(AuthParams { timestamp, hmac_hex: hmac_hex.to_string() }) -} - -/// Validates the HMAC authentication after the request body has been read. -fn validate_hmac_auth( - timestamp: u64, provided_hmac_hex: &str, body: &[u8], api_key: &str, -) -> Result<(), LdkServerError> { - // Validate timestamp is within acceptable window let now = std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) - .map_err(|_| LdkServerError::new(AuthError, "System time error"))? + .map_err(|_| auth_err("System time error"))? .as_secs(); - let time_diff = now.abs_diff(timestamp); - if time_diff > AUTH_TIMESTAMP_TOLERANCE_SECS { - return Err(LdkServerError::new(AuthError, "Request timestamp expired")); + if now.abs_diff(timestamp) > AUTH_TIMESTAMP_TOLERANCE_SECS { + return Err(auth_err("Request timestamp expired")); } - // Compute expected HMAC: HMAC-SHA256(api_key, timestamp_bytes || body) + // HMAC-SHA256(api_key, timestamp_bytes) — no body since TLS guarantees integrity let mut hmac_engine: HmacEngine = HmacEngine::new(api_key.as_bytes()); hmac_engine.input(×tamp.to_be_bytes()); - hmac_engine.input(body); let expected_hmac = Hmac::::from_engine(hmac_engine); - // Compare HMACs (constant-time comparison via Hash equality) - let expected_hex = expected_hmac.to_string(); - if expected_hex != provided_hmac_hex { - return Err(LdkServerError::new(AuthError, "Invalid credentials")); + let provided_hmac = provided_hmac_hex + .parse::>() + .map_err(|_| auth_err("Invalid HMAC in x-auth"))?; + + if expected_hmac != provided_hmac { + return Err(auth_err("Invalid credentials")); } Ok(()) @@ -173,24 +162,26 @@ pub(crate) struct Context { } impl Service> for NodeService { - type Response = Response>; + type Response = Response; type Error = hyper::Error; type Future = Pin> + Send>>; fn call(&self, req: Request) -> Self::Future { - // Handle metrics endpoint separately to bypass auth and return plain text + // Handle metrics endpoint (plain HTTP GET, not gRPC) if req.method() == hyper::Method::GET && req.uri().path().len() > 1 && &req.uri().path()[1..] == GET_METRICS_PATH { if let Some(expected_header) = &self.metrics_auth_header { - let auth_header = req.headers().get("Authorization").and_then(|h| h.to_str().ok()); + let auth_header = req.headers().get("authorization").and_then(|h| h.to_str().ok()); if auth_header != Some(expected_header) { return Box::pin(async move { Ok(Response::builder() - .status(StatusCode::UNAUTHORIZED) - .header("WWW-Authenticate", "Basic realm=\"metrics\"") - .body(Full::new(Bytes::from("Unauthorized"))) + .status(401) + .header("www-authenticate", "Basic realm=\"metrics\"") + .body(GrpcBody::Plain { + data: Some(bytes::Bytes::from("Unauthorized")), + }) .unwrap()) }); } @@ -200,477 +191,307 @@ impl Service> for NodeService { let metrics = Arc::clone(metrics); return Box::pin(async move { Ok(Response::builder() - .header("Content-Type", "text/plain") - .body(Full::new(Bytes::from(metrics.gather_metrics()))) + .header("content-type", "text/plain") + .body(GrpcBody::Plain { + data: Some(bytes::Bytes::from(metrics.gather_metrics())), + }) .unwrap()) }); } else { return Box::pin(async move { Ok(Response::builder() - .status(StatusCode::NOT_FOUND) - .body(Full::new(Bytes::from("Not Found"))) + .status(404) + .body(GrpcBody::Plain { data: Some(bytes::Bytes::from("Not Found")) }) .unwrap()) }); } } - // Extract auth params from headers (validation happens after body is read) - let auth_params = match extract_auth_params(&req) { - Ok(params) => params, - Err(e) => { - let (error_response, status_code) = to_error_response(e); - return Box::pin(async move { - Ok(Response::builder() - .status(status_code) - .body(Full::new(Bytes::from(error_response.encode_to_vec()))) - // unwrap safety: body only errors when previous chained calls failed. - .unwrap()) - }); - }, - }; + // Validate gRPC prerequisites + if let Err(status) = validate_grpc_request(&req) { + return Box::pin(async move { Ok(grpc_error_response(status)) }); + } + + // Validate auth before reading the body + if let Err(e) = validate_auth(&req, &self.api_key) { + let status = ldk_error_to_grpc_status(e); + return Box::pin(async move { Ok(grpc_error_response(status)) }); + } - let context = Context { - node: Arc::clone(&self.node), - paginated_kv_store: Arc::clone(&self.paginated_kv_store), + let context = Arc::clone(&self.context); + let path = req.uri().path().to_string(); + let deadline = req + .headers() + .get("grpc-timeout") + .and_then(|v| v.to_str().ok()) + .and_then(parse_grpc_timeout); + + // Strip the service prefix to get the method name + let method = match path.strip_prefix(GRPC_SERVICE_PREFIX) { + Some(m) => m.to_string(), + None => { + let status = + GrpcStatus::new(GRPC_STATUS_UNIMPLEMENTED, format!("Unknown path: {path}")); + return Box::pin(async move { Ok(grpc_error_response(status)) }); + }, }; - let api_key = self.api_key.clone(); - // Exclude '/' from path pattern matching. - match &req.uri().path()[1..] { - GET_NODE_INFO_PATH => Box::pin(handle_request( - context, - req, - auth_params, - api_key, - handle_get_node_info_request, - )), - GET_BALANCES_PATH => Box::pin(handle_request( - context, - req, - auth_params, - api_key, - handle_get_balances_request, - )), - ONCHAIN_RECEIVE_PATH => Box::pin(handle_request( - context, - req, - auth_params, - api_key, - handle_onchain_receive_request, - )), - ONCHAIN_SEND_PATH => Box::pin(handle_request( - context, - req, - auth_params, - api_key, - handle_onchain_send_request, - )), - BOLT11_RECEIVE_PATH => Box::pin(handle_request( - context, - req, - auth_params, - api_key, - handle_bolt11_receive_request, - )), - BOLT11_RECEIVE_FOR_HASH_PATH => Box::pin(handle_request( - context, - req, - auth_params, - api_key, - handle_bolt11_receive_for_hash_request, - )), - BOLT11_CLAIM_FOR_HASH_PATH => Box::pin(handle_request( - context, - req, - auth_params, - api_key, - handle_bolt11_claim_for_hash_request, - )), - BOLT11_FAIL_FOR_HASH_PATH => Box::pin(handle_request( - context, - req, - auth_params, - api_key, - handle_bolt11_fail_for_hash_request, - )), - BOLT11_RECEIVE_VIA_JIT_CHANNEL_PATH => Box::pin(handle_request( + let is_streaming = false; + let future: Self::Future = match method.as_str() { + GET_NODE_INFO_PATH => { + Box::pin(handle_grpc_unary(context, req, handle_get_node_info_request)) + }, + GET_BALANCES_PATH => { + Box::pin(handle_grpc_unary(context, req, handle_get_balances_request)) + }, + ONCHAIN_RECEIVE_PATH => { + Box::pin(handle_grpc_unary(context, req, handle_onchain_receive_request)) + }, + ONCHAIN_SEND_PATH => { + Box::pin(handle_grpc_unary(context, req, handle_onchain_send_request)) + }, + BOLT11_RECEIVE_PATH => { + Box::pin(handle_grpc_unary(context, req, handle_bolt11_receive_request)) + }, + BOLT11_RECEIVE_FOR_HASH_PATH => { + Box::pin(handle_grpc_unary(context, req, handle_bolt11_receive_for_hash_request)) + }, + BOLT11_CLAIM_FOR_HASH_PATH => { + Box::pin(handle_grpc_unary(context, req, handle_bolt11_claim_for_hash_request)) + }, + BOLT11_FAIL_FOR_HASH_PATH => { + Box::pin(handle_grpc_unary(context, req, handle_bolt11_fail_for_hash_request)) + }, + BOLT11_RECEIVE_VIA_JIT_CHANNEL_PATH => Box::pin(handle_grpc_unary( context, req, - auth_params, - api_key, handle_bolt11_receive_via_jit_channel_request, )), - BOLT11_RECEIVE_VARIABLE_AMOUNT_VIA_JIT_CHANNEL_PATH => Box::pin(handle_request( + BOLT11_RECEIVE_VARIABLE_AMOUNT_VIA_JIT_CHANNEL_PATH => Box::pin(handle_grpc_unary( context, req, - auth_params, - api_key, handle_bolt11_receive_variable_amount_via_jit_channel_request, )), - BOLT11_SEND_PATH => Box::pin(handle_request( - context, - req, - auth_params, - api_key, - handle_bolt11_send_request, - )), - BOLT12_RECEIVE_PATH => Box::pin(handle_request( - context, - req, - auth_params, - api_key, - handle_bolt12_receive_request, - )), - BOLT12_SEND_PATH => Box::pin(handle_request( - context, - req, - auth_params, - api_key, - handle_bolt12_send_request, - )), - OPEN_CHANNEL_PATH => { - Box::pin(handle_request(context, req, auth_params, api_key, handle_open_channel)) + BOLT11_SEND_PATH => { + Box::pin(handle_grpc_unary(context, req, handle_bolt11_send_request)) }, - SPLICE_IN_PATH => Box::pin(handle_request( - context, - req, - auth_params, - api_key, - handle_splice_in_request, - )), - SPLICE_OUT_PATH => Box::pin(handle_request( - context, - req, - auth_params, - api_key, - handle_splice_out_request, - )), - CLOSE_CHANNEL_PATH => Box::pin(handle_request( - context, - req, - auth_params, - api_key, - handle_close_channel_request, - )), - FORCE_CLOSE_CHANNEL_PATH => Box::pin(handle_request( - context, - req, - auth_params, - api_key, - handle_force_close_channel_request, - )), - LIST_CHANNELS_PATH => Box::pin(handle_request( - context, - req, - auth_params, - api_key, - handle_list_channels_request, - )), - UPDATE_CHANNEL_CONFIG_PATH => Box::pin(handle_request( - context, - req, - auth_params, - api_key, - handle_update_channel_config_request, - )), - GET_PAYMENT_DETAILS_PATH => Box::pin(handle_request( - context, - req, - auth_params, - api_key, - handle_get_payment_details_request, - )), - LIST_PAYMENTS_PATH => Box::pin(handle_request( - context, - req, - auth_params, - api_key, - handle_list_payments_request, - )), - LIST_FORWARDED_PAYMENTS_PATH => Box::pin(handle_request( - context, - req, - auth_params, - api_key, - handle_list_forwarded_payments_request, - )), - CONNECT_PEER_PATH => { - Box::pin(handle_request(context, req, auth_params, api_key, handle_connect_peer)) + BOLT12_RECEIVE_PATH => { + Box::pin(handle_grpc_unary(context, req, handle_bolt12_receive_request)) }, + BOLT12_SEND_PATH => { + Box::pin(handle_grpc_unary(context, req, handle_bolt12_send_request)) + }, + OPEN_CHANNEL_PATH => Box::pin(handle_grpc_unary(context, req, handle_open_channel)), + SPLICE_IN_PATH => Box::pin(handle_grpc_unary(context, req, handle_splice_in_request)), + SPLICE_OUT_PATH => Box::pin(handle_grpc_unary(context, req, handle_splice_out_request)), + CLOSE_CHANNEL_PATH => { + Box::pin(handle_grpc_unary(context, req, handle_close_channel_request)) + }, + FORCE_CLOSE_CHANNEL_PATH => { + Box::pin(handle_grpc_unary(context, req, handle_force_close_channel_request)) + }, + LIST_CHANNELS_PATH => { + Box::pin(handle_grpc_unary(context, req, handle_list_channels_request)) + }, + UPDATE_CHANNEL_CONFIG_PATH => { + Box::pin(handle_grpc_unary(context, req, handle_update_channel_config_request)) + }, + GET_PAYMENT_DETAILS_PATH => { + Box::pin(handle_grpc_unary(context, req, handle_get_payment_details_request)) + }, + LIST_PAYMENTS_PATH => { + Box::pin(handle_grpc_unary(context, req, handle_list_payments_request)) + }, + LIST_FORWARDED_PAYMENTS_PATH => { + Box::pin(handle_grpc_unary(context, req, handle_list_forwarded_payments_request)) + }, + CONNECT_PEER_PATH => Box::pin(handle_grpc_unary(context, req, handle_connect_peer)), DISCONNECT_PEER_PATH => { - Box::pin(handle_request(context, req, auth_params, api_key, handle_disconnect_peer)) + Box::pin(handle_grpc_unary(context, req, handle_disconnect_peer)) }, - LIST_PEERS_PATH => Box::pin(handle_request( - context, - req, - auth_params, - api_key, - handle_list_peers_request, - )), - SPONTANEOUS_SEND_PATH => Box::pin(handle_request( - context, - req, - auth_params, - api_key, - handle_spontaneous_send_request, - )), - UNIFIED_SEND_PATH => Box::pin(handle_request( - context, - req, - auth_params, - api_key, - handle_unified_send_request, - )), - SIGN_MESSAGE_PATH => Box::pin(handle_request( - context, - req, - auth_params, - api_key, - handle_sign_message_request, - )), - VERIFY_SIGNATURE_PATH => Box::pin(handle_request( - context, - req, - auth_params, - api_key, - handle_verify_signature_request, - )), - EXPORT_PATHFINDING_SCORES_PATH => Box::pin(handle_request( - context, - req, - auth_params, - api_key, - handle_export_pathfinding_scores_request, - )), - GRAPH_LIST_CHANNELS_PATH => Box::pin(handle_request( - context, - req, - auth_params, - api_key, - handle_graph_list_channels_request, - )), - GRAPH_GET_CHANNEL_PATH => Box::pin(handle_request( - context, - req, - auth_params, - api_key, - handle_graph_get_channel_request, - )), - GRAPH_LIST_NODES_PATH => Box::pin(handle_request( - context, - req, - auth_params, - api_key, - handle_graph_list_nodes_request, - )), - GRAPH_GET_NODE_PATH => Box::pin(handle_request( - context, - req, - auth_params, - api_key, - handle_graph_get_node_request, - )), - DECODE_INVOICE_PATH => Box::pin(handle_request( - context, - req, - auth_params, - api_key, - handle_decode_invoice_request, - )), - DECODE_OFFER_PATH => Box::pin(handle_request( - context, - req, - auth_params, - api_key, - handle_decode_offer_request, - )), - path => { - let error = format!("Unknown request: {}", path).into_bytes(); - Box::pin(async { - Ok(Response::builder() - .status(StatusCode::BAD_REQUEST) - .body(Full::new(Bytes::from(error))) - // unwrap safety: body only errors when previous chained calls failed. - .unwrap()) - }) + LIST_PEERS_PATH => Box::pin(handle_grpc_unary(context, req, handle_list_peers_request)), + SPONTANEOUS_SEND_PATH => { + Box::pin(handle_grpc_unary(context, req, handle_spontaneous_send_request)) + }, + UNIFIED_SEND_PATH => { + Box::pin(handle_grpc_unary(context, req, handle_unified_send_request)) + }, + SIGN_MESSAGE_PATH => { + Box::pin(handle_grpc_unary(context, req, handle_sign_message_request)) + }, + VERIFY_SIGNATURE_PATH => { + Box::pin(handle_grpc_unary(context, req, handle_verify_signature_request)) + }, + EXPORT_PATHFINDING_SCORES_PATH => { + Box::pin(handle_grpc_unary(context, req, handle_export_pathfinding_scores_request)) + }, + GRAPH_LIST_CHANNELS_PATH => { + Box::pin(handle_grpc_unary(context, req, handle_graph_list_channels_request)) + }, + GRAPH_GET_CHANNEL_PATH => { + Box::pin(handle_grpc_unary(context, req, handle_graph_get_channel_request)) + }, + GRAPH_LIST_NODES_PATH => { + Box::pin(handle_grpc_unary(context, req, handle_graph_list_nodes_request)) }, + GRAPH_GET_NODE_PATH => { + Box::pin(handle_grpc_unary(context, req, handle_graph_get_node_request)) + }, + DECODE_INVOICE_PATH => { + Box::pin(handle_grpc_unary(context, req, handle_decode_invoice_request)) + }, + DECODE_OFFER_PATH => { + Box::pin(handle_grpc_unary(context, req, handle_decode_offer_request)) + }, + _ => { + let status = + GrpcStatus::new(GRPC_STATUS_UNIMPLEMENTED, format!("Unknown method: {method}")); + Box::pin(async move { Ok(grpc_error_response(status)) }) + }, + }; + + // Apply grpc-timeout deadline to unary RPCs (not streaming). + match deadline { + Some(d) if !is_streaming => Box::pin(async move { + tokio::time::timeout(d, future).await.unwrap_or_else(|_| { + Ok(grpc_error_response(GrpcStatus::new( + GRPC_STATUS_DEADLINE_EXCEEDED, + "Deadline exceeded", + ))) + }) + }), + _ => future, } } } -async fn handle_request< +async fn handle_grpc_unary< T: Message + Default, R: Message, Fut: Future> + Send, F: Fn(Arc, T) -> Fut + Send, >( - context: Context, request: Request, auth_params: AuthParams, api_key: String, - handler: F, -) -> Result<>>::Response, hyper::Error> { - // Limit the size of the request body to prevent abuse + context: Arc, request: Request, handler: F, +) -> Result, hyper::Error> { + // Read and size-limit the request body let limited_body = Limited::new(request.into_body(), MAX_BODY_SIZE); let bytes = match limited_body.collect().await { Ok(collected) => collected.to_bytes(), Err(_) => { - let (error_response, status_code) = to_error_response(LdkServerError::new( - 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(grpc_error_response(GrpcStatus::new( + GRPC_STATUS_INVALID_ARGUMENT, + "Request body too large or failed to read", + ))); }, }; - // Validate HMAC authentication with the request body - if let Err(e) = - validate_hmac_auth(auth_params.timestamp, &auth_params.hmac_hex, &bytes, &api_key) - { - let (error_response, status_code) = to_error_response(e); - return Ok(Response::builder() - .status(status_code) - .body(Full::new(Bytes::from(error_response.encode_to_vec()))) - // unwrap safety: body only errors when previous chained calls failed. - .unwrap()); - } + // Decode gRPC framing then protobuf + let req_msg = decode_grpc_body(&bytes) + .and_then(|b| { + T::decode(b) + .map_err(|_| GrpcStatus::new(GRPC_STATUS_INVALID_ARGUMENT, "Malformed request")) + }) + .map_err(grpc_error_response); + let req_msg = match req_msg { + Ok(m) => m, + Err(resp) => return Ok(resp), + }; - match T::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()), - 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()) - }, - }, - 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()) + // Yield before handler execution to allow cancellation if the client + // has already disconnected (e.g., RST_STREAM). Hyper drops the handler + // future at yield points when a stream is reset. + tokio::task::yield_now().await; + + // Call handler + match handler(context, req_msg).await { + Ok(response) => { + let encoded = encode_grpc_frame(&response.encode_to_vec()); + Ok(grpc_response(GrpcBody::Unary { data: Some(encoded), trailers_sent: false })) }, + Err(e) => Ok(grpc_error_response(ldk_error_to_grpc_status(e))), } } +/// Map an `LdkServerError` to a `GrpcStatus`. +pub(crate) fn ldk_error_to_grpc_status(e: LdkServerError) -> GrpcStatus { + let code = match e.error_code { + LdkServerErrorCode::InvalidRequestError => GRPC_STATUS_INVALID_ARGUMENT, + LdkServerErrorCode::AuthError => GRPC_STATUS_UNAUTHENTICATED, + LdkServerErrorCode::LightningError => GRPC_STATUS_FAILED_PRECONDITION, + LdkServerErrorCode::InternalServerError => GRPC_STATUS_INTERNAL, + }; + GrpcStatus { code, message: e.message } +} + #[cfg(test)] mod tests { use super::*; - fn compute_hmac(api_key: &str, timestamp: u64, body: &[u8]) -> String { + fn compute_hmac(api_key: &str, timestamp: u64) -> String { let mut hmac_engine: HmacEngine = HmacEngine::new(api_key.as_bytes()); hmac_engine.input(×tamp.to_be_bytes()); - hmac_engine.input(body); Hmac::::from_engine(hmac_engine).to_string() } fn create_test_request(auth_header: Option) -> Request<()> { - let mut builder = Request::builder(); + let mut builder = + Request::builder().method("POST").header("content-type", "application/grpc+proto"); if let Some(header) = auth_header { - builder = builder.header("X-Auth", header); + builder = builder.header("x-auth", header); } builder.body(()).unwrap() } #[test] - fn test_extract_auth_params_success() { + fn test_validate_auth_success() { + let api_key = "test_api_key"; let timestamp = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs(); - let hmac = "8f5a33c2c68fb253899a588308fd13dcaf162d2788966a1fb6cc3aa2e0c51a93"; + let hmac = compute_hmac(api_key, timestamp); let auth_header = format!("HMAC {timestamp}:{hmac}"); - let req = create_test_request(Some(auth_header)); - let result = extract_auth_params(&req); - assert!(result.is_ok()); - let AuthParams { timestamp: ts, hmac_hex } = result.unwrap(); - assert_eq!(ts, timestamp); - assert_eq!(hmac_hex, hmac); + assert!(validate_auth(&req, api_key).is_ok()); } #[test] - fn test_extract_auth_params_missing_header() { + fn test_validate_auth_missing_header() { let req = create_test_request(None); - - let result = extract_auth_params(&req); + let result = validate_auth(&req, "test_key"); assert!(result.is_err()); - assert_eq!(result.unwrap_err().error_code, AuthError); + assert_eq!(result.unwrap_err().error_code, LdkServerErrorCode::AuthError); } #[test] - fn test_extract_auth_params_invalid_format() { - // Missing "HMAC " prefix + fn test_validate_auth_invalid_format() { let req = create_test_request(Some("12345:deadbeef".to_string())); - - let result = extract_auth_params(&req); + let result = validate_auth(&req, "test_key"); assert!(result.is_err()); - assert_eq!(result.unwrap_err().error_code, AuthError); + assert_eq!(result.unwrap_err().error_code, LdkServerErrorCode::AuthError); } #[test] - fn test_validate_hmac_auth_success() { - let api_key = "test_api_key".to_string(); - let body = b"test request body"; + fn test_validate_auth_wrong_key() { let timestamp = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs(); - let hmac = compute_hmac(&api_key, timestamp, body); + let hmac = compute_hmac("wrong_key", timestamp); + let req = create_test_request(Some(format!("HMAC {timestamp}:{hmac}"))); - let result = validate_hmac_auth(timestamp, &hmac, body, &api_key); - assert!(result.is_ok()); - } - - #[test] - fn test_validate_hmac_auth_wrong_key() { - let api_key = "test_api_key".to_string(); - let body = b"test request body"; - let timestamp = - std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs(); - // Compute HMAC with wrong key - let hmac = compute_hmac("wrong_key", timestamp, body); - - let result = validate_hmac_auth(timestamp, &hmac, body, &api_key); + let result = validate_auth(&req, "test_api_key"); assert!(result.is_err()); - assert_eq!(result.unwrap_err().error_code, AuthError); + assert_eq!(result.unwrap_err().error_code, LdkServerErrorCode::AuthError); } #[test] - fn test_validate_hmac_auth_expired_timestamp() { - let api_key = "test_api_key".to_string(); - let body = b"test request body"; - // Use a timestamp from 10 minutes ago + fn test_validate_auth_expired_timestamp() { let timestamp = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs() - 600; - let hmac = compute_hmac(&api_key, timestamp, body); - - let result = validate_hmac_auth(timestamp, &hmac, body, &api_key); - assert!(result.is_err()); - assert_eq!(result.unwrap_err().error_code, AuthError); - } - - #[test] - fn test_validate_hmac_auth_tampered_body() { - let api_key = "test_api_key".to_string(); - let original_body = b"test request body"; - let tampered_body = b"tampered body"; - let timestamp = - std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs(); - // Compute HMAC with original body - let hmac = compute_hmac(&api_key, timestamp, original_body); + let hmac = compute_hmac("test_api_key", timestamp); + let req = create_test_request(Some(format!("HMAC {timestamp}:{hmac}"))); - // Try to validate with tampered body - let result = validate_hmac_auth(timestamp, &hmac, tampered_body, &api_key); + let result = validate_auth(&req, "test_api_key"); assert!(result.is_err()); - assert_eq!(result.unwrap_err().error_code, AuthError); + assert_eq!(result.unwrap_err().error_code, LdkServerErrorCode::AuthError); } } diff --git a/ldk-server/src/util/config.rs b/ldk-server/src/util/config.rs index caa16fdf..515df5e5 100644 --- a/ldk-server/src/util/config.rs +++ b/ldk-server/src/util/config.rs @@ -21,7 +21,7 @@ use ldk_node::liquidity::LSPS2ServiceConfig; use log::LevelFilter; use serde::{Deserialize, Serialize}; -const DEFAULT_REST_SERVICE_ADDRESS: &str = "127.0.0.1:3536"; +const DEFAULT_GRPC_SERVICE_ADDRESS: &str = "127.0.0.1:3536"; #[cfg(not(test))] const DEFAULT_CONFIG_FILE: &str = "config.toml"; @@ -46,14 +46,10 @@ pub struct Config { pub alias: Option, pub network: Network, pub tls_config: Option, - pub rest_service_addr: SocketAddr, + pub grpc_service_addr: SocketAddr, pub storage_dir_path: Option, pub chain_source: ChainSource, pub rgs_server_url: Option, - #[cfg_attr(not(feature = "events-rabbitmq"), allow(dead_code))] - pub rabbitmq_connection_string: String, - #[cfg_attr(not(feature = "events-rabbitmq"), allow(dead_code))] - pub rabbitmq_exchange_name: String, pub lsps2_client_config: Option, #[cfg_attr(not(feature = "experimental-lsps2-support"), allow(dead_code))] pub lsps2_service_config: Option, @@ -101,7 +97,7 @@ struct ConfigBuilder { alias: Option, network: Option, tls_config: Option, - rest_service_address: Option, + grpc_service_address: Option, storage_dir_path: Option, electrum_url: Option, esplora_url: Option, @@ -109,8 +105,6 @@ struct ConfigBuilder { bitcoind_rpc_user: Option, bitcoind_rpc_password: Option, rgs_server_url: Option, - rabbitmq_connection_string: Option, - rabbitmq_exchange_name: Option, lsps2: Option, log_level: Option, log_file_path: Option, @@ -130,8 +124,8 @@ impl ConfigBuilder { node.listening_addresses.or(self.listening_addresses.clone()); self.announcement_addresses = node.announcement_addresses.or(self.announcement_addresses.clone()); - self.rest_service_address = - node.rest_service_address.or(self.rest_service_address.clone()); + self.grpc_service_address = + node.grpc_service_address.or(self.grpc_service_address.clone()); self.alias = node.alias.or(self.alias.clone()); self.pathfinding_scores_source_url = node.pathfinding_scores_source_url.or(self.pathfinding_scores_source_url.clone()); @@ -163,11 +157,6 @@ impl ConfigBuilder { self.log_file_path = log.file.or(self.log_file_path.clone()); } - if let Some(rabbitmq) = toml.rabbitmq { - self.rabbitmq_connection_string = Some(rabbitmq.connection_string); - self.rabbitmq_exchange_name = Some(rabbitmq.exchange_name); - } - if let Some(liquidity) = toml.liquidity { self.lsps2 = Some(liquidity); } @@ -206,8 +195,8 @@ impl ConfigBuilder { self.announcement_addresses = Some(node_announcement_addresses.clone()); } - if let Some(node_rest_service_address) = &args.node_rest_service_address { - self.rest_service_address = Some(node_rest_service_address.clone()); + if let Some(node_grpc_service_address) = &args.node_grpc_service_address { + self.grpc_service_address = Some(node_grpc_service_address.clone()); } if let Some(node_alias) = &args.node_alias { @@ -258,9 +247,9 @@ impl ConfigBuilder { fn build(self) -> io::Result { let network = self.network.ok_or_else(|| missing_field_err("network"))?; - let rest_service_addr = self - .rest_service_address - .unwrap_or_else(|| DEFAULT_REST_SERVICE_ADDRESS.to_string()) + let grpc_service_addr = self + .grpc_service_address + .unwrap_or_else(|| DEFAULT_GRPC_SERVICE_ADDRESS.to_string()) .parse::() .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?; @@ -362,30 +351,6 @@ impl ConfigBuilder { .transpose()? .unwrap_or(LevelFilter::Debug); - #[cfg(feature = "events-rabbitmq")] - let (rabbitmq_connection_string, rabbitmq_exchange_name) = { - let connection_string = self.rabbitmq_connection_string.ok_or_else(|| io::Error::new( - io::ErrorKind::InvalidInput, - "Both `rabbitmq.connection_string` and `rabbitmq.exchange_name` must be configured if enabling `events-rabbitmq` feature." - ))?; - let exchange_name = self.rabbitmq_exchange_name.ok_or_else(|| io::Error::new( - io::ErrorKind::InvalidInput, - "Both `rabbitmq.connection_string` and `rabbitmq.exchange_name` must be configured if enabling `events-rabbitmq` feature." - ))?; - - if connection_string.is_empty() || exchange_name.is_empty() { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - "Both `rabbitmq.connection_string` and `rabbitmq.exchange_name` must be configured if enabling `events-rabbitmq` feature." - )); - } - - (connection_string, exchange_name) - }; - - #[cfg(not(feature = "events-rabbitmq"))] - let (rabbitmq_connection_string, rabbitmq_exchange_name) = (String::new(), String::new()); - let lsps2_client_config = self .lsps2 .as_ref() @@ -443,12 +408,10 @@ impl ConfigBuilder { announcement_addrs, alias, tls_config: self.tls_config, - rest_service_addr, + grpc_service_addr, storage_dir_path: self.storage_dir_path, chain_source, rgs_server_url: self.rgs_server_url, - rabbitmq_connection_string, - rabbitmq_exchange_name, lsps2_client_config, lsps2_service_config, log_level, @@ -471,7 +434,6 @@ pub struct TomlConfig { bitcoind: Option, electrum: Option, esplora: Option, - rabbitmq: Option, liquidity: Option, log: Option, tls: Option, @@ -484,7 +446,7 @@ struct NodeConfig { network: Option, listening_addresses: Option>, announcement_addresses: Option>, - rest_service_address: Option, + grpc_service_address: Option, alias: Option, pathfinding_scores_source_url: Option, rgs_server_url: Option, @@ -523,12 +485,6 @@ struct LogConfig { file: Option, } -#[derive(Deserialize, Serialize)] -struct RabbitmqConfig { - connection_string: String, - exchange_name: String, -} - #[derive(Deserialize, Serialize)] struct TomlTlsConfig { cert_path: Option, @@ -661,10 +617,10 @@ pub struct ArgsConfig { #[arg( long, - env = "LDK_SERVER_NODE_REST_SERVICE_ADDRESS", - help = "The rest service address for the LDK Server API." + env = "LDK_SERVER_NODE_GRPC_SERVICE_ADDRESS", + help = "The gRPC service address for the LDK Server API." )] - node_rest_service_address: Option, + node_grpc_service_address: Option, #[arg( long, @@ -821,7 +777,7 @@ mod tests { network = "regtest" listening_addresses = ["localhost:3001"] announcement_addresses = ["54.3.7.81:3001"] - rest_service_address = "127.0.0.1:3002" + grpc_service_address = "127.0.0.1:3002" alias = "LDK Server" rgs_server_url = "https://rapidsync.lightningdevkit.org/snapshot/v2/" @@ -842,10 +798,6 @@ mod tests { rpc_user = "bitcoind-testuser" rpc_password = "bitcoind-testpassword" - [rabbitmq] - connection_string = "rabbitmq_connection_string" - exchange_name = "rabbitmq_exchange_name" - [liquidity.lsps2_client] node_pubkey = "0217890e3aad8d35bc054f43acc00084b25229ecff0ab68debd82883ad65ee8266" address = "127.0.0.1:39735" @@ -872,7 +824,7 @@ mod tests { node_network: Some(Network::Regtest), node_listening_addresses: Some(vec!["localhost:3008".to_string()]), node_announcement_addresses: Some(vec!["54.3.7.81:3001".to_string()]), - node_rest_service_address: Some(String::from("127.0.0.1:3009")), + node_grpc_service_address: Some(String::from("127.0.0.1:3009")), bitcoind_rpc_address: Some(String::from("127.0.1.9:18443")), bitcoind_rpc_user: Some(String::from("bitcoind-testuser_cli")), bitcoind_rpc_password: Some(String::from("bitcoind-testpassword_cli")), @@ -893,7 +845,7 @@ mod tests { node_network: None, node_listening_addresses: None, node_announcement_addresses: None, - node_rest_service_address: None, + node_grpc_service_address: None, node_alias: None, bitcoind_rpc_address: None, bitcoind_rpc_user: None, @@ -930,19 +882,12 @@ mod tests { let alias = "LDK Server"; - #[cfg(feature = "events-rabbitmq")] - let (expected_rabbit_conn, expected_rabbit_exchange) = - ("rabbitmq_connection_string".to_string(), "rabbitmq_exchange_name".to_string()); - - #[cfg(not(feature = "events-rabbitmq"))] - let (expected_rabbit_conn, expected_rabbit_exchange) = (String::new(), String::new()); - let expected = Config { listening_addrs: Some(vec![SocketAddress::from_str("localhost:3001").unwrap()]), announcement_addrs: Some(vec![SocketAddress::from_str("54.3.7.81:3001").unwrap()]), alias: Some(parse_alias(alias).unwrap()), network: Network::Regtest, - rest_service_addr: SocketAddr::from_str("127.0.0.1:3002").unwrap(), + grpc_service_addr: SocketAddr::from_str("127.0.0.1:3002").unwrap(), storage_dir_path: Some("/tmp".to_string()), tls_config: Some(TlsConfig { cert_path: Some("/path/to/tls.crt".to_string()), @@ -956,8 +901,6 @@ mod tests { rpc_password: "bitcoind-testpassword".to_string(), }, rgs_server_url: Some("https://rapidsync.lightningdevkit.org/snapshot/v2/".to_string()), - rabbitmq_connection_string: expected_rabbit_conn, - rabbitmq_exchange_name: expected_rabbit_exchange, lsps2_client_config: Some(LSPSClientConfig { node_id: PublicKey::from_str( "0217890e3aad8d35bc054f43acc00084b25229ecff0ab68debd82883ad65ee8266", @@ -994,12 +937,10 @@ mod tests { assert_eq!(config.announcement_addrs, expected.announcement_addrs); assert_eq!(config.alias, expected.alias); assert_eq!(config.network, expected.network); - assert_eq!(config.rest_service_addr, expected.rest_service_addr); + assert_eq!(config.grpc_service_addr, expected.grpc_service_addr); assert_eq!(config.storage_dir_path, expected.storage_dir_path); assert_eq!(config.chain_source, expected.chain_source); assert_eq!(config.rgs_server_url, expected.rgs_server_url); - assert_eq!(config.rabbitmq_connection_string, expected.rabbitmq_connection_string); - assert_eq!(config.rabbitmq_exchange_name, expected.rabbitmq_exchange_name); assert_eq!(config.lsps2_client_config, expected.lsps2_client_config); #[cfg(feature = "experimental-lsps2-support")] assert_eq!(config.lsps2_service_config.is_some(), expected.lsps2_service_config.is_some()); @@ -1016,7 +957,7 @@ mod tests { network = "regtest" listening_addresses = ["localhost:3001"] announcement_addresses = ["54.3.7.81:3001"] - rest_service_address = "127.0.0.1:3002" + grpc_service_address = "127.0.0.1:3002" alias = "LDK Server" pathfinding_scores_source_url = "https://example.com/" @@ -1035,10 +976,6 @@ mod tests { [electrum] server_url = "ssl://electrum.blockstream.info:50002" - [rabbitmq] - connection_string = "rabbitmq_connection_string" - exchange_name = "rabbitmq_exchange_name" - [liquidity.lsps2_client] node_pubkey = "0217890e3aad8d35bc054f43acc00084b25229ecff0ab68debd82883ad65ee8266" address = "127.0.0.1:39735" @@ -1071,7 +1008,7 @@ mod tests { network = "regtest" listening_addresses = ["localhost:3001"] announcement_addresses = ["54.3.7.81:3001"] - rest_service_address = "127.0.0.1:3002" + grpc_service_address = "127.0.0.1:3002" alias = "LDK Server" pathfinding_scores_source_url = "https://example.com/" @@ -1092,10 +1029,6 @@ mod tests { rpc_user = "bitcoind-testuser" rpc_password = "bitcoind-testpassword" - [rabbitmq] - connection_string = "rabbitmq_connection_string" - exchange_name = "rabbitmq_exchange_name" - [liquidity.lsps2_client] node_pubkey = "0217890e3aad8d35bc054f43acc00084b25229ecff0ab68debd82883ad65ee8266" address = "127.0.0.1:39735" @@ -1132,7 +1065,7 @@ mod tests { network = "regtest" listening_addresses = ["localhost:3001"] announcement_addresses = ["54.3.7.81:3001"] - rest_service_address = "127.0.0.1:3002" + grpc_service_address = "127.0.0.1:3002" alias = "LDK Server" pathfinding_scores_source_url = "https://example.com/" @@ -1156,10 +1089,6 @@ mod tests { [esplora] server_url = "https://mempool.space/api" - [rabbitmq] - connection_string = "rabbitmq_connection_string" - exchange_name = "rabbitmq_exchange_name" - [liquidity.lsps2_client] node_pubkey = "0217890e3aad8d35bc054f43acc00084b25229ecff0ab68debd82883ad65ee8266" address = "127.0.0.1:39735" @@ -1194,17 +1123,13 @@ mod tests { let toml_config = r#" [node] network = "regtest" - rest_service_address = "127.0.0.1:3002" + grpc_service_address = "127.0.0.1:3002" [bitcoind] rpc_address = "127.0.0.1:8332" rpc_user = "bitcoind-testuser" rpc_password = "bitcoind-testpassword" - [rabbitmq] - connection_string = "rabbitmq_connection_string" - exchange_name = "rabbitmq_exchange_name" - [liquidity.lsps2_service] advertise_service = false channel_opening_fee_ppm = 1000 # 0.1% fee @@ -1251,14 +1176,6 @@ mod tests { ); } - #[cfg(feature = "events-rabbitmq")] - { - validate_missing!( - "[rabbitmq]", - "Both `rabbitmq.connection_string` and `rabbitmq.exchange_name` must be configured if enabling `events-rabbitmq` feature." - ); - } - validate_missing!("rpc_password", missing_field_msg("bitcoind_rpc_password")); validate_missing!("rpc_user", missing_field_msg("bitcoind_rpc_user")); validate_missing!("rpc_address", missing_field_msg("bitcoind_rpc_address")); @@ -1275,7 +1192,6 @@ mod tests { #[test] #[cfg(not(feature = "experimental-lsps2-support"))] - #[cfg(not(feature = "events-rabbitmq"))] fn test_config_from_args_config() { let args_config = default_args_config(); let config = load_config(&args_config).unwrap(); @@ -1292,8 +1208,8 @@ mod tests { ) .unwrap()]), network: Network::Regtest, - rest_service_addr: SocketAddr::from_str( - args_config.node_rest_service_address.as_deref().unwrap(), + grpc_service_addr: SocketAddr::from_str( + args_config.node_grpc_service_address.as_deref().unwrap(), ) .unwrap(), alias: Some(parse_alias(args_config.node_alias.as_deref().unwrap()).unwrap()), @@ -1306,8 +1222,6 @@ mod tests { rpc_password: args_config.bitcoind_rpc_password.unwrap(), }, rgs_server_url: None, - rabbitmq_connection_string: String::new(), - rabbitmq_exchange_name: String::new(), lsps2_client_config: None, lsps2_service_config: None, log_level: LevelFilter::Trace, @@ -1323,12 +1237,10 @@ mod tests { assert_eq!(config.listening_addrs, expected.listening_addrs); assert_eq!(config.announcement_addrs, expected.announcement_addrs); assert_eq!(config.network, expected.network); - assert_eq!(config.rest_service_addr, expected.rest_service_addr); + assert_eq!(config.grpc_service_addr, expected.grpc_service_addr); assert_eq!(config.storage_dir_path, expected.storage_dir_path); assert_eq!(config.chain_source, expected.chain_source); assert_eq!(config.rgs_server_url, expected.rgs_server_url); - assert_eq!(config.rabbitmq_connection_string, expected.rabbitmq_connection_string); - assert_eq!(config.rabbitmq_exchange_name, expected.rabbitmq_exchange_name); assert!(config.lsps2_service_config.is_none()); assert_eq!(config.pathfinding_scores_source_url, expected.pathfinding_scores_source_url); assert_eq!(config.metrics_enabled, expected.metrics_enabled); @@ -1337,7 +1249,6 @@ mod tests { #[test] #[cfg(not(feature = "experimental-lsps2-support"))] - #[cfg(not(feature = "events-rabbitmq"))] fn test_config_missing_fields_in_args_config() { macro_rules! validate_missing { ($field:ident, $err_msg:expr) => { @@ -1367,13 +1278,6 @@ mod tests { args_config.config_file = Some(storage_path.join(config_file_name).to_string_lossy().to_string()); - #[cfg(feature = "events-rabbitmq")] - let (expected_rabbit_conn, expected_rabbit_exchange) = - ("rabbitmq_connection_string".to_string(), "rabbitmq_exchange_name".to_string()); - - #[cfg(not(feature = "events-rabbitmq"))] - let (expected_rabbit_conn, expected_rabbit_exchange) = (String::new(), String::new()); - let (host, port) = parse_host_port(args_config.bitcoind_rpc_address.clone().unwrap().as_str()).unwrap(); @@ -1388,8 +1292,8 @@ mod tests { ) .unwrap()]), network: Network::Regtest, - rest_service_addr: SocketAddr::from_str( - args_config.node_rest_service_address.as_deref().unwrap(), + grpc_service_addr: SocketAddr::from_str( + args_config.node_grpc_service_address.as_deref().unwrap(), ) .unwrap(), alias: Some(parse_alias(args_config.node_alias.as_deref().unwrap()).unwrap()), @@ -1406,8 +1310,6 @@ mod tests { rpc_password: args_config.bitcoind_rpc_password.unwrap(), }, rgs_server_url: Some("https://rapidsync.lightningdevkit.org/snapshot/v2/".to_string()), - rabbitmq_connection_string: expected_rabbit_conn, - rabbitmq_exchange_name: expected_rabbit_exchange, lsps2_client_config: Some(LSPSClientConfig { node_id: PublicKey::from_str( "0217890e3aad8d35bc054f43acc00084b25229ecff0ab68debd82883ad65ee8266", @@ -1443,12 +1345,10 @@ mod tests { assert_eq!(config.listening_addrs, expected.listening_addrs); assert_eq!(config.announcement_addrs, expected.announcement_addrs); assert_eq!(config.network, expected.network); - assert_eq!(config.rest_service_addr, expected.rest_service_addr); + assert_eq!(config.grpc_service_addr, expected.grpc_service_addr); assert_eq!(config.storage_dir_path, expected.storage_dir_path); assert_eq!(config.chain_source, expected.chain_source); assert_eq!(config.rgs_server_url, expected.rgs_server_url); - assert_eq!(config.rabbitmq_connection_string, expected.rabbitmq_connection_string); - assert_eq!(config.rabbitmq_exchange_name, expected.rabbitmq_exchange_name); assert_eq!(config.lsps2_client_config, expected.lsps2_client_config); #[cfg(feature = "experimental-lsps2-support")] assert_eq!(config.lsps2_service_config.is_some(), expected.lsps2_service_config.is_some()); @@ -1457,16 +1357,6 @@ mod tests { assert_eq!(config.tor_config, expected.tor_config); } - #[test] - #[cfg(feature = "events-rabbitmq")] - fn test_error_if_rabbitmq_feature_without_valid_config_file() { - let args_config = empty_args_config(); - let result = load_config(&args_config); - assert!(result.is_err()); - let err = result.unwrap_err(); - assert_eq!(err.kind(), io::ErrorKind::InvalidInput); - } - #[test] #[cfg(feature = "experimental-lsps2-support")] fn test_error_if_lsps2_feature_without_valid_config_file() { @@ -1479,12 +1369,11 @@ mod tests { #[test] #[cfg(not(feature = "experimental-lsps2-support"))] - #[cfg(not(feature = "events-rabbitmq"))] - fn test_default_rest_service_address() { + fn test_default_grpc_service_address() { let storage_path = std::env::temp_dir(); - let config_file_name = "test_default_rest_service_address.toml"; + let config_file_name = "test_default_grpc_service_address.toml"; - // Config without rest_service_address + // Config without grpc_service_address let toml_config = r#" [node] network = "regtest" @@ -1503,8 +1392,8 @@ mod tests { let config = load_config(&args_config).unwrap(); assert_eq!( - config.rest_service_addr, - SocketAddr::from_str(DEFAULT_REST_SERVICE_ADDRESS).unwrap() + config.grpc_service_addr, + SocketAddr::from_str(DEFAULT_GRPC_SERVICE_ADDRESS).unwrap() ); } diff --git a/ldk-server/src/util/proto_adapter.rs b/ldk-server/src/util/proto_adapter.rs index 8576752e..28cc7ec1 100644 --- a/ldk-server/src/util/proto_adapter.rs +++ b/ldk-server/src/util/proto_adapter.rs @@ -9,7 +9,6 @@ use bytes::Bytes; use hex::prelude::*; -use hyper::StatusCode; use ldk_node::bitcoin::hashes::sha256; use ldk_node::bitcoin::secp256k1::PublicKey; use ldk_node::config::{ChannelConfig, MaxDustHTLCExposure}; @@ -23,7 +22,6 @@ use ldk_node::payment::{ ConfirmationStatus, PaymentDetails, PaymentDirection, PaymentKind, PaymentStatus, }; use ldk_node::{ChannelDetails, LightningBalance, PeerDetails, PendingSweepBalance, UserChannelId}; -use ldk_server_grpc::error::{ErrorCode, ErrorResponse}; use ldk_server_grpc::types::confirmation_status::Status::{Confirmed, Unconfirmed}; use ldk_server_grpc::types::lightning_balance::BalanceType::{ ClaimableAwaitingConfirmations, ClaimableOnChannelClose, ContentiousClaimable, @@ -40,9 +38,7 @@ use ldk_server_grpc::types::{ }; use crate::api::error::LdkServerError; -use crate::api::error::LdkServerErrorCode::{ - AuthError, InternalServerError, InvalidRequestError, LightningError, -}; +use crate::api::error::LdkServerErrorCode::InvalidRequestError; pub(crate) fn peer_to_proto(peer: PeerDetails) -> Peer { Peer { @@ -514,23 +510,3 @@ pub(crate) fn graph_node_to_proto(node: NodeInfo) -> ldk_server_grpc::types::Gra announcement_info: node.announcement_info.map(graph_node_announcement_to_proto), } } - -pub(crate) fn to_error_response(ldk_error: LdkServerError) -> (ErrorResponse, StatusCode) { - let error_code = match ldk_error.error_code { - InvalidRequestError => ErrorCode::InvalidRequestError, - AuthError => ErrorCode::AuthError, - LightningError => ErrorCode::LightningError, - InternalServerError => ErrorCode::InternalServerError, - } as i32; - - let status = match ldk_error.error_code { - InvalidRequestError => StatusCode::BAD_REQUEST, - AuthError => StatusCode::UNAUTHORIZED, - LightningError => StatusCode::INTERNAL_SERVER_ERROR, - InternalServerError => StatusCode::INTERNAL_SERVER_ERROR, - }; - - let error_response = ErrorResponse { message: ldk_error.message, error_code }; - - (error_response, status) -} diff --git a/ldk-server/src/util/tls.rs b/ldk-server/src/util/tls.rs index 99f881b5..b7cc57b6 100644 --- a/ldk-server/src/util/tls.rs +++ b/ldk-server/src/util/tls.rs @@ -410,10 +410,12 @@ fn load_tls_config(cert_path: &str, key_path: &str) -> Result Date: Fri, 27 Mar 2026 13:56:17 -0500 Subject: [PATCH 5/8] Update client library for gRPC Switch from protobuf-over-REST to gRPC wire format. Requests use gRPC framing (5-byte length-prefix) with application/grpc+proto content-type, routed to /api.LightningNode/{method}. HMAC auth simplified to timestamp-only signing to match the server change. Error handling uses Trailers-Only mode: when the server returns a gRPC error without a body, grpc-status appears as a regular HTTP/2 header that reqwest can read. Co-Authored-By: Claude Opus 4.6 (1M context) --- Cargo.lock | 1176 +------------------------------ ldk-server-cli/src/main.rs | 4 +- ldk-server-cli/src/types.rs | 2 +- ldk-server-client/src/client.rs | 357 ++++------ ldk-server-client/src/error.rs | 8 +- ldk-server-client/src/lib.rs | 2 +- 6 files changed, 189 insertions(+), 1360 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7ba5aea5..8ddaff48 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,17 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "aes" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", -] - [[package]] name = "ahash" version = "0.8.12" @@ -34,54 +23,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "amq-protocol" -version = "7.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "587d313f3a8b4a40f866cc84b6059fe83133bf172165ac3b583129dd211d8e1c" -dependencies = [ - "amq-protocol-tcp", - "amq-protocol-types", - "amq-protocol-uri", - "cookie-factory", - "nom", - "serde", -] - -[[package]] -name = "amq-protocol-tcp" -version = "7.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc707ab9aa964a85d9fc25908a3fdc486d2e619406883b3105b48bf304a8d606" -dependencies = [ - "amq-protocol-uri", - "tcp-stream", - "tracing", -] - -[[package]] -name = "amq-protocol-types" -version = "7.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf99351d92a161c61ec6ecb213bc7057f5b837dd4e64ba6cb6491358efd770c4" -dependencies = [ - "cookie-factory", - "nom", - "serde", - "serde_json", -] - -[[package]] -name = "amq-protocol-uri" -version = "7.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f89f8273826a676282208e5af38461a07fe939def57396af6ad5997fcf56577d" -dependencies = [ - "amq-protocol-types", - "percent-encoding", - "url", -] - [[package]] name = "android_system_properties" version = "0.1.5" @@ -109,172 +50,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" -[[package]] -name = "asn1-rs" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56624a96882bb8c26d61312ae18cb45868e5a9992ea73c58e45c3101e56a1e60" -dependencies = [ - "asn1-rs-derive", - "asn1-rs-impl", - "displaydoc", - "nom", - "num-traits", - "rusticata-macros", - "thiserror", - "time", -] - -[[package]] -name = "asn1-rs-derive" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", - "synstructure", -] - -[[package]] -name = "asn1-rs-impl" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "async-channel" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" -dependencies = [ - "concurrent-queue", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-executor" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" -dependencies = [ - "async-task", - "concurrent-queue", - "fastrand 2.4.1", - "futures-lite 2.6.1", - "pin-project-lite", - "slab", -] - -[[package]] -name = "async-global-executor" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13f937e26114b93193065fd44f507aa2e9169ad0cdabbb996920b1fe1ddea7ba" -dependencies = [ - "async-channel", - "async-executor", - "async-io 2.6.0", - "async-lock 3.4.2", - "blocking", - "futures-lite 2.6.1", -] - -[[package]] -name = "async-global-executor-trait" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9af57045d58eeb1f7060e7025a1631cbc6399e0a1d10ad6735b3d0ea7f8346ce" -dependencies = [ - "async-global-executor", - "async-trait", - "executor-trait", -] - -[[package]] -name = "async-io" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" -dependencies = [ - "async-lock 2.8.0", - "autocfg", - "cfg-if", - "concurrent-queue", - "futures-lite 1.13.0", - "log", - "parking", - "polling 2.8.0", - "rustix 0.37.28", - "slab", - "socket2 0.4.10", - "waker-fn", -] - -[[package]] -name = "async-io" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" -dependencies = [ - "autocfg", - "cfg-if", - "concurrent-queue", - "futures-io", - "futures-lite 2.6.1", - "parking", - "polling 3.11.0", - "rustix 1.1.4", - "slab", - "windows-sys 0.61.2", -] - -[[package]] -name = "async-lock" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" -dependencies = [ - "event-listener 2.5.3", -] - -[[package]] -name = "async-lock" -version = "3.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" -dependencies = [ - "event-listener 5.4.1", - "event-listener-strategy", - "pin-project-lite", -] - -[[package]] -name = "async-reactor-trait" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6012d170ad00de56c9ee354aef2e358359deb1ec504254e0e5a3774771de0e" -dependencies = [ - "async-io 1.13.0", - "async-trait", - "futures-core", - "reactor-trait", -] - -[[package]] -name = "async-task" -version = "4.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" - [[package]] name = "async-trait" version = "0.1.89" @@ -365,12 +140,6 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" -[[package]] -name = "base64ct" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" - [[package]] name = "bdk_chain" version = "0.23.3" @@ -554,37 +323,6 @@ dependencies = [ "webpki-roots 0.25.4", ] -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "block-padding" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" -dependencies = [ - "generic-array", -] - -[[package]] -name = "blocking" -version = "1.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" -dependencies = [ - "async-channel", - "async-task", - "futures-io", - "futures-lite 2.6.1", - "piper", -] - [[package]] name = "bumpalo" version = "3.20.2" @@ -606,15 +344,6 @@ dependencies = [ "serde", ] -[[package]] -name = "cbc" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" -dependencies = [ - "cipher", -] - [[package]] name = "cc" version = "1.2.59" @@ -655,16 +384,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", -] - [[package]] name = "clap" version = "4.6.0" @@ -713,39 +432,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" -[[package]] -name = "cms" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b77c319abfd5219629c45c34c89ba945ed3c5e49fcde9d16b6c3885f118a730" -dependencies = [ - "const-oid", - "der", - "spki", - "x509-cert", -] - -[[package]] -name = "concurrent-queue" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "const-oid" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" - -[[package]] -name = "cookie-factory" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9885fa71e26b8ab7855e2ec7cae6e9b380edff76cd052e07c683a0319d51b3a2" - [[package]] name = "core-foundation" version = "0.9.4" @@ -762,104 +448,6 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" -[[package]] -name = "cpufeatures" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" -dependencies = [ - "libc", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" - -[[package]] -name = "crypto-common" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "data-encoding" -version = "2.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" - -[[package]] -name = "der" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" -dependencies = [ - "const-oid", - "der_derive", - "flagset", - "pem-rfc7468", - "zeroize", -] - -[[package]] -name = "der-parser" -version = "10.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6" -dependencies = [ - "asn1-rs", - "displaydoc", - "nom", - "num-bigint", - "num-traits", - "rusticata-macros", -] - -[[package]] -name = "der_derive" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "deranged" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" -dependencies = [ - "powerfmt", -] - -[[package]] -name = "des" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdd80ce8ce993de27e9f063a444a4d53ce8e8db4c1f00cc03af5ad5a9867a1e" -dependencies = [ - "cipher", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", - "subtle", -] - [[package]] name = "displaydoc" version = "0.2.5" @@ -881,12 +469,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "doc-comment" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "780955b8b195a21ab8e4ac6b60dd1dbdcec1dc6c51c0617964b08c81785e12c9" - [[package]] name = "either" version = "1.15.0" @@ -950,42 +532,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - -[[package]] -name = "event-listener" -version = "5.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener-strategy" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" -dependencies = [ - "event-listener 5.4.1", - "pin-project-lite", -] - -[[package]] -name = "executor-trait" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c39dff9342e4e0e16ce96be751eb21a94e94a87bb2f6e63ad1961c2ce109bf" -dependencies = [ - "async-trait", -] - [[package]] name = "fallible-iterator" version = "0.3.0" @@ -998,15 +544,6 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" -[[package]] -name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] - [[package]] name = "fastrand" version = "2.4.1" @@ -1025,23 +562,6 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" -[[package]] -name = "flagset" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7ac824320a75a52197e8f2d787f6a38b6718bb6897a35142d749af3c0e8f4fe" - -[[package]] -name = "flume" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" -dependencies = [ - "futures-core", - "futures-sink", - "spin", -] - [[package]] name = "fnv" version = "1.0.7" @@ -1111,34 +631,6 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" -[[package]] -name = "futures-lite" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" -dependencies = [ - "fastrand 1.9.0", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] - -[[package]] -name = "futures-lite" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" -dependencies = [ - "fastrand 2.4.1", - "futures-core", - "futures-io", - "parking", - "pin-project-lite", -] - [[package]] name = "futures-macro" version = "0.3.32" @@ -1179,16 +671,6 @@ dependencies = [ "slab", ] -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - [[package]] name = "getrandom" version = "0.2.17" @@ -1325,24 +807,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - -[[package]] -name = "hermit-abi" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - [[package]] name = "hex-conservative" version = "0.2.2" @@ -1358,15 +822,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - [[package]] name = "home" version = "0.5.12" @@ -1710,36 +1165,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "inout" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" -dependencies = [ - "block-padding", - "generic-array", -] - -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi 0.3.9", - "libc", - "windows-sys 0.48.0", -] - [[package]] name = "ipnet" version = "2.12.0" @@ -1783,28 +1208,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "lapin" -version = "2.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02d2aa4725b9607915fa1a73e940710a3be6af508ce700e56897cbe8847fbb07" -dependencies = [ - "amq-protocol", - "async-global-executor-trait", - "async-reactor-trait", - "async-trait", - "executor-trait", - "flume", - "futures-core", - "futures-io", - "parking_lot", - "pinky-swear", - "reactor-trait", - "serde", - "tracing", - "waker-fn", -] - [[package]] name = "lazy_static" version = "1.5.0" @@ -1858,7 +1261,6 @@ dependencies = [ name = "ldk-server" version = "0.1.0" dependencies = [ - "async-trait", "base64 0.21.7", "bytes", "chrono", @@ -1869,7 +1271,6 @@ dependencies = [ "http-body-util", "hyper 1.9.0", "hyper-util", - "lapin", "ldk-node", "ldk-server-grpc", "log", @@ -2085,12 +1486,6 @@ dependencies = [ "bitcoin", ] -[[package]] -name = "linux-raw-sys" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" - [[package]] name = "linux-raw-sys" version = "0.4.15" @@ -2109,15 +1504,6 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" -[[package]] -name = "lock_api" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" -dependencies = [ - "scopeguard", -] - [[package]] name = "log" version = "0.4.29" @@ -2144,187 +1530,61 @@ checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "miniscript" -version = "12.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487906208f38448e186e3deb02f2b8ef046a9078b0de00bdb28bf4fb9b76951c" -dependencies = [ - "bech32", - "bitcoin", - "serde", -] - -[[package]] -name = "mio" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" -dependencies = [ - "libc", - "wasi", - "windows-sys 0.61.2", -] - -[[package]] -name = "multimap" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" - -[[package]] -name = "musig2" -version = "0.1.0" -source = "git+https://github.com/arik-so/rust-musig2?rev=6f95a05718cbb44d8fe3fa6021aea8117aa38d50#6f95a05718cbb44d8fe3fa6021aea8117aa38d50" -dependencies = [ - "bitcoin", -] - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "num-bigint" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" -dependencies = [ - "num-integer", - "num-traits", -] - -[[package]] -name = "num-conv" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - -[[package]] -name = "oid-registry" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7" -dependencies = [ - "asn1-rs", -] - -[[package]] -name = "once_cell" -version = "1.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" - -[[package]] -name = "openssl-probe" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" - -[[package]] -name = "p12-keystore" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cae83056e7cb770211494a0ecf66d9fa7eba7d00977e5bb91f0e925b40b937f" -dependencies = [ - "cbc", - "cms", - "der", - "des", - "hex", - "hmac", - "pkcs12", - "pkcs5", - "rand 0.9.2", - "rc2", - "sha1", - "sha2", - "thiserror", - "x509-parser", -] - -[[package]] -name = "parking" -version = "2.2.1" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] -name = "parking_lot" -version = "0.12.5" +name = "miniscript" +version = "12.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +checksum = "487906208f38448e186e3deb02f2b8ef046a9078b0de00bdb28bf4fb9b76951c" dependencies = [ - "lock_api", - "parking_lot_core", + "bech32", + "bitcoin", + "serde", ] [[package]] -name = "parking_lot_core" -version = "0.9.12" +name = "mio" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" dependencies = [ - "cfg-if", "libc", - "redox_syscall", - "smallvec", - "windows-link", + "wasi", + "windows-sys 0.61.2", ] [[package]] -name = "pbkdf2" -version = "0.12.2" +name = "multimap" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" + +[[package]] +name = "musig2" +version = "0.1.0" +source = "git+https://github.com/arik-so/rust-musig2?rev=6f95a05718cbb44d8fe3fa6021aea8117aa38d50#6f95a05718cbb44d8fe3fa6021aea8117aa38d50" dependencies = [ - "digest", - "hmac", + "bitcoin", ] [[package]] -name = "pem-rfc7468" -version = "0.7.0" +name = "num-traits" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ - "base64ct", + "autocfg", ] +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + [[package]] name = "percent-encoding" version = "2.3.2" @@ -2373,95 +1633,12 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" -[[package]] -name = "pinky-swear" -version = "6.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1ea6e230dd3a64d61bcb8b79e597d3ab6b4c94ec7a234ce687dd718b4f2e657" -dependencies = [ - "doc-comment", - "flume", - "parking_lot", - "tracing", -] - -[[package]] -name = "piper" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" -dependencies = [ - "atomic-waker", - "fastrand 2.4.1", - "futures-io", -] - -[[package]] -name = "pkcs12" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "695b3df3d3cc1015f12d70235e35b6b79befc5fa7a9b95b951eab1dd07c9efc2" -dependencies = [ - "cms", - "const-oid", - "der", - "digest", - "spki", - "x509-cert", - "zeroize", -] - -[[package]] -name = "pkcs5" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e847e2c91a18bfa887dd028ec33f2fe6f25db77db3619024764914affe8b69a6" -dependencies = [ - "aes", - "cbc", - "der", - "pbkdf2", - "scrypt", - "sha2", - "spki", -] - [[package]] name = "pkg-config" version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" -[[package]] -name = "polling" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" -dependencies = [ - "autocfg", - "bitflags 1.3.2", - "cfg-if", - "concurrent-queue", - "libc", - "log", - "pin-project-lite", - "windows-sys 0.48.0", -] - -[[package]] -name = "polling" -version = "3.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" -dependencies = [ - "cfg-if", - "concurrent-queue", - "hermit-abi 0.5.2", - "pin-project-lite", - "rustix 1.1.4", - "windows-sys 0.61.2", -] - [[package]] name = "possiblyrandom" version = "0.2.0" @@ -2479,12 +1656,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - [[package]] name = "ppv-lite86" version = "0.2.21" @@ -2712,35 +1883,6 @@ dependencies = [ "getrandom 0.3.4", ] -[[package]] -name = "rc2" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62c64daa8e9438b84aaae55010a93f396f8e60e3911590fcba770d04643fc1dd" -dependencies = [ - "cipher", -] - -[[package]] -name = "reactor-trait" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "438a4293e4d097556730f4711998189416232f009c137389e0f961d2bc0ddc58" -dependencies = [ - "async-trait", - "futures-core", - "futures-io", -] - -[[package]] -name = "redox_syscall" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags 2.11.0", -] - [[package]] name = "regex" version = "1.12.3" @@ -2794,7 +1936,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "rustls 0.21.12", - "rustls-pemfile 1.0.4", + "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", @@ -2883,29 +2025,6 @@ version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" -[[package]] -name = "rusticata-macros" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" -dependencies = [ - "nom", -] - -[[package]] -name = "rustix" -version = "0.37.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "519165d378b97752ca44bbe15047d5d3409e875f39327546b42ac81d7e18c1b6" -dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys 0.3.8", - "windows-sys 0.48.0", -] - [[package]] name = "rustix" version = "0.38.44" @@ -2959,32 +2078,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rustls-connector" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70cc376c6ba1823ae229bacf8ad93c136d93524eab0e4e5e0e4f96b9c4e5b212" -dependencies = [ - "log", - "rustls 0.23.37", - "rustls-native-certs", - "rustls-pki-types", - "rustls-webpki 0.103.10", -] - -[[package]] -name = "rustls-native-certs" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" -dependencies = [ - "openssl-probe", - "rustls-pemfile 2.2.0", - "rustls-pki-types", - "schannel", - "security-framework", -] - [[package]] name = "rustls-pemfile" version = "1.0.4" @@ -2994,15 +2087,6 @@ dependencies = [ "base64 0.21.7", ] -[[package]] -name = "rustls-pemfile" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" -dependencies = [ - "rustls-pki-types", -] - [[package]] name = "rustls-pki-types" version = "1.14.0" @@ -3046,41 +2130,6 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" -[[package]] -name = "salsa20" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" -dependencies = [ - "cipher", -] - -[[package]] -name = "schannel" -version = "0.1.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "scrypt" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" -dependencies = [ - "pbkdf2", - "salsa20", - "sha2", -] - [[package]] name = "sct" version = "0.7.1" @@ -3112,29 +2161,6 @@ dependencies = [ "cc", ] -[[package]] -name = "security-framework" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" -dependencies = [ - "bitflags 2.11.0", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "semver" version = "1.0.28" @@ -3205,28 +2231,6 @@ dependencies = [ "serde", ] -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha2" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - [[package]] name = "shlex" version = "1.3.0" @@ -3255,16 +2259,6 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" -[[package]] -name = "socket2" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "socket2" version = "0.5.10" @@ -3285,25 +2279,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] - -[[package]] -name = "spki" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" -dependencies = [ - "base64ct", - "der", -] - [[package]] name = "stable_deref_trait" version = "1.2.1" @@ -3391,25 +2366,13 @@ dependencies = [ "libc", ] -[[package]] -name = "tcp-stream" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "495b0abdce3dc1f8fd27240651c9e68890c14e9d9c61527b1ce44d8a5a7bd3d5" -dependencies = [ - "cfg-if", - "p12-keystore", - "rustls-connector", - "rustls-pemfile 2.2.0", -] - [[package]] name = "tempfile" version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ - "fastrand 2.4.1", + "fastrand", "getrandom 0.4.2", "once_cell", "rustix 1.1.4", @@ -3436,37 +2399,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "time" -version = "0.3.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" -dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde_core", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" - -[[package]] -name = "time-macros" -version = "0.2.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" -dependencies = [ - "num-conv", - "time-core", -] - [[package]] name = "tinystr" version = "0.8.3" @@ -3737,12 +2669,6 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" -[[package]] -name = "typenum" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" - [[package]] name = "unicode-ident" version = "1.0.24" @@ -3821,12 +2747,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "waker-fn" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" - [[package]] name = "want" version = "0.3.1" @@ -4421,34 +3341,6 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" -[[package]] -name = "x509-cert" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1301e935010a701ae5f8655edc0ad17c44bad3ac5ce8c39185f75453b720ae94" -dependencies = [ - "const-oid", - "der", - "spki", -] - -[[package]] -name = "x509-parser" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4569f339c0c402346d4a75a9e39cf8dad310e287eef1ff56d4c68e5067f53460" -dependencies = [ - "asn1-rs", - "data-encoding", - "der-parser", - "lazy_static", - "nom", - "oid-registry", - "rusticata-macros", - "thiserror", - "time", -] - [[package]] name = "yoke" version = "0.8.2" diff --git a/ldk-server-cli/src/main.rs b/ldk-server-cli/src/main.rs index c13ec9bc..6aec5d69 100644 --- a/ldk-server-cli/src/main.rs +++ b/ldk-server-cli/src/main.rs @@ -23,7 +23,7 @@ use ldk_server_client::error::LdkServerError; use ldk_server_client::error::LdkServerErrorCode::{ AuthError, InternalError, InternalServerError, InvalidRequestError, LightningError, }; -use ldk_server_client::ldk_server_protos::api::{ +use ldk_server_client::ldk_server_grpc::api::{ Bolt11ClaimForHashRequest, Bolt11ClaimForHashResponse, Bolt11FailForHashRequest, Bolt11FailForHashResponse, Bolt11ReceiveForHashRequest, Bolt11ReceiveForHashResponse, Bolt11ReceiveRequest, Bolt11ReceiveResponse, Bolt11ReceiveVariableAmountViaJitChannelRequest, @@ -45,7 +45,7 @@ use ldk_server_client::ldk_server_protos::api::{ SpontaneousSendResponse, UnifiedSendRequest, UnifiedSendResponse, UpdateChannelConfigRequest, UpdateChannelConfigResponse, VerifySignatureRequest, VerifySignatureResponse, }; -use ldk_server_client::ldk_server_protos::types::{ +use ldk_server_client::ldk_server_grpc::types::{ bolt11_invoice_description, Bolt11InvoiceDescription, ChannelConfig, PageToken, RouteParametersConfig, }; diff --git a/ldk-server-cli/src/types.rs b/ldk-server-cli/src/types.rs index 92f778ad..5b1aa93e 100644 --- a/ldk-server-cli/src/types.rs +++ b/ldk-server-cli/src/types.rs @@ -16,7 +16,7 @@ use std::fmt; use std::str::FromStr; -use ldk_server_client::ldk_server_protos::types::{ForwardedPayment, PageToken, Payment}; +use ldk_server_client::ldk_server_grpc::types::{ForwardedPayment, PageToken, Payment}; use serde::Serialize; /// CLI-specific wrapper for paginated responses that formats the page token diff --git a/ldk-server-client/src/client.rs b/ldk-server-client/src/client.rs index e40e6c9e..daa06394 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_grpc::api::{ Bolt11ClaimForHashRequest, Bolt11ClaimForHashResponse, Bolt11FailForHashRequest, Bolt11FailForHashResponse, Bolt11ReceiveForHashRequest, Bolt11ReceiveForHashResponse, Bolt11ReceiveRequest, Bolt11ReceiveResponse, Bolt11ReceiveVariableAmountViaJitChannelRequest, @@ -34,7 +34,7 @@ use ldk_server_protos::api::{ SpontaneousSendResponse, UnifiedSendRequest, UnifiedSendResponse, UpdateChannelConfigRequest, UpdateChannelConfigResponse, VerifySignatureRequest, VerifySignatureResponse, }; -use ldk_server_protos::endpoints::{ +use ldk_server_grpc::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, @@ -42,15 +42,13 @@ use ldk_server_protos::endpoints::{ DISCONNECT_PEER_PATH, EXPORT_PATHFINDING_SCORES_PATH, FORCE_CLOSE_CHANNEL_PATH, GET_BALANCES_PATH, GET_METRICS_PATH, GET_NODE_INFO_PATH, GET_PAYMENT_DETAILS_PATH, GRAPH_GET_CHANNEL_PATH, GRAPH_GET_NODE_PATH, GRAPH_LIST_CHANNELS_PATH, GRAPH_LIST_NODES_PATH, - LIST_CHANNELS_PATH, LIST_FORWARDED_PAYMENTS_PATH, LIST_PAYMENTS_PATH, LIST_PEERS_PATH, - ONCHAIN_RECEIVE_PATH, ONCHAIN_SEND_PATH, OPEN_CHANNEL_PATH, SIGN_MESSAGE_PATH, SPLICE_IN_PATH, - SPLICE_OUT_PATH, SPONTANEOUS_SEND_PATH, UNIFIED_SEND_PATH, UPDATE_CHANNEL_CONFIG_PATH, - VERIFY_SIGNATURE_PATH, + GRPC_SERVICE_PREFIX, LIST_CHANNELS_PATH, LIST_FORWARDED_PAYMENTS_PATH, LIST_PAYMENTS_PATH, + LIST_PEERS_PATH, ONCHAIN_RECEIVE_PATH, ONCHAIN_SEND_PATH, OPEN_CHANNEL_PATH, SIGN_MESSAGE_PATH, + SPLICE_IN_PATH, SPLICE_OUT_PATH, SPONTANEOUS_SEND_PATH, UNIFIED_SEND_PATH, + UPDATE_CHANNEL_CONFIG_PATH, VERIFY_SIGNATURE_PATH, }; -use ldk_server_protos::error::{ErrorCode, ErrorResponse}; -use prost::bytes::Bytes; +use ldk_server_grpc::grpc::{decode_grpc_body, encode_grpc_frame, percent_decode}; use prost::Message; -use reqwest::header::CONTENT_TYPE; use reqwest::{Certificate, Client}; use crate::error::LdkServerError; @@ -58,9 +56,7 @@ use crate::error::LdkServerErrorCode::{ AuthError, InternalError, InternalServerError, InvalidRequestError, LightningError, }; -const APPLICATION_OCTET_STREAM: &str = "application/octet-stream"; - -/// Client to access a hosted instance of LDK Server. +/// Client to access a hosted instance of LDK Server via gRPC. /// /// The client requires the server's TLS certificate to be provided for verification. /// This certificate can be found at `/tls.crt` after the @@ -72,11 +68,6 @@ pub struct LdkServerClient { api_key: String, } -enum RequestType { - Get, - Post, -} - impl LdkServerClient { /// Constructs a [`LdkServerClient`] using `base_url` as the ldk-server endpoint. /// @@ -98,28 +89,26 @@ impl LdkServerClient { /// Computes the HMAC-SHA256 authentication header value. /// Format: "HMAC :" - fn compute_auth_header(&self, body: &[u8]) -> String { + /// Uses timestamp-only HMAC (no body) since TLS guarantees integrity. + fn compute_auth_header(&self) -> String { let timestamp = SystemTime::now() .duration_since(UNIX_EPOCH) .expect("System time should be after Unix epoch") .as_secs(); - // Compute HMAC-SHA256(api_key, timestamp_bytes || body) + // HMAC-SHA256(api_key, timestamp_bytes) — no body let mut hmac_engine: HmacEngine = HmacEngine::new(self.api_key.as_bytes()); hmac_engine.input(×tamp.to_be_bytes()); - hmac_engine.input(body); let hmac_result = Hmac::::from_engine(hmac_engine); format!("HMAC {}:{}", timestamp, hmac_result) } /// Retrieve the latest node info like `node_id`, `current_best_block` etc. - /// For API contract/usage, refer to docs for [`GetNodeInfoRequest`] and [`GetNodeInfoResponse`]. pub async fn get_node_info( &self, request: GetNodeInfoRequest, ) -> Result { - let url = format!("https://{}/{GET_NODE_INFO_PATH}", self.base_url); - self.post_request(&request, &url).await + self.grpc_unary(&request, GET_NODE_INFO_PATH).await } /// Retrieve the node metrics in Prometheus format. @@ -132,9 +121,22 @@ impl LdkServerClient { &self, username: Option<&str>, password: Option<&str>, ) -> Result { let url = format!("https://{}/{GET_METRICS_PATH}", self.base_url); - let payload = - self.make_request(&url, RequestType::Get, None, false, username, password).await?; - + let mut builder = self.client.get(&url); + if let (Some(u), Some(p)) = (username, password) { + builder = builder.basic_auth(u, Some(p)); + } + let response = builder.send().await.map_err(|e| { + LdkServerError::new(InternalError, format!("HTTP request failed: {}", e)) + })?; + if !response.status().is_success() { + return Err(LdkServerError::new( + InternalError, + format!("Metrics request failed with status {}", response.status()), + )); + } + let payload = response.bytes().await.map_err(|e| { + LdkServerError::new(InternalError, format!("Failed to read response body: {}", e)) + })?; String::from_utf8(payload.to_vec()).map_err(|e| { LdkServerError::new( InternalError, @@ -144,411 +146,346 @@ impl LdkServerClient { } /// Retrieves an overview of all known balances. - /// For API contract/usage, refer to docs for [`GetBalancesRequest`] and [`GetBalancesResponse`]. pub async fn get_balances( &self, request: GetBalancesRequest, ) -> Result { - let url = format!("https://{}/{GET_BALANCES_PATH}", self.base_url); - self.post_request(&request, &url).await + self.grpc_unary(&request, GET_BALANCES_PATH).await } /// Retrieve a new on-chain funding address. - /// For API contract/usage, refer to docs for [`OnchainReceiveRequest`] and [`OnchainReceiveResponse`]. pub async fn onchain_receive( &self, request: OnchainReceiveRequest, ) -> Result { - let url = format!("https://{}/{ONCHAIN_RECEIVE_PATH}", self.base_url); - self.post_request(&request, &url).await + self.grpc_unary(&request, ONCHAIN_RECEIVE_PATH).await } /// Send an on-chain payment to the given address. - /// For API contract/usage, refer to docs for [`OnchainSendRequest`] and [`OnchainSendResponse`]. pub async fn onchain_send( &self, request: OnchainSendRequest, ) -> Result { - let url = format!("https://{}/{ONCHAIN_SEND_PATH}", self.base_url); - self.post_request(&request, &url).await + self.grpc_unary(&request, ONCHAIN_SEND_PATH).await } /// Retrieve a new BOLT11 payable invoice. - /// For API contract/usage, refer to docs for [`Bolt11ReceiveRequest`] and [`Bolt11ReceiveResponse`]. pub async fn bolt11_receive( &self, request: Bolt11ReceiveRequest, ) -> Result { - let url = format!("https://{}/{BOLT11_RECEIVE_PATH}", self.base_url); - self.post_request(&request, &url).await + self.grpc_unary(&request, BOLT11_RECEIVE_PATH).await } /// Retrieve a new BOLT11 payable invoice for a given payment hash. - /// The inbound payment will NOT be automatically claimed upon arrival. - /// For API contract/usage, refer to docs for [`Bolt11ReceiveForHashRequest`] and [`Bolt11ReceiveForHashResponse`]. pub async fn bolt11_receive_for_hash( &self, request: Bolt11ReceiveForHashRequest, ) -> Result { - let url = format!("https://{}/{BOLT11_RECEIVE_FOR_HASH_PATH}", self.base_url); - self.post_request(&request, &url).await + self.grpc_unary(&request, BOLT11_RECEIVE_FOR_HASH_PATH).await } - /// Manually claim a payment for a given payment hash with the corresponding preimage. - /// For API contract/usage, refer to docs for [`Bolt11ClaimForHashRequest`] and [`Bolt11ClaimForHashResponse`]. + /// Manually claim a payment for a given payment hash. pub async fn bolt11_claim_for_hash( &self, request: Bolt11ClaimForHashRequest, ) -> Result { - let url = format!("https://{}/{BOLT11_CLAIM_FOR_HASH_PATH}", self.base_url); - self.post_request(&request, &url).await + self.grpc_unary(&request, BOLT11_CLAIM_FOR_HASH_PATH).await } /// Manually fail a payment for a given payment hash. - /// For API contract/usage, refer to docs for [`Bolt11FailForHashRequest`] and [`Bolt11FailForHashResponse`]. pub async fn bolt11_fail_for_hash( &self, request: Bolt11FailForHashRequest, ) -> Result { - let url = format!("https://{}/{BOLT11_FAIL_FOR_HASH_PATH}", self.base_url); - self.post_request(&request, &url).await + self.grpc_unary(&request, BOLT11_FAIL_FOR_HASH_PATH).await } /// Retrieve a new fixed-amount BOLT11 invoice for receiving via an LSPS2 JIT channel. - /// For API contract/usage, refer to docs for [`Bolt11ReceiveViaJitChannelRequest`] and - /// [`Bolt11ReceiveViaJitChannelResponse`]. pub async fn bolt11_receive_via_jit_channel( &self, request: Bolt11ReceiveViaJitChannelRequest, ) -> Result { - let url = format!("https://{}/{BOLT11_RECEIVE_VIA_JIT_CHANNEL_PATH}", self.base_url); - self.post_request(&request, &url).await + self.grpc_unary(&request, BOLT11_RECEIVE_VIA_JIT_CHANNEL_PATH).await } /// Retrieve a new variable-amount BOLT11 invoice for receiving via an LSPS2 JIT channel. - /// For API contract/usage, refer to docs for - /// [`Bolt11ReceiveVariableAmountViaJitChannelRequest`] and - /// [`Bolt11ReceiveVariableAmountViaJitChannelResponse`]. pub async fn bolt11_receive_variable_amount_via_jit_channel( &self, request: Bolt11ReceiveVariableAmountViaJitChannelRequest, ) -> Result { - let url = format!( - "https://{}/{BOLT11_RECEIVE_VARIABLE_AMOUNT_VIA_JIT_CHANNEL_PATH}", - self.base_url, - ); - self.post_request(&request, &url).await + self.grpc_unary(&request, BOLT11_RECEIVE_VARIABLE_AMOUNT_VIA_JIT_CHANNEL_PATH).await } /// Send a payment for a BOLT11 invoice. - /// For API contract/usage, refer to docs for [`Bolt11SendRequest`] and [`Bolt11SendResponse`]. pub async fn bolt11_send( &self, request: Bolt11SendRequest, ) -> Result { - let url = format!("https://{}/{BOLT11_SEND_PATH}", self.base_url); - self.post_request(&request, &url).await + self.grpc_unary(&request, BOLT11_SEND_PATH).await } - /// Retrieve a new BOLT11 payable offer. - /// For API contract/usage, refer to docs for [`Bolt12ReceiveRequest`] and [`Bolt12ReceiveResponse`]. + /// Retrieve a new BOLT12 offer. pub async fn bolt12_receive( &self, request: Bolt12ReceiveRequest, ) -> Result { - let url = format!("https://{}/{BOLT12_RECEIVE_PATH}", self.base_url); - self.post_request(&request, &url).await + self.grpc_unary(&request, BOLT12_RECEIVE_PATH).await } /// Send a payment for a BOLT12 offer. - /// For API contract/usage, refer to docs for [`Bolt12SendRequest`] and [`Bolt12SendResponse`]. pub async fn bolt12_send( &self, request: Bolt12SendRequest, ) -> Result { - let url = format!("https://{}/{BOLT12_SEND_PATH}", self.base_url); - self.post_request(&request, &url).await + self.grpc_unary(&request, BOLT12_SEND_PATH).await } /// Creates a new outbound channel. - /// For API contract/usage, refer to docs for [`OpenChannelRequest`] and [`OpenChannelResponse`]. pub async fn open_channel( &self, request: OpenChannelRequest, ) -> Result { - let url = format!("https://{}/{OPEN_CHANNEL_PATH}", self.base_url); - self.post_request(&request, &url).await + self.grpc_unary(&request, OPEN_CHANNEL_PATH).await } /// Splices funds into the channel specified by given request. - /// For API contract/usage, refer to docs for [`SpliceInRequest`] and [`SpliceInResponse`]. pub async fn splice_in( &self, request: SpliceInRequest, ) -> Result { - let url = format!("https://{}/{SPLICE_IN_PATH}", self.base_url); - self.post_request(&request, &url).await + self.grpc_unary(&request, SPLICE_IN_PATH).await } /// Splices funds out of the channel specified by given request. - /// For API contract/usage, refer to docs for [`SpliceOutRequest`] and [`SpliceOutResponse`]. pub async fn splice_out( &self, request: SpliceOutRequest, ) -> Result { - let url = format!("https://{}/{SPLICE_OUT_PATH}", self.base_url); - self.post_request(&request, &url).await + self.grpc_unary(&request, SPLICE_OUT_PATH).await } /// Closes the channel specified by given request. - /// For API contract/usage, refer to docs for [`CloseChannelRequest`] and [`CloseChannelResponse`]. pub async fn close_channel( &self, request: CloseChannelRequest, ) -> Result { - let url = format!("https://{}/{CLOSE_CHANNEL_PATH}", self.base_url); - self.post_request(&request, &url).await + self.grpc_unary(&request, CLOSE_CHANNEL_PATH).await } /// Force closes the channel specified by given request. - /// For API contract/usage, refer to docs for [`ForceCloseChannelRequest`] and [`ForceCloseChannelResponse`]. pub async fn force_close_channel( &self, request: ForceCloseChannelRequest, ) -> Result { - let url = format!("https://{}/{FORCE_CLOSE_CHANNEL_PATH}", self.base_url); - self.post_request(&request, &url).await + self.grpc_unary(&request, FORCE_CLOSE_CHANNEL_PATH).await } /// Retrieves list of known channels. - /// For API contract/usage, refer to docs for [`ListChannelsRequest`] and [`ListChannelsResponse`]. pub async fn list_channels( &self, request: ListChannelsRequest, ) -> Result { - let url = format!("https://{}/{LIST_CHANNELS_PATH}", self.base_url); - self.post_request(&request, &url).await + self.grpc_unary(&request, LIST_CHANNELS_PATH).await } /// Retrieves list of all payments sent or received by us. - /// For API contract/usage, refer to docs for [`ListPaymentsRequest`] and [`ListPaymentsResponse`]. pub async fn list_payments( &self, request: ListPaymentsRequest, ) -> Result { - let url = format!("https://{}/{LIST_PAYMENTS_PATH}", self.base_url); - self.post_request(&request, &url).await + self.grpc_unary(&request, LIST_PAYMENTS_PATH).await } /// Updates the config for a previously opened channel. - /// For API contract/usage, refer to docs for [`UpdateChannelConfigRequest`] and [`UpdateChannelConfigResponse`]. pub async fn update_channel_config( &self, request: UpdateChannelConfigRequest, ) -> Result { - let url = format!("https://{}/{UPDATE_CHANNEL_CONFIG_PATH}", self.base_url); - self.post_request(&request, &url).await + self.grpc_unary(&request, UPDATE_CHANNEL_CONFIG_PATH).await } /// Retrieves payment details for a given payment id. - /// For API contract/usage, refer to docs for [`GetPaymentDetailsRequest`] and [`GetPaymentDetailsResponse`]. pub async fn get_payment_details( &self, request: GetPaymentDetailsRequest, ) -> Result { - let url = format!("https://{}/{GET_PAYMENT_DETAILS_PATH}", self.base_url); - self.post_request(&request, &url).await + self.grpc_unary(&request, GET_PAYMENT_DETAILS_PATH).await } /// Retrieves list of all forwarded payments. - /// For API contract/usage, refer to docs for [`ListForwardedPaymentsRequest`] and [`ListForwardedPaymentsResponse`]. pub async fn list_forwarded_payments( &self, request: ListForwardedPaymentsRequest, ) -> Result { - let url = format!("https://{}/{LIST_FORWARDED_PAYMENTS_PATH}", self.base_url); - self.post_request(&request, &url).await + self.grpc_unary(&request, LIST_FORWARDED_PAYMENTS_PATH).await } /// Connect to a peer on the Lightning Network. - /// For API contract/usage, refer to docs for [`ConnectPeerRequest`] and [`ConnectPeerResponse`]. pub async fn connect_peer( &self, request: ConnectPeerRequest, ) -> Result { - let url = format!("https://{}/{CONNECT_PEER_PATH}", self.base_url); - self.post_request(&request, &url).await + self.grpc_unary(&request, CONNECT_PEER_PATH).await } /// Disconnect from a peer and remove it from the peer store. - /// For API contract/usage, refer to docs for [`DisconnectPeerRequest`] and [`DisconnectPeerResponse`]. pub async fn disconnect_peer( &self, request: DisconnectPeerRequest, ) -> Result { - let url = format!("https://{}/{DISCONNECT_PEER_PATH}", self.base_url); - self.post_request(&request, &url).await + self.grpc_unary(&request, DISCONNECT_PEER_PATH).await } /// Retrieves list of peers. - /// For API contract/usage, refer to docs for [`ListPeersRequest`] and [`ListPeersResponse`]. pub async fn list_peers( &self, request: ListPeersRequest, ) -> Result { - let url = format!("https://{}/{LIST_PEERS_PATH}", self.base_url); - self.post_request(&request, &url).await + self.grpc_unary(&request, LIST_PEERS_PATH).await } /// Send a spontaneous payment (keysend) to a node. - /// For API contract/usage, refer to docs for [`SpontaneousSendRequest`] and [`SpontaneousSendResponse`]. pub async fn spontaneous_send( &self, request: SpontaneousSendRequest, ) -> Result { - let url = format!("https://{}/{SPONTANEOUS_SEND_PATH}", self.base_url); - self.post_request(&request, &url).await + self.grpc_unary(&request, SPONTANEOUS_SEND_PATH).await } /// Send a payment given a BIP 21 URI or BIP 353 Human-Readable Name. - /// For API contract/usage, refer to docs for [`UnifiedSendRequest`] and [`UnifiedSendResponse`]. pub async fn unified_send( &self, request: UnifiedSendRequest, ) -> Result { - let url = format!("https://{}/{UNIFIED_SEND_PATH}", self.base_url); - self.post_request(&request, &url).await + self.grpc_unary(&request, UNIFIED_SEND_PATH).await } /// Decode a BOLT11 invoice and return its parsed fields. - /// For API contract/usage, refer to docs for [`DecodeInvoiceRequest`] and [`DecodeInvoiceResponse`]. pub async fn decode_invoice( &self, request: DecodeInvoiceRequest, ) -> Result { - let url = format!("https://{}/{DECODE_INVOICE_PATH}", self.base_url); - self.post_request(&request, &url).await + self.grpc_unary(&request, DECODE_INVOICE_PATH).await } /// Decode a BOLT12 offer and return its parsed fields. - /// For API contract/usage, refer to docs for [`DecodeOfferRequest`] and [`DecodeOfferResponse`]. pub async fn decode_offer( &self, request: DecodeOfferRequest, ) -> Result { - let url = format!("https://{}/{DECODE_OFFER_PATH}", self.base_url); - self.post_request(&request, &url).await + self.grpc_unary(&request, DECODE_OFFER_PATH).await } /// Sign a message with the node's secret key. - /// For API contract/usage, refer to docs for [`SignMessageRequest`] and [`SignMessageResponse`]. pub async fn sign_message( &self, request: SignMessageRequest, ) -> Result { - let url = format!("https://{}/{SIGN_MESSAGE_PATH}", self.base_url); - self.post_request(&request, &url).await + self.grpc_unary(&request, SIGN_MESSAGE_PATH).await } /// Verify a signature against a message and public key. - /// For API contract/usage, refer to docs for [`VerifySignatureRequest`] and [`VerifySignatureResponse`]. pub async fn verify_signature( &self, request: VerifySignatureRequest, ) -> Result { - let url = format!("https://{}/{VERIFY_SIGNATURE_PATH}", self.base_url); - self.post_request(&request, &url).await + self.grpc_unary(&request, VERIFY_SIGNATURE_PATH).await } /// Export the pathfinding scores used by the router. - /// For API contract/usage, refer to docs for [`ExportPathfindingScoresRequest`] and [`ExportPathfindingScoresResponse`]. pub async fn export_pathfinding_scores( &self, request: ExportPathfindingScoresRequest, ) -> Result { - let url = format!("https://{}/{EXPORT_PATHFINDING_SCORES_PATH}", self.base_url); - self.post_request(&request, &url).await + self.grpc_unary(&request, EXPORT_PATHFINDING_SCORES_PATH).await } /// Returns a list of all known short channel IDs in the network graph. - /// For API contract/usage, refer to docs for [`GraphListChannelsRequest`] and [`GraphListChannelsResponse`]. pub async fn graph_list_channels( &self, request: GraphListChannelsRequest, ) -> Result { - let url = format!("https://{}/{GRAPH_LIST_CHANNELS_PATH}", self.base_url); - self.post_request(&request, &url).await + self.grpc_unary(&request, GRAPH_LIST_CHANNELS_PATH).await } /// Returns information on a channel with the given short channel ID from the network graph. - /// For API contract/usage, refer to docs for [`GraphGetChannelRequest`] and [`GraphGetChannelResponse`]. pub async fn graph_get_channel( &self, request: GraphGetChannelRequest, ) -> Result { - let url = format!("https://{}/{GRAPH_GET_CHANNEL_PATH}", self.base_url); - self.post_request(&request, &url).await + self.grpc_unary(&request, GRAPH_GET_CHANNEL_PATH).await } /// Returns a list of all known node IDs in the network graph. - /// For API contract/usage, refer to docs for [`GraphListNodesRequest`] and [`GraphListNodesResponse`]. pub async fn graph_list_nodes( &self, request: GraphListNodesRequest, ) -> Result { - let url = format!("https://{}/{GRAPH_LIST_NODES_PATH}", self.base_url); - self.post_request(&request, &url).await + self.grpc_unary(&request, GRAPH_LIST_NODES_PATH).await } /// Returns information on a node with the given ID from the network graph. - /// For API contract/usage, refer to docs for [`GraphGetNodeRequest`] and [`GraphGetNodeResponse`]. pub async fn graph_get_node( &self, request: GraphGetNodeRequest, ) -> Result { - let url = format!("https://{}/{GRAPH_GET_NODE_PATH}", self.base_url); - self.post_request(&request, &url).await + self.grpc_unary(&request, GRAPH_GET_NODE_PATH).await } - async fn post_request( - &self, request: &Rq, url: &str, + /// Send a unary gRPC request and decode the response. + async fn grpc_unary( + &self, request: &Rq, method: &str, ) -> Result { - let request_body = request.encode_to_vec(); - let payload = - self.make_request(url, RequestType::Post, Some(request_body), true, None, None).await?; - Rs::decode(&payload[..]).map_err(|e| { - LdkServerError::new(InternalError, format!("Failed to decode success response: {}", e)) - }) - } - - async fn make_request( - &self, url: &str, request_type: RequestType, body: Option>, - hmac_authenticated: bool, metrics_username: Option<&str>, metrics_password: Option<&str>, - ) -> Result { - let builder = match request_type { - RequestType::Get => self.client.get(url), - RequestType::Post => self.client.post(url), - }; - - let builder = if hmac_authenticated { - let body_for_auth = body.as_deref().unwrap_or(&[]); - let auth_header = self.compute_auth_header(body_for_auth); - builder.header("X-Auth", auth_header) - } else { - builder - }; - - let builder = if let Some(body_content) = body { - builder.header(CONTENT_TYPE, APPLICATION_OCTET_STREAM).body(body_content) - } else { - builder - }; - - let builder = if let (Some(username), Some(password)) = (metrics_username, metrics_password) - { - builder.basic_auth(username, Some(password)) - } else { - builder - }; - - let response_raw = builder.send().await.map_err(|e| { - LdkServerError::new(InternalError, format!("HTTP request failed: {}", e)) - })?; + let grpc_body = encode_grpc_frame(&request.encode_to_vec()).to_vec(); + + let url = format!("https://{}{}{}", self.base_url, GRPC_SERVICE_PREFIX, method); + let auth_header = self.compute_auth_header(); + + let response = self + .client + .post(&url) + .header("content-type", "application/grpc+proto") + .header("te", "trailers") + .header("x-auth", auth_header) + .body(grpc_body) + .send() + .await + .map_err(|e| { + LdkServerError::new(InternalError, format!("gRPC request failed: {}", e)) + })?; - self.handle_response(response_raw).await - } + // Check for Trailers-Only error responses (grpc-status in response headers). + // In gRPC, when there is no response body (error case), the server sends + // grpc-status as part of the initial HEADERS frame, readable as a regular header. + if let Some(status_val) = response.headers().get("grpc-status") { + if let Ok(status_str) = status_val.to_str() { + if let Ok(code) = status_str.parse::() { + if code != 0 { + let message = response + .headers() + .get("grpc-message") + .and_then(|v| v.to_str().ok()) + .map(percent_decode) + .unwrap_or_default(); + return Err(grpc_code_to_error(code, message)); + } + } + } + } - async fn handle_response( - &self, response_raw: reqwest::Response, - ) -> Result { - let status = response_raw.status(); - let payload = response_raw.bytes().await.map_err(|e| { + // Read the response body + let payload = response.bytes().await.map_err(|e| { LdkServerError::new(InternalError, format!("Failed to read response body: {}", e)) })?; - if status.is_success() { - Ok(payload) - } else { - let error_response = ErrorResponse::decode(&payload[..]).map_err(|e| { - LdkServerError::new( - InternalError, - format!("Failed to decode error response (status {}): {}", status, e), - ) - })?; + let proto_bytes = decode_grpc_body(&payload) + .map_err(|e| LdkServerError::new(InternalError, e.message))?; - 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, - }; + Rs::decode(proto_bytes).map_err(|e| { + LdkServerError::new(InternalError, format!("Failed to decode gRPC response: {}", e)) + }) + } +} + +/// Map a gRPC status code to an LdkServerError. +fn grpc_code_to_error(code: u32, message: String) -> LdkServerError { + let error_code = match code { + 3 => InvalidRequestError, // INVALID_ARGUMENT + 16 => AuthError, // UNAUTHENTICATED + 9 => LightningError, // FAILED_PRECONDITION + 13 => InternalServerError, // INTERNAL + _ => InternalError, + }; + LdkServerError::new(error_code, message) +} - Err(LdkServerError::new(error_code, error_response.message)) +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_grpc_code_to_error_all_known_codes() { + let cases = [ + (3u32, InvalidRequestError, "msg"), + (16, AuthError, "msg"), + (9, LightningError, "msg"), + (13, InternalServerError, "msg"), + ]; + for (code, expected_error_code, msg) in cases { + let err = grpc_code_to_error(code, msg.to_string()); + assert_eq!(err.error_code, expected_error_code, "wrong mapping for gRPC code {code}"); + assert_eq!(err.message, msg); } } + + #[test] + fn test_grpc_code_to_error_unknown_code() { + let err = grpc_code_to_error(99, "unknown".to_string()); + assert_eq!(err.error_code, InternalError); + } } diff --git a/ldk-server-client/src/error.rs b/ldk-server-client/src/error.rs index 67cba37d..bbccd40b 100644 --- a/ldk-server-client/src/error.rs +++ b/ldk-server-client/src/error.rs @@ -41,16 +41,16 @@ 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_grpc::error::ErrorCode::InvalidRequestError`]. InvalidRequestError, - /// Please refer to [`ldk_server_protos::error::ErrorCode::AuthError`]. + /// Please refer to [`ldk_server_grpc::error::ErrorCode::AuthError`]. AuthError, - /// Please refer to [`ldk_server_protos::error::ErrorCode::LightningError`]. + /// Please refer to [`ldk_server_grpc::error::ErrorCode::LightningError`]. LightningError, - /// Please refer to [`ldk_server_protos::error::ErrorCode::InternalServerError`]. + /// Please refer to [`ldk_server_grpc::error::ErrorCode::InternalServerError`]. InternalServerError, /// There is an unknown error, it could be a client-side bug, unrecognized error-code, network error diff --git a/ldk-server-client/src/lib.rs b/ldk-server-client/src/lib.rs index 098c7087..ab89afe8 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_grpc; From d47d69826aede1b6720e2b2ae5fd841f50d707e3 Mon Sep 17 00:00:00 2001 From: benthecarman Date: Fri, 27 Mar 2026 15:59:47 -0500 Subject: [PATCH 6/8] Add SubscribeEvents server-streaming RPC Implement the SubscribeEvents RPC end-to-end: a Stream variant in GrpcBody that delivers multiple gRPC-framed messages from an mpsc channel, a handler in service.rs that bridges the broadcast channel to the streaming response, a client EventStream type that reads gRPC frames incrementally from the HTTP/2 body, and e2e test assertions that verify payment events arrive via the stream. Also regenerates proto code to include SubscribeEventsRequest. Co-Authored-By: Claude Opus 4.6 (1M context) --- Cargo.lock | 5 + e2e-tests/Cargo.lock | 1173 +------------------------------ e2e-tests/Cargo.toml | 3 - e2e-tests/build.rs | 2 +- e2e-tests/src/lib.rs | 125 +--- e2e-tests/tests/e2e.rs | 212 +++--- ldk-server-client/Cargo.toml | 7 + ldk-server-client/src/client.rs | 291 +++++++- ldk-server/src/main.rs | 4 + ldk-server/src/service.rs | 83 ++- 10 files changed, 483 insertions(+), 1422 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8ddaff48..7228ef33 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1302,9 +1302,14 @@ name = "ldk-server-client" version = "0.1.0" dependencies = [ "bitcoin_hashes", + "hyper 0.14.32", + "hyper-rustls 0.24.2", "ldk-server-grpc", "prost", "reqwest 0.11.27", + "rustls 0.21.12", + "rustls-pemfile", + "tokio", ] [[package]] diff --git a/e2e-tests/Cargo.lock b/e2e-tests/Cargo.lock index a65d2c21..56bb272e 100644 --- a/e2e-tests/Cargo.lock +++ b/e2e-tests/Cargo.lock @@ -8,17 +8,6 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" -[[package]] -name = "aes" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", -] - [[package]] name = "ahash" version = "0.8.12" @@ -40,54 +29,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "amq-protocol" -version = "7.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "587d313f3a8b4a40f866cc84b6059fe83133bf172165ac3b583129dd211d8e1c" -dependencies = [ - "amq-protocol-tcp", - "amq-protocol-types", - "amq-protocol-uri", - "cookie-factory", - "nom", - "serde", -] - -[[package]] -name = "amq-protocol-tcp" -version = "7.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc707ab9aa964a85d9fc25908a3fdc486d2e619406883b3105b48bf304a8d606" -dependencies = [ - "amq-protocol-uri", - "tcp-stream", - "tracing", -] - -[[package]] -name = "amq-protocol-types" -version = "7.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf99351d92a161c61ec6ecb213bc7057f5b837dd4e64ba6cb6491358efd770c4" -dependencies = [ - "cookie-factory", - "nom", - "serde", - "serde_json", -] - -[[package]] -name = "amq-protocol-uri" -version = "7.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f89f8273826a676282208e5af38461a07fe939def57396af6ad5997fcf56577d" -dependencies = [ - "amq-protocol-types", - "percent-encoding", - "url", -] - [[package]] name = "android_system_properties" version = "0.1.5" @@ -109,172 +50,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" -[[package]] -name = "asn1-rs" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56624a96882bb8c26d61312ae18cb45868e5a9992ea73c58e45c3101e56a1e60" -dependencies = [ - "asn1-rs-derive", - "asn1-rs-impl", - "displaydoc", - "nom", - "num-traits", - "rusticata-macros", - "thiserror", - "time", -] - -[[package]] -name = "asn1-rs-derive" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", - "synstructure", -] - -[[package]] -name = "asn1-rs-impl" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "async-channel" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" -dependencies = [ - "concurrent-queue", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-executor" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" -dependencies = [ - "async-task", - "concurrent-queue", - "fastrand 2.4.1", - "futures-lite 2.6.1", - "pin-project-lite", - "slab", -] - -[[package]] -name = "async-global-executor" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13f937e26114b93193065fd44f507aa2e9169ad0cdabbb996920b1fe1ddea7ba" -dependencies = [ - "async-channel", - "async-executor", - "async-io 2.6.0", - "async-lock 3.4.2", - "blocking", - "futures-lite 2.6.1", -] - -[[package]] -name = "async-global-executor-trait" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9af57045d58eeb1f7060e7025a1631cbc6399e0a1d10ad6735b3d0ea7f8346ce" -dependencies = [ - "async-global-executor", - "async-trait", - "executor-trait", -] - -[[package]] -name = "async-io" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" -dependencies = [ - "async-lock 2.8.0", - "autocfg", - "cfg-if", - "concurrent-queue", - "futures-lite 1.13.0", - "log", - "parking", - "polling 2.8.0", - "rustix 0.37.28", - "slab", - "socket2 0.4.10", - "waker-fn", -] - -[[package]] -name = "async-io" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" -dependencies = [ - "autocfg", - "cfg-if", - "concurrent-queue", - "futures-io", - "futures-lite 2.6.1", - "parking", - "polling 3.11.0", - "rustix 1.1.4", - "slab", - "windows-sys 0.61.2", -] - -[[package]] -name = "async-lock" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" -dependencies = [ - "event-listener 2.5.3", -] - -[[package]] -name = "async-lock" -version = "3.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" -dependencies = [ - "event-listener 5.4.1", - "event-listener-strategy", - "pin-project-lite", -] - -[[package]] -name = "async-reactor-trait" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6012d170ad00de56c9ee354aef2e358359deb1ec504254e0e5a3774771de0e" -dependencies = [ - "async-io 1.13.0", - "async-trait", - "futures-core", - "reactor-trait", -] - -[[package]] -name = "async-task" -version = "4.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" - [[package]] name = "async-trait" version = "0.1.89" @@ -326,12 +101,6 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" -[[package]] -name = "base64ct" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" - [[package]] name = "bdk_chain" version = "0.23.3" @@ -515,37 +284,6 @@ dependencies = [ "webpki-roots 0.25.4", ] -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "block-padding" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" -dependencies = [ - "generic-array", -] - -[[package]] -name = "blocking" -version = "1.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" -dependencies = [ - "async-channel", - "async-task", - "futures-io", - "futures-lite 2.6.1", - "piper", -] - [[package]] name = "bumpalo" version = "3.20.2" @@ -587,15 +325,6 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "cbc" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" -dependencies = [ - "cipher", -] - [[package]] name = "cc" version = "1.2.59" @@ -636,49 +365,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", -] - -[[package]] -name = "cms" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b77c319abfd5219629c45c34c89ba945ed3c5e49fcde9d16b6c3885f118a730" -dependencies = [ - "const-oid", - "der", - "spki", - "x509-cert", -] - -[[package]] -name = "concurrent-queue" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "const-oid" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" - -[[package]] -name = "cookie-factory" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9885fa71e26b8ab7855e2ec7cae6e9b380edff76cd052e07c683a0319d51b3a2" - [[package]] name = "core-foundation" version = "0.9.4" @@ -739,15 +425,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "cpufeatures" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" -dependencies = [ - "libc", -] - [[package]] name = "crc32fast" version = "1.5.0" @@ -763,89 +440,6 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" -[[package]] -name = "crypto-common" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "data-encoding" -version = "2.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" - -[[package]] -name = "der" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" -dependencies = [ - "const-oid", - "der_derive", - "flagset", - "pem-rfc7468", - "zeroize", -] - -[[package]] -name = "der-parser" -version = "10.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6" -dependencies = [ - "asn1-rs", - "displaydoc", - "nom", - "num-bigint", - "num-traits", - "rusticata-macros", -] - -[[package]] -name = "der_derive" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "deranged" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" -dependencies = [ - "powerfmt", -] - -[[package]] -name = "des" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdd80ce8ce993de27e9f063a444a4d53ce8e8db4c1f00cc03af5ad5a9867a1e" -dependencies = [ - "cipher", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", - "subtle", -] - [[package]] name = "displaydoc" version = "0.2.5" @@ -867,24 +461,15 @@ dependencies = [ "tokio", ] -[[package]] -name = "doc-comment" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "780955b8b195a21ab8e4ac6b60dd1dbdcec1dc6c51c0617964b08c81785e12c9" - [[package]] name = "e2e-tests" version = "0.1.0" dependencies = [ "corepc-node", - "futures-util", "hex-conservative", - "lapin", "ldk-node", "ldk-server-client", "ldk-server-grpc", - "prost", "serde_json", "tempfile", "tokio", @@ -953,42 +538,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - -[[package]] -name = "event-listener" -version = "5.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener-strategy" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" -dependencies = [ - "event-listener 5.4.1", - "pin-project-lite", -] - -[[package]] -name = "executor-trait" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c39dff9342e4e0e16ce96be751eb21a94e94a87bb2f6e63ad1961c2ce109bf" -dependencies = [ - "async-trait", -] - [[package]] name = "fallible-iterator" version = "0.3.0" @@ -1001,15 +550,6 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" -[[package]] -name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] - [[package]] name = "fastrand" version = "2.4.1" @@ -1039,12 +579,6 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" -[[package]] -name = "flagset" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7ac824320a75a52197e8f2d787f6a38b6718bb6897a35142d749af3c0e8f4fe" - [[package]] name = "flate2" version = "1.1.9" @@ -1055,17 +589,6 @@ dependencies = [ "miniz_oxide", ] -[[package]] -name = "flume" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" -dependencies = [ - "futures-core", - "futures-sink", - "spin", -] - [[package]] name = "fnv" version = "1.0.7" @@ -1135,34 +658,6 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" -[[package]] -name = "futures-lite" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" -dependencies = [ - "fastrand 1.9.0", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] - -[[package]] -name = "futures-lite" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" -dependencies = [ - "fastrand 2.4.1", - "futures-core", - "futures-io", - "parking", - "pin-project-lite", -] - [[package]] name = "futures-macro" version = "0.3.32" @@ -1203,16 +698,6 @@ dependencies = [ "slab", ] -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - [[package]] name = "getrandom" version = "0.2.17" @@ -1324,24 +809,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - -[[package]] -name = "hermit-abi" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - [[package]] name = "hex-conservative" version = "0.2.2" @@ -1357,15 +824,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - [[package]] name = "home" version = "0.5.12" @@ -1685,36 +1143,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "inout" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" -dependencies = [ - "block-padding", - "generic-array", -] - -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi 0.3.9", - "libc", - "windows-sys 0.48.0", -] - [[package]] name = "ipnet" version = "2.12.0" @@ -1770,28 +1198,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "lapin" -version = "2.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02d2aa4725b9607915fa1a73e940710a3be6af508ce700e56897cbe8847fbb07" -dependencies = [ - "amq-protocol", - "async-global-executor-trait", - "async-reactor-trait", - "async-trait", - "executor-trait", - "flume", - "futures-core", - "futures-io", - "parking_lot", - "pinky-swear", - "reactor-trait", - "serde", - "tracing", - "waker-fn", -] - [[package]] name = "lazy_static" version = "1.5.0" @@ -1846,9 +1252,13 @@ name = "ldk-server-client" version = "0.1.0" dependencies = [ "bitcoin_hashes", + "hyper 0.14.32", + "hyper-rustls 0.24.2", "ldk-server-grpc", "prost", "reqwest 0.11.27", + "rustls 0.21.12", + "rustls-pemfile", ] [[package]] @@ -1891,7 +1301,7 @@ dependencies = [ "bitflags 2.11.0", "libc", "plain", - "redox_syscall 0.7.3", + "redox_syscall", ] [[package]] @@ -2037,12 +1447,6 @@ dependencies = [ "bitcoin", ] -[[package]] -name = "linux-raw-sys" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" - [[package]] name = "linux-raw-sys" version = "0.4.15" @@ -2061,15 +1465,6 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" -[[package]] -name = "lock_api" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" -dependencies = [ - "scopeguard", -] - [[package]] name = "log" version = "0.4.29" @@ -2094,12 +1489,6 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - [[package]] name = "miniscript" version = "12.3.5" @@ -2125,175 +1514,55 @@ dependencies = [ name = "minreq" version = "2.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05015102dad0f7d61691ca347e9d9d9006685a64aefb3d79eecf62665de2153d" -dependencies = [ - "rustls 0.21.12", - "rustls-webpki 0.101.7", - "serde", - "serde_json", - "webpki-roots 0.25.4", -] - -[[package]] -name = "mio" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" -dependencies = [ - "libc", - "wasi", - "windows-sys 0.61.2", -] - -[[package]] -name = "multimap" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" - -[[package]] -name = "musig2" -version = "0.1.0" -source = "git+https://github.com/arik-so/rust-musig2?rev=6f95a05718cbb44d8fe3fa6021aea8117aa38d50#6f95a05718cbb44d8fe3fa6021aea8117aa38d50" -dependencies = [ - "bitcoin", -] - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "num-bigint" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" -dependencies = [ - "num-integer", - "num-traits", -] - -[[package]] -name = "num-conv" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - -[[package]] -name = "oid-registry" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7" -dependencies = [ - "asn1-rs", -] - -[[package]] -name = "once_cell" -version = "1.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" - -[[package]] -name = "openssl-probe" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" - -[[package]] -name = "p12-keystore" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cae83056e7cb770211494a0ecf66d9fa7eba7d00977e5bb91f0e925b40b937f" -dependencies = [ - "cbc", - "cms", - "der", - "des", - "hex", - "hmac", - "pkcs12", - "pkcs5", - "rand 0.9.2", - "rc2", - "sha1", - "sha2", - "thiserror", - "x509-parser", -] - -[[package]] -name = "parking" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" - -[[package]] -name = "parking_lot" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +checksum = "05015102dad0f7d61691ca347e9d9d9006685a64aefb3d79eecf62665de2153d" dependencies = [ - "lock_api", - "parking_lot_core", + "rustls 0.21.12", + "rustls-webpki 0.101.7", + "serde", + "serde_json", + "webpki-roots 0.25.4", ] [[package]] -name = "parking_lot_core" -version = "0.9.12" +name = "mio" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" dependencies = [ - "cfg-if", "libc", - "redox_syscall 0.5.18", - "smallvec", - "windows-link", + "wasi", + "windows-sys 0.61.2", ] [[package]] -name = "pbkdf2" -version = "0.12.2" +name = "multimap" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" + +[[package]] +name = "musig2" +version = "0.1.0" +source = "git+https://github.com/arik-so/rust-musig2?rev=6f95a05718cbb44d8fe3fa6021aea8117aa38d50#6f95a05718cbb44d8fe3fa6021aea8117aa38d50" dependencies = [ - "digest", - "hmac", + "bitcoin", ] [[package]] -name = "pem-rfc7468" -version = "0.7.0" +name = "num-traits" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ - "base64ct", + "autocfg", ] +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + [[package]] name = "percent-encoding" version = "2.3.2" @@ -2322,59 +1591,6 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" -[[package]] -name = "pinky-swear" -version = "6.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1ea6e230dd3a64d61bcb8b79e597d3ab6b4c94ec7a234ce687dd718b4f2e657" -dependencies = [ - "doc-comment", - "flume", - "parking_lot", - "tracing", -] - -[[package]] -name = "piper" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" -dependencies = [ - "atomic-waker", - "fastrand 2.4.1", - "futures-io", -] - -[[package]] -name = "pkcs12" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "695b3df3d3cc1015f12d70235e35b6b79befc5fa7a9b95b951eab1dd07c9efc2" -dependencies = [ - "cms", - "const-oid", - "der", - "digest", - "spki", - "x509-cert", - "zeroize", -] - -[[package]] -name = "pkcs5" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e847e2c91a18bfa887dd028ec33f2fe6f25db77db3619024764914affe8b69a6" -dependencies = [ - "aes", - "cbc", - "der", - "pbkdf2", - "scrypt", - "sha2", - "spki", -] - [[package]] name = "pkg-config" version = "0.3.32" @@ -2387,36 +1603,6 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" -[[package]] -name = "polling" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" -dependencies = [ - "autocfg", - "bitflags 1.3.2", - "cfg-if", - "concurrent-queue", - "libc", - "log", - "pin-project-lite", - "windows-sys 0.48.0", -] - -[[package]] -name = "polling" -version = "3.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" -dependencies = [ - "cfg-if", - "concurrent-queue", - "hermit-abi 0.5.2", - "pin-project-lite", - "rustix 1.1.4", - "windows-sys 0.61.2", -] - [[package]] name = "possiblyrandom" version = "0.2.0" @@ -2434,12 +1620,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - [[package]] name = "ppv-lite86" version = "0.2.21" @@ -2667,35 +1847,6 @@ dependencies = [ "getrandom 0.3.4", ] -[[package]] -name = "rc2" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62c64daa8e9438b84aaae55010a93f396f8e60e3911590fcba770d04643fc1dd" -dependencies = [ - "cipher", -] - -[[package]] -name = "reactor-trait" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "438a4293e4d097556730f4711998189416232f009c137389e0f961d2bc0ddc58" -dependencies = [ - "async-trait", - "futures-core", - "futures-io", -] - -[[package]] -name = "redox_syscall" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags 2.11.0", -] - [[package]] name = "redox_syscall" version = "0.7.3" @@ -2758,7 +1909,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "rustls 0.21.12", - "rustls-pemfile 1.0.4", + "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", @@ -2847,29 +1998,6 @@ version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" -[[package]] -name = "rusticata-macros" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" -dependencies = [ - "nom", -] - -[[package]] -name = "rustix" -version = "0.37.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "519165d378b97752ca44bbe15047d5d3409e875f39327546b42ac81d7e18c1b6" -dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys 0.3.8", - "windows-sys 0.48.0", -] - [[package]] name = "rustix" version = "0.38.44" @@ -2923,32 +2051,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rustls-connector" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70cc376c6ba1823ae229bacf8ad93c136d93524eab0e4e5e0e4f96b9c4e5b212" -dependencies = [ - "log", - "rustls 0.23.37", - "rustls-native-certs", - "rustls-pki-types", - "rustls-webpki 0.103.10", -] - -[[package]] -name = "rustls-native-certs" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" -dependencies = [ - "openssl-probe", - "rustls-pemfile 2.2.0", - "rustls-pki-types", - "schannel", - "security-framework", -] - [[package]] name = "rustls-pemfile" version = "1.0.4" @@ -2958,15 +2060,6 @@ dependencies = [ "base64 0.21.7", ] -[[package]] -name = "rustls-pemfile" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" -dependencies = [ - "rustls-pki-types", -] - [[package]] name = "rustls-pki-types" version = "1.14.0" @@ -3010,41 +2103,6 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" -[[package]] -name = "salsa20" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" -dependencies = [ - "cipher", -] - -[[package]] -name = "schannel" -version = "0.1.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "scrypt" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" -dependencies = [ - "pbkdf2", - "salsa20", - "sha2", -] - [[package]] name = "sct" version = "0.7.1" @@ -3076,29 +2134,6 @@ dependencies = [ "cc", ] -[[package]] -name = "security-framework" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" -dependencies = [ - "bitflags 2.11.0", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "semver" version = "1.0.28" @@ -3160,28 +2195,6 @@ dependencies = [ "serde", ] -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha2" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - [[package]] name = "shlex" version = "1.3.0" @@ -3206,16 +2219,6 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" -[[package]] -name = "socket2" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "socket2" version = "0.5.10" @@ -3236,25 +2239,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] - -[[package]] -name = "spki" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" -dependencies = [ - "base64ct", - "der", -] - [[package]] name = "stable_deref_trait" version = "1.2.1" @@ -3347,25 +2331,13 @@ dependencies = [ "xattr", ] -[[package]] -name = "tcp-stream" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "495b0abdce3dc1f8fd27240651c9e68890c14e9d9c61527b1ce44d8a5a7bd3d5" -dependencies = [ - "cfg-if", - "p12-keystore", - "rustls-connector", - "rustls-pemfile 2.2.0", -] - [[package]] name = "tempfile" version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ - "fastrand 2.4.1", + "fastrand", "getrandom 0.4.2", "once_cell", "rustix 1.1.4", @@ -3392,37 +2364,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "time" -version = "0.3.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" -dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde_core", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" - -[[package]] -name = "time-macros" -version = "0.2.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" -dependencies = [ - "num-conv", - "time-core", -] - [[package]] name = "tinystr" version = "0.8.3" @@ -3577,12 +2518,6 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" -[[package]] -name = "typenum" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" - [[package]] name = "unicode-ident" version = "1.0.24" @@ -3661,12 +2596,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "waker-fn" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" - [[package]] name = "want" version = "0.3.1" @@ -4261,34 +3190,6 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" -[[package]] -name = "x509-cert" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1301e935010a701ae5f8655edc0ad17c44bad3ac5ce8c39185f75453b720ae94" -dependencies = [ - "const-oid", - "der", - "spki", -] - -[[package]] -name = "x509-parser" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4569f339c0c402346d4a75a9e39cf8dad310e287eef1ff56d4c68e5067f53460" -dependencies = [ - "asn1-rs", - "data-encoding", - "der-parser", - "lazy_static", - "nom", - "oid-registry", - "rusticata-macros", - "thiserror", - "time", -] - [[package]] name = "xattr" version = "1.6.1" diff --git a/e2e-tests/Cargo.toml b/e2e-tests/Cargo.toml index 6b070470..bfc07941 100644 --- a/e2e-tests/Cargo.toml +++ b/e2e-tests/Cargo.toml @@ -11,7 +11,4 @@ ldk-server-client = { path = "../ldk-server-client" } ldk-server-grpc = { path = "../ldk-server-grpc", features = ["serde"] } 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 = "3aef2b39265ae60b29f4d60de8291895f12eb880" } diff --git a/e2e-tests/build.rs b/e2e-tests/build.rs index ac995049..b8841d3d 100644 --- a/e2e-tests/build.rs +++ b/e2e-tests/build.rs @@ -21,7 +21,7 @@ fn main() { "-p", "ldk-server", "--features", - "events-rabbitmq,experimental-lsps2-support", + "experimental-lsps2-support", "-p", "ldk-server-cli", ]) diff --git a/e2e-tests/src/lib.rs b/e2e-tests/src/lib.rs index eef1d2bc..b1d85824 100644 --- a/e2e-tests/src/lib.rs +++ b/e2e-tests/src/lib.rs @@ -87,26 +87,20 @@ impl TestBitcoind { /// Handle to a running ldk-server child process. pub struct LdkServerHandle { child: Option, - pub rest_port: u16, + pub grpc_port: u16, pub p2p_port: u16, pub storage_dir: PathBuf, pub api_key: String, pub tls_cert_path: PathBuf, pub node_id: String, - pub exchange_name: String, client: LdkServerClient, } +#[derive(Default)] pub struct LdkServerConfig { pub metrics_auth: Option<(String, String)>, } -impl Default for LdkServerConfig { - fn default() -> Self { - Self { metrics_auth: None } - } -} - impl LdkServerHandle { /// Starts a new ldk-server instance against the given bitcoind. /// Waits until the server is ready to accept requests. @@ -117,14 +111,12 @@ impl LdkServerHandle { pub async fn start_with_config(bitcoind: &TestBitcoind, config: LdkServerConfig) -> Self { #[allow(deprecated)] let storage_dir = tempfile::tempdir().unwrap().into_path(); - let rest_port = find_available_port(); + let grpc_port = find_available_port(); let p2p_port = find_available_port(); let (rpc_host, rpc_port_num, rpc_user, rpc_password) = bitcoind.rpc_details(); let rpc_address = format!("{rpc_host}:{rpc_port_num}"); - let exchange_name = format!("e2e_test_exchange_{rest_port}"); - let metrics_auth_config = if let Some((user, pass)) = config.metrics_auth { format!("username = \"{}\"\npassword = \"{}\"", user, pass) } else { @@ -135,7 +127,7 @@ impl LdkServerHandle { r#"[node] network = "regtest" listening_addresses = ["127.0.0.1:{p2p_port}"] -rest_service_address = "127.0.0.1:{rest_port}" +grpc_service_address = "127.0.0.1:{grpc_port}" alias = "e2e-test-node" [storage.disk] @@ -146,10 +138,6 @@ rpc_address = "{rpc_address}" rpc_user = "{rpc_user}" rpc_password = "{rpc_password}" -[rabbitmq] -connection_string = "amqp://guest:guest@localhost:5672/%2f" -exchange_name = "{exchange_name}" - [liquidity.lsps2_service] advertise_service = false channel_opening_fee_ppm = 10000 @@ -216,18 +204,17 @@ poll_metrics_interval = 1 // Read TLS cert let tls_cert_pem = std::fs::read(&tls_cert_path).unwrap(); - let base_url = format!("127.0.0.1:{rest_port}"); + let base_url = format!("127.0.0.1:{grpc_port}"); let client = LdkServerClient::new(base_url, api_key.clone(), &tls_cert_pem).unwrap(); let mut handle = Self { child: Some(child), - rest_port, + grpc_port, p2p_port, storage_dir, api_key, tls_cert_path, node_id: String::new(), - exchange_name, client, }; @@ -247,7 +234,7 @@ poll_metrics_interval = 1 } pub fn base_url(&self) -> String { - format!("127.0.0.1:{}", self.rest_port) + format!("127.0.0.1:{}", self.grpc_port) } } @@ -452,101 +439,3 @@ pub async fn setup_funded_channel( open_resp.user_channel_id } - -/// RabbitMQ event consumer for verifying events published by ldk-server. -pub struct RabbitMqEventConsumer { - _connection: lapin::Connection, - channel: lapin::Channel, - consumer: lapin::Consumer, -} - -impl RabbitMqEventConsumer { - /// Connect to RabbitMQ and create an exclusive queue bound to the given exchange. - pub async fn new(exchange_name: &str) -> Self { - use lapin::options::{ - BasicConsumeOptions, ExchangeDeclareOptions, QueueBindOptions, QueueDeclareOptions, - }; - use lapin::types::FieldTable; - use lapin::{ConnectionProperties, ExchangeKind}; - - let connection = lapin::Connection::connect( - "amqp://guest:guest@localhost:5672/%2f", - ConnectionProperties::default(), - ) - .await - .expect("Failed to connect to RabbitMQ"); - - let channel = connection.create_channel().await.expect("Failed to create channel"); - - // Declare exchange (idempotent — may already exist from the server) - channel - .exchange_declare( - exchange_name, - ExchangeKind::Fanout, - ExchangeDeclareOptions { durable: true, ..Default::default() }, - FieldTable::default(), - ) - .await - .expect("Failed to declare exchange"); - - // Create exclusive auto-delete queue with server-generated name - let queue = channel - .queue_declare( - "", - QueueDeclareOptions { exclusive: true, auto_delete: true, ..Default::default() }, - FieldTable::default(), - ) - .await - .expect("Failed to declare queue"); - let queue_name = queue.name().to_string(); - - channel - .queue_bind( - &queue_name, - exchange_name, - "", - QueueBindOptions::default(), - FieldTable::default(), - ) - .await - .expect("Failed to bind queue"); - - let consumer = channel - .basic_consume( - &queue_name, - &format!("consumer_{}", queue_name), - BasicConsumeOptions::default(), - FieldTable::default(), - ) - .await - .expect("Failed to start consumer"); - - Self { _connection: connection, channel, consumer } - } - - /// Consume up to `count` events, waiting up to `timeout` for each. - pub async fn consume_events( - &mut self, count: usize, timeout: Duration, - ) -> Vec { - use futures_util::StreamExt; - use lapin::options::BasicAckOptions; - 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"); - self.channel - .basic_ack(delivery.delivery_tag, BasicAckOptions::default()) - .await - .expect("Failed to ack"); - events.push(event); - }, - _ => break, - } - } - events - } -} diff --git a/e2e-tests/tests/e2e.rs b/e2e-tests/tests/e2e.rs index adbe6c07..8d0ef7c4 100644 --- a/e2e-tests/tests/e2e.rs +++ b/e2e-tests/tests/e2e.rs @@ -12,21 +12,37 @@ use std::time::Duration; use e2e_tests::{ find_available_port, mine_and_sync, run_cli, run_cli_raw, setup_funded_channel, - wait_for_onchain_balance, LdkServerConfig, LdkServerHandle, RabbitMqEventConsumer, - TestBitcoind, + wait_for_onchain_balance, LdkServerConfig, LdkServerHandle, TestBitcoind, }; use hex_conservative::{DisplayHex, FromHex}; use ldk_node::bitcoin::hashes::{sha256, Hash}; use ldk_node::lightning::ln::msgs::SocketAddress; use ldk_node::lightning::offers::offer::Offer; use ldk_node::lightning_invoice::Bolt11Invoice; +use ldk_server_client::client::EventStream; use ldk_server_client::ldk_server_grpc::api::{ Bolt11ReceiveRequest, Bolt12ReceiveRequest, OnchainReceiveRequest, }; +use ldk_server_client::ldk_server_grpc::events::event_envelope::Event; +use ldk_server_client::ldk_server_grpc::events::EventEnvelope; use ldk_server_client::ldk_server_grpc::types::{ bolt11_invoice_description, Bolt11InvoiceDescription, }; -use ldk_server_grpc::events::event_envelope::Event; + +const EVENT_TIMEOUT: Duration = Duration::from_secs(15); + +async fn wait_for_event(events: &mut EventStream, pred: impl Fn(&Event) -> bool) -> EventEnvelope { + tokio::time::timeout(EVENT_TIMEOUT, async { + while let Some(Ok(ev)) = events.next_message().await { + if ev.event.as_ref().is_some_and(&pred) { + return ev; + } + } + panic!("Event stream ended without matching event"); + }) + .await + .expect("Timed out waiting for event") +} #[tokio::test] async fn test_cli_get_node_info() { @@ -216,31 +232,21 @@ async fn test_cli_decode_invoice() { // serde_json escapes control chars (U+0000–U+001F) as \uXXXX in JSON. let desc_with_ansi = "pay me\x1b[31m RED \x1b[0m"; let output_ansi = run_cli(&server, &["bolt11-receive", "-d", desc_with_ansi]); - let raw_decoded = run_cli_raw( - &server, - &["decode-invoice", output_ansi["invoice"].as_str().unwrap()], - ); - assert!( - !raw_decoded.contains('\x1b'), - "Raw CLI output must not contain ANSI escape bytes" - ); + let raw_decoded = + run_cli_raw(&server, &["decode-invoice", output_ansi["invoice"].as_str().unwrap()]); + assert!(!raw_decoded.contains('\x1b'), "Raw CLI output must not contain ANSI escape bytes"); // Test that Unicode bidi override characters in the description are escaped // (sanitize_for_terminal replaces them with \uXXXX in CLI output) let desc_with_bidi = "pay me\u{202E}evil"; let output_bidi = run_cli(&server, &["bolt11-receive", "-d", desc_with_bidi]); - let raw_bidi = run_cli_raw( - &server, - &["decode-invoice", output_bidi["invoice"].as_str().unwrap()], - ); + let raw_bidi = + run_cli_raw(&server, &["decode-invoice", output_bidi["invoice"].as_str().unwrap()]); assert!( !raw_bidi.contains('\u{202E}'), "Raw CLI output must not contain bidi override characters" ); - assert!( - raw_bidi.contains("\\u202E"), - "Bidi characters should be escaped as \\uXXXX in output" - ); + assert!(raw_bidi.contains("\\u202E"), "Bidi characters should be escaped as \\uXXXX in output"); } #[tokio::test] @@ -308,30 +314,20 @@ async fn test_cli_decode_offer() { // Test that ANSI escape sequences cannot reach the terminal via CLI output. let desc_with_ansi = "offer\x1b[31m RED \x1b[0m"; let output_ansi = run_cli(&server_a, &["bolt12-receive", desc_with_ansi]); - let raw_decoded = run_cli_raw( - &server_a, - &["decode-offer", output_ansi["offer"].as_str().unwrap()], - ); - assert!( - !raw_decoded.contains('\x1b'), - "Raw CLI output must not contain ANSI escape bytes" - ); + let raw_decoded = + run_cli_raw(&server_a, &["decode-offer", output_ansi["offer"].as_str().unwrap()]); + assert!(!raw_decoded.contains('\x1b'), "Raw CLI output must not contain ANSI escape bytes"); // Test that Unicode bidi override characters in the description are escaped let desc_with_bidi = "offer\u{202E}evil"; let output_bidi = run_cli(&server_a, &["bolt12-receive", desc_with_bidi]); - let raw_bidi = run_cli_raw( - &server_a, - &["decode-offer", output_bidi["offer"].as_str().unwrap()], - ); + let raw_bidi = + run_cli_raw(&server_a, &["decode-offer", output_bidi["offer"].as_str().unwrap()]); assert!( !raw_bidi.contains('\u{202E}'), "Raw CLI output must not contain bidi override characters" ); - assert!( - raw_bidi.contains("\\u202E"), - "Bidi characters should be escaped as \\uXXXX in output" - ); + assert!(raw_bidi.contains("\\u202E"), "Bidi characters should be escaped as \\uXXXX in output"); } #[tokio::test] @@ -447,15 +443,15 @@ async fn test_cli_update_channel_config() { assert!(output.is_object()); } -#[tokio::test] +#[tokio::test(flavor = "multi_thread", worker_threads = 1)] async fn test_cli_bolt11_send() { let bitcoind = TestBitcoind::new(); let server_a = LdkServerHandle::start(&bitcoind).await; let server_b = LdkServerHandle::start(&bitcoind).await; - // Set up event consumers before any payments - let mut consumer_a = RabbitMqEventConsumer::new(&server_a.exchange_name).await; - let mut consumer_b = RabbitMqEventConsumer::new(&server_b.exchange_name).await; + // Subscribe to events before the payment + let mut events_a = server_a.client().subscribe_events().await.unwrap(); + let mut events_b = server_b.client().subscribe_events().await.unwrap(); setup_funded_channel(&bitcoind, &server_a, &server_b, 100_000).await; @@ -477,19 +473,11 @@ async fn test_cli_bolt11_send() { assert!(!output["payment_id"].as_str().unwrap().is_empty()); // Verify events - tokio::time::sleep(Duration::from_secs(5)).await; + let event_a = wait_for_event(&mut events_a, |e| matches!(e, Event::PaymentSuccessful(_))).await; + assert!(matches!(&event_a.event, Some(Event::PaymentSuccessful(_)))); - 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(_)))), - "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(_)))), - "Expected PaymentReceived on receiver" - ); + let event_b = wait_for_event(&mut events_b, |e| matches!(e, Event::PaymentReceived(_))).await; + assert!(matches!(&event_b.event, Some(Event::PaymentReceived(_)))); } #[tokio::test] @@ -553,14 +541,14 @@ async fn test_cli_bolt12_send() { assert!(!output["payment_id"].as_str().unwrap().is_empty()); } -#[tokio::test] +#[tokio::test(flavor = "multi_thread", worker_threads = 1)] async fn test_cli_spontaneous_send() { let bitcoind = TestBitcoind::new(); let server_a = LdkServerHandle::start(&bitcoind).await; let server_b = LdkServerHandle::start(&bitcoind).await; - let mut consumer_a = RabbitMqEventConsumer::new(&server_a.exchange_name).await; - let mut consumer_b = RabbitMqEventConsumer::new(&server_b.exchange_name).await; + let mut events_a = server_a.client().subscribe_events().await.unwrap(); + let mut events_b = server_b.client().subscribe_events().await.unwrap(); setup_funded_channel(&bitcoind, &server_a, &server_b, 100_000).await; @@ -568,19 +556,11 @@ async fn test_cli_spontaneous_send() { assert!(!output["payment_id"].as_str().unwrap().is_empty()); // Verify events - tokio::time::sleep(Duration::from_secs(5)).await; - - 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(_)))), - "Expected PaymentSuccessful on sender" - ); + let event_a = wait_for_event(&mut events_a, |e| matches!(e, Event::PaymentSuccessful(_))).await; + assert!(matches!(&event_a.event, Some(Event::PaymentSuccessful(_)))); - 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(_)))), - "Expected PaymentReceived on receiver" - ); + let event_b = wait_for_event(&mut events_b, |e| matches!(e, Event::PaymentReceived(_))).await; + assert!(matches!(&event_b.event, Some(Event::PaymentReceived(_)))); } #[tokio::test] @@ -783,8 +763,8 @@ async fn test_forwarded_payment_event() { // B: LSP node (all e2e servers include LSPS2 service config) let server_b = LdkServerHandle::start(&bitcoind).await; - // Set up RabbitMQ consumer on B before any payments - let mut consumer_b = RabbitMqEventConsumer::new(&server_b.exchange_name).await; + // Subscribe to events on B before any payments + let mut events_b = server_b.client().subscribe_events().await.unwrap(); // Open channel A -> B (1M sats, larger for JIT forwarding) setup_funded_channel(&bitcoind, &server_a, &server_b, 1_000_000).await; @@ -822,7 +802,6 @@ async fn test_forwarded_payment_event() { let node_c = builder_c.build(node_entropy_c).unwrap(); node_c.start().unwrap(); - node_c.sync_wallets().unwrap(); // C generates JIT invoice via LSPS2 @@ -838,31 +817,40 @@ async fn test_forwarded_payment_event() { run_cli(&server_a, &["bolt11-send", &jit_invoice.to_string()]); // Wait for payment processing and JIT channel open - tokio::time::sleep(Duration::from_secs(15)).await; + tokio::time::sleep(Duration::from_secs(10)).await; // Mine blocks to confirm JIT channel mine_and_sync(&bitcoind, &[&server_a, &server_b], 6).await; - tokio::time::sleep(Duration::from_secs(5)).await; + tokio::time::sleep(Duration::from_secs(10)).await; - // 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::>() - ); + // Verify PaymentForwarded event on B (drain other events) + let forwarded = tokio::time::timeout(Duration::from_secs(30), async { + loop { + match events_b.next_message().await { + Some(Ok(ev)) if matches!(&ev.event, Some(Event::PaymentForwarded(_))) => { + return ev; + }, + Some(Ok(_)) => continue, // drain non-matching events + Some(Err(e)) => panic!("Error reading event stream: {e}"), + None => panic!("Event stream ended without PaymentForwarded"), + } + } + }) + .await + .expect("Timed out waiting for PaymentForwarded event on LSP node B"); + assert!(matches!(&forwarded.event, Some(Event::PaymentForwarded(_)))); node_c.stop().unwrap(); } -#[tokio::test] +#[tokio::test(flavor = "multi_thread", worker_threads = 1)] async fn test_hodl_invoice_claim() { let bitcoind = TestBitcoind::new(); let server_a = LdkServerHandle::start(&bitcoind).await; let server_b = LdkServerHandle::start(&bitcoind).await; - let mut consumer_a = RabbitMqEventConsumer::new(&server_a.exchange_name).await; - let mut consumer_b = RabbitMqEventConsumer::new(&server_b.exchange_name).await; + let mut events_a = server_a.client().subscribe_events().await.unwrap(); + let mut events_b = server_b.client().subscribe_events().await.unwrap(); setup_funded_channel(&bitcoind, &server_a, &server_b, 100_000).await; @@ -897,13 +885,10 @@ async fn test_hodl_invoice_claim() { // Pay the hodl invoice from A run_cli(&server_a, &["bolt11-send", invoice]); - // 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::>() - ); + // Wait for PaymentClaimable event on B (drain other events) + let claimable = + wait_for_event(&mut events_b, |e| matches!(e, Event::PaymentClaimable(_))).await; + assert!(matches!(&claimable.event, Some(Event::PaymentClaimable(_)))); // Claim the payment on B let mut args: Vec<&str> = vec!["bolt11-claim-for-hash", &preimage_hex]; @@ -915,36 +900,21 @@ async fn test_hodl_invoice_claim() { } run_cli(&server_b, &args); - // 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::>() - ); - - // 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::>() - ); + // Wait for PaymentSuccessful on A after claim (drain other events) + let successful = + wait_for_event(&mut events_a, |e| matches!(e, Event::PaymentSuccessful(_))).await; + assert!(matches!(&successful.event, Some(Event::PaymentSuccessful(_)))); } } -#[tokio::test] +#[tokio::test(flavor = "multi_thread", worker_threads = 1)] 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; - // Set up event consumers before any payments - let mut consumer_a = RabbitMqEventConsumer::new(&server_a.exchange_name).await; - let mut consumer_b = RabbitMqEventConsumer::new(&server_b.exchange_name).await; + let mut events_a = server_a.client().subscribe_events().await.unwrap(); + let mut events_b = server_b.client().subscribe_events().await.unwrap(); setup_funded_channel(&bitcoind, &server_a, &server_b, 100_000).await; @@ -971,30 +941,16 @@ async fn test_hodl_invoice_fail() { // Pay the hodl invoice from A run_cli(&server_a, &["bolt11-send", invoice]); - // Wait for payment to arrive at B - tokio::time::sleep(Duration::from_secs(5)).await; - // 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::>() - ); + let event_b = wait_for_event(&mut events_b, |e| matches!(e, Event::PaymentClaimable(_))).await; + assert!(matches!(&event_b.event, Some(Event::PaymentClaimable(_)))); // Fail the payment on B using CLI run_cli(&server_b, &["bolt11-fail-for-hash", &payment_hash_hex]); - // Wait for failure to propagate - tokio::time::sleep(Duration::from_secs(5)).await; - // 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::>() - ); + let event_a = wait_for_event(&mut events_a, |e| matches!(e, Event::PaymentFailed(_))).await; + assert!(matches!(&event_a.event, Some(Event::PaymentFailed(_)))); } #[tokio::test] diff --git a/ldk-server-client/Cargo.toml b/ldk-server-client/Cargo.toml index ed2a61d5..9768ac52 100644 --- a/ldk-server-client/Cargo.toml +++ b/ldk-server-client/Cargo.toml @@ -12,3 +12,10 @@ ldk-server-grpc = { path = "../ldk-server-grpc" } reqwest = { version = "0.11.13", default-features = false, features = ["rustls-tls"] } prost = { version = "0.11.6", default-features = false, features = ["std", "prost-derive"] } bitcoin_hashes = "0.14" +hyper = { version = "0.14", default-features = false, features = ["client", "http2", "runtime", "tcp"] } +hyper-rustls = { version = "0.24", default-features = false, features = ["http2", "tls12", "tokio-runtime"] } +rustls = "0.21" +rustls-pemfile = "1" + +[dev-dependencies] +tokio = { version = "1", default-features = false, features = ["macros", "rt"] } diff --git a/ldk-server-client/src/client.rs b/ldk-server-client/src/client.rs index daa06394..df5cba7d 100644 --- a/ldk-server-client/src/client.rs +++ b/ldk-server-client/src/client.rs @@ -7,10 +7,15 @@ // You may not use this file except in accordance with one or both of these // licenses. +use std::io::Cursor; use std::time::{SystemTime, UNIX_EPOCH}; use bitcoin_hashes::hmac::{Hmac, HmacEngine}; use bitcoin_hashes::{sha256, Hash, HashEngine}; +use hyper::body::HttpBody as _; +use hyper::{Body as HyperBody, Client as HyperClient, Request as HyperRequest, Version}; +use hyper_rustls::{HttpsConnector, HttpsConnectorBuilder}; +use ldk_server_grpc::api::SubscribeEventsRequest; use ldk_server_grpc::api::{ Bolt11ClaimForHashRequest, Bolt11ClaimForHashResponse, Bolt11FailForHashRequest, Bolt11FailForHashResponse, Bolt11ReceiveForHashRequest, Bolt11ReceiveForHashResponse, @@ -44,18 +49,23 @@ use ldk_server_grpc::endpoints::{ GRAPH_GET_CHANNEL_PATH, GRAPH_GET_NODE_PATH, GRAPH_LIST_CHANNELS_PATH, GRAPH_LIST_NODES_PATH, GRPC_SERVICE_PREFIX, LIST_CHANNELS_PATH, LIST_FORWARDED_PAYMENTS_PATH, LIST_PAYMENTS_PATH, LIST_PEERS_PATH, ONCHAIN_RECEIVE_PATH, ONCHAIN_SEND_PATH, OPEN_CHANNEL_PATH, SIGN_MESSAGE_PATH, - SPLICE_IN_PATH, SPLICE_OUT_PATH, SPONTANEOUS_SEND_PATH, UNIFIED_SEND_PATH, - UPDATE_CHANNEL_CONFIG_PATH, VERIFY_SIGNATURE_PATH, + SPLICE_IN_PATH, SPLICE_OUT_PATH, SPONTANEOUS_SEND_PATH, SUBSCRIBE_EVENTS_PATH, + UNIFIED_SEND_PATH, UPDATE_CHANNEL_CONFIG_PATH, VERIFY_SIGNATURE_PATH, }; +use ldk_server_grpc::events::EventEnvelope; use ldk_server_grpc::grpc::{decode_grpc_body, encode_grpc_frame, percent_decode}; use prost::Message; -use reqwest::{Certificate, Client}; +use reqwest::{header::HeaderMap, Certificate, Client}; +use rustls::{ClientConfig, RootCertStore}; +use rustls_pemfile::certs; use crate::error::LdkServerError; use crate::error::LdkServerErrorCode::{ AuthError, InternalError, InternalServerError, InvalidRequestError, LightningError, }; +type StreamingClient = HyperClient, HyperBody>; + /// Client to access a hosted instance of LDK Server via gRPC. /// /// The client requires the server's TLS certificate to be provided for verification. @@ -65,6 +75,7 @@ use crate::error::LdkServerErrorCode::{ pub struct LdkServerClient { base_url: String, client: Client, + streaming_client: StreamingClient, api_key: String, } @@ -78,13 +89,14 @@ impl LdkServerClient { pub fn new(base_url: String, api_key: String, server_cert_pem: &[u8]) -> Result { let cert = Certificate::from_pem(server_cert_pem) .map_err(|e| format!("Failed to parse server certificate: {e}"))?; + let streaming_client = build_streaming_client(server_cert_pem)?; let client = Client::builder() .add_root_certificate(cert) .build() .map_err(|e| format!("Failed to build HTTP client: {e}"))?; - Ok(Self { base_url, client, api_key }) + Ok(Self { base_url, client, streaming_client, api_key }) } /// Computes the HMAC-SHA256 authentication header value. @@ -397,6 +409,13 @@ impl LdkServerClient { self.grpc_unary(&request, GRAPH_GET_NODE_PATH).await } + /// Subscribe to a stream of server events via server-streaming gRPC. + /// + /// Returns an [`EventStream`] that yields [`EventEnvelope`] messages as they arrive. + pub async fn subscribe_events(&self) -> Result { + self.grpc_server_streaming(&SubscribeEventsRequest {}, SUBSCRIBE_EVENTS_PATH).await + } + /// Send a unary gRPC request and decode the response. async fn grpc_unary( &self, request: &Rq, method: &str, @@ -422,20 +441,8 @@ impl LdkServerClient { // Check for Trailers-Only error responses (grpc-status in response headers). // In gRPC, when there is no response body (error case), the server sends // grpc-status as part of the initial HEADERS frame, readable as a regular header. - if let Some(status_val) = response.headers().get("grpc-status") { - if let Ok(status_str) = status_val.to_str() { - if let Ok(code) = status_str.parse::() { - if code != 0 { - let message = response - .headers() - .get("grpc-message") - .and_then(|v| v.to_str().ok()) - .map(percent_decode) - .unwrap_or_default(); - return Err(grpc_code_to_error(code, message)); - } - } - } + if let Some(error) = grpc_error_from_headers(response.headers()) { + return Err(error); } // Read the response body @@ -450,23 +457,255 @@ impl LdkServerClient { LdkServerError::new(InternalError, format!("Failed to decode gRPC response: {}", e)) }) } + + /// Open a server-streaming gRPC call and return a [`GrpcStream`] that + /// yields decoded messages of type `Rs` as they arrive. + async fn grpc_server_streaming( + &self, request: &Rq, method: &str, + ) -> Result, LdkServerError> { + let grpc_body = encode_grpc_frame(&request.encode_to_vec()).to_vec(); + + let url = format!("https://{}{}{}", self.base_url, GRPC_SERVICE_PREFIX, method); + let auth_header = self.compute_auth_header(); + + let response = self + .streaming_client + .request( + HyperRequest::post(&url) + .version(Version::HTTP_2) + .header("content-type", "application/grpc+proto") + .header("te", "trailers") + .header("x-auth", auth_header) + .body(HyperBody::from(grpc_body)) + .map_err(|e| { + LdkServerError::new( + InternalError, + format!("Failed to build gRPC request: {e}"), + ) + })?, + ) + .await + .map_err(|e| { + LdkServerError::new(InternalError, format!("gRPC request failed: {}", e)) + })?; + + let (parts, body) = response.into_parts(); + if let Some(error) = grpc_error_from_headers(&parts.headers) { + return Err(error); + } + + Ok(GrpcStream { + body, + buf: Vec::new(), + trailers_checked: false, + _marker: std::marker::PhantomData, + }) + } } /// Map a gRPC status code to an LdkServerError. fn grpc_code_to_error(code: u32, message: String) -> LdkServerError { - let error_code = match code { - 3 => InvalidRequestError, // INVALID_ARGUMENT - 16 => AuthError, // UNAUTHENTICATED - 9 => LightningError, // FAILED_PRECONDITION - 13 => InternalServerError, // INTERNAL - _ => InternalError, - }; - LdkServerError::new(error_code, message) + match code { + 3 => LdkServerError::new(InvalidRequestError, message), // INVALID_ARGUMENT + 9 => LdkServerError::new(LightningError, message), // FAILED_PRECONDITION + 13 => LdkServerError::new(InternalServerError, message), // INTERNAL + 14 => LdkServerError::new( + InternalError, + if message.is_empty() { + "gRPC stream became unavailable".to_string() + } else { + format!("gRPC stream became unavailable: {message}") + }, + ), + 16 => LdkServerError::new(AuthError, message), // UNAUTHENTICATED + _ => LdkServerError::new( + InternalError, + if message.is_empty() { + format!("gRPC status {code}") + } else { + format!("gRPC status {code}: {message}") + }, + ), + } +} + +fn grpc_error_from_headers(headers: &HeaderMap) -> Option { + let code = headers.get("grpc-status")?.to_str().ok()?.parse::().ok()?; + if code == 0 { + return None; + } + + let message = headers + .get("grpc-message") + .and_then(|v| v.to_str().ok()) + .map(percent_decode) + .unwrap_or_default(); + Some(grpc_code_to_error(code, message)) +} + +/// A server-streaming gRPC response that yields decoded protobuf messages of type `M`. +/// +/// Call [`next_message`](GrpcStream::next_message) to receive the next message from the server. +pub struct GrpcStream { + body: hyper::Body, + buf: Vec, + trailers_checked: bool, + _marker: std::marker::PhantomData, +} + +/// Type alias for a streaming response that yields [`EventEnvelope`] messages. +pub type EventStream = GrpcStream; + +impl GrpcStream { + /// Wait for the next message from the server. + /// + /// Returns `None` if the stream has ended. + pub async fn next_message(&mut self) -> Option> { + loop { + // Try to decode a complete gRPC frame from the buffer + if self.buf.len() >= 5 { + let msg_len = + u32::from_be_bytes([self.buf[1], self.buf[2], self.buf[3], self.buf[4]]) + as usize; + if self.buf.len() >= 5 + msg_len { + let proto_bytes = &self.buf[5..5 + msg_len]; + let result = M::decode(proto_bytes).map_err(|e| { + LdkServerError::new( + InternalError, + format!("Failed to decode gRPC stream message: {}", e), + ) + }); + self.buf.drain(..5 + msg_len); + return Some(result); + } + } + + // Need more data — read the next chunk from the response body + match self.body.data().await { + Some(Ok(chunk)) => self.buf.extend_from_slice(&chunk), + Some(Err(e)) => { + return Some(Err(LdkServerError::new( + InternalError, + format!("Failed to read gRPC stream: {}", e), + ))); + }, + None => { + if self.trailers_checked { + return None; + } + self.trailers_checked = true; + return self.finish_stream().await; + }, + } + } + } + + async fn finish_stream(&mut self) -> Option> { + match self.body.trailers().await { + Ok(Some(trailers)) => { + if let Some(error) = grpc_error_from_headers(&trailers) { + return Some(Err(error)); + } + }, + Ok(None) => {}, + Err(e) => { + return Some(Err(LdkServerError::new( + InternalError, + format!("Failed to read gRPC stream trailers: {}", e), + ))); + }, + } + + if self.buf.is_empty() { + None + } else { + Some(Err(LdkServerError::new( + InternalError, + "gRPC stream ended with an incomplete frame", + ))) + } + } +} + +fn build_streaming_client(server_cert_pem: &[u8]) -> Result { + let mut pem_reader = Cursor::new(server_cert_pem); + let certs = + certs(&mut pem_reader).map_err(|e| format!("Failed to parse server certificate: {e}"))?; + if certs.is_empty() { + return Err("Failed to parse server certificate: no certificates found in PEM".to_string()); + } + + let mut roots = RootCertStore::empty(); + let (added, _ignored) = roots.add_parsable_certificates(&certs); + if added == 0 { + return Err("Failed to build streaming client: certificate was not accepted".to_string()); + } + + let tls_config = ClientConfig::builder() + .with_safe_defaults() + .with_root_certificates(roots) + .with_no_client_auth(); + let connector = HttpsConnectorBuilder::new() + .with_tls_config(tls_config) + .https_only() + .enable_http2() + .build(); + + Ok(HyperClient::builder().http2_only(true).build(connector)) } #[cfg(test)] mod tests { use super::*; + use hyper::Body; + use reqwest::header::HeaderValue; + + #[test] + fn test_grpc_error_from_headers_ignores_ok_status() { + let mut headers = HeaderMap::new(); + headers.insert("grpc-status", HeaderValue::from_static("0")); + assert!(grpc_error_from_headers(&headers).is_none()); + } + + #[test] + fn test_grpc_error_from_headers_decodes_message() { + let mut headers = HeaderMap::new(); + headers.insert("grpc-status", HeaderValue::from_static("3")); + headers.insert("grpc-message", HeaderValue::from_static("bad%20request")); + + let err = grpc_error_from_headers(&headers).unwrap(); + assert_eq!(err.error_code, InvalidRequestError); + assert_eq!(err.message, "bad request"); + } + + #[test] + fn test_grpc_code_to_error_marks_unavailable_streams() { + let err = grpc_code_to_error(14, "server shutting down".to_string()); + assert_eq!(err.error_code, InternalError); + assert_eq!(err.message, "gRPC stream became unavailable: server shutting down"); + } + + #[tokio::test] + async fn test_event_stream_surfaces_terminal_grpc_status() { + let (mut sender, body) = Body::channel(); + let mut trailers = HeaderMap::new(); + trailers.insert("grpc-status", HeaderValue::from_static("14")); + trailers.insert("grpc-message", HeaderValue::from_static("server%20restarting")); + sender.send_trailers(trailers).await.unwrap(); + drop(sender); + + let mut stream: EventStream = GrpcStream { + body, + buf: Vec::new(), + trailers_checked: false, + _marker: std::marker::PhantomData, + }; + + let result = stream.next_message().await.unwrap().unwrap_err(); + assert_eq!(result.error_code, InternalError); + assert_eq!(result.message, "gRPC stream became unavailable: server restarting"); + assert!(stream.next_message().await.is_none()); + } #[test] fn test_grpc_code_to_error_all_known_codes() { diff --git a/ldk-server/src/main.rs b/ldk-server/src/main.rs index 9d16d615..30807541 100644 --- a/ldk-server/src/main.rs +++ b/ldk-server/src/main.rs @@ -231,6 +231,7 @@ fn main() { }); let (event_sender, _) = broadcast::channel::(1024); + let (shutdown_tx, shutdown_rx) = tokio::sync::watch::channel(false); info!("Starting up..."); match node.start() { @@ -493,6 +494,7 @@ fn main() { metrics.clone(), metrics_auth_header.clone(), event_sender.clone(), + shutdown_rx.clone(), ); let acceptor = tls_acceptor.clone(); runtime.spawn(async move { @@ -512,6 +514,7 @@ fn main() { } _ = tokio::signal::ctrl_c() => { info!("Received CTRL-C, shutting down.."); + let _ = shutdown_tx.send(true); break; } _ = sighup_stream.recv() => { @@ -521,6 +524,7 @@ fn main() { } _ = sigterm_stream.recv() => { info!("Received SIGTERM, shutting down.."); + let _ = shutdown_tx.send(true); break; } } diff --git a/ldk-server/src/service.rs b/ldk-server/src/service.rs index fe96942c..a2fc2c0f 100644 --- a/ldk-server/src/service.rs +++ b/ldk-server/src/service.rs @@ -28,8 +28,8 @@ use ldk_server_grpc::endpoints::{ GRAPH_GET_CHANNEL_PATH, GRAPH_GET_NODE_PATH, GRAPH_LIST_CHANNELS_PATH, GRAPH_LIST_NODES_PATH, LIST_CHANNELS_PATH, LIST_FORWARDED_PAYMENTS_PATH, LIST_PAYMENTS_PATH, LIST_PEERS_PATH, ONCHAIN_RECEIVE_PATH, ONCHAIN_SEND_PATH, OPEN_CHANNEL_PATH, SIGN_MESSAGE_PATH, SPLICE_IN_PATH, - SPLICE_OUT_PATH, SPONTANEOUS_SEND_PATH, UNIFIED_SEND_PATH, UPDATE_CHANNEL_CONFIG_PATH, - VERIFY_SIGNATURE_PATH, + SPLICE_OUT_PATH, SPONTANEOUS_SEND_PATH, SUBSCRIBE_EVENTS_PATH, UNIFIED_SEND_PATH, + UPDATE_CHANNEL_CONFIG_PATH, VERIFY_SIGNATURE_PATH, }; use ldk_server_grpc::events::EventEnvelope; use ldk_server_grpc::grpc::{ @@ -95,17 +95,19 @@ pub(crate) struct NodeService { api_key: String, metrics: Option>, metrics_auth_header: Option, - event_sender: broadcast::Sender, + event_sender: broadcast::Sender, + shutdown_rx: tokio::sync::watch::Receiver, } impl NodeService { pub(crate) fn new( node: Arc, paginated_kv_store: Arc, api_key: String, metrics: Option>, metrics_auth_header: Option, - event_sender: broadcast::Sender, + event_sender: broadcast::Sender, + shutdown_rx: tokio::sync::watch::Receiver, ) -> Self { let context = Arc::new(Context { node, paginated_kv_store }); - Self { context, api_key, metrics, metrics_auth_header, event_sender } + Self { context, api_key, metrics, metrics_auth_header, event_sender, shutdown_rx } } } @@ -220,11 +222,26 @@ impl Service> for NodeService { let context = Arc::clone(&self.context); let path = req.uri().path().to_string(); - let deadline = req - .headers() - .get("grpc-timeout") - .and_then(|v| v.to_str().ok()) - .and_then(parse_grpc_timeout); + let deadline = match req.headers().get("grpc-timeout") { + Some(value) => { + let value = match value.to_str() { + Ok(value) => value, + Err(_) => { + let status = GrpcStatus::new( + GRPC_STATUS_INVALID_ARGUMENT, + "Invalid grpc-timeout header", + ); + return Box::pin(async move { Ok(grpc_error_response(status)) }); + }, + }; + + match parse_grpc_timeout(value) { + Ok(timeout) => Some(timeout), + Err(status) => return Box::pin(async move { Ok(grpc_error_response(status)) }), + } + }, + None => None, + }; // Strip the service prefix to get the method name let method = match path.strip_prefix(GRPC_SERVICE_PREFIX) { @@ -343,6 +360,52 @@ impl Service> for NodeService { DECODE_OFFER_PATH => { Box::pin(handle_grpc_unary(context, req, handle_decode_offer_request)) }, + SUBSCRIBE_EVENTS_PATH => { + let event_sender = self.event_sender.clone(); + let mut shutdown_rx = self.shutdown_rx.clone(); + Box::pin(async move { + let mut rx = event_sender.subscribe(); + let (tx, mpsc_rx) = mpsc::channel::>(64); + tokio::spawn(async move { + loop { + tokio::select! { + result = rx.recv() => { + match result { + Ok(event) => { + let frame = encode_grpc_frame(&event.encode_to_vec()); + if tx.send(Ok(frame)).await.is_err() { + break; // client disconnected + } + }, + Err(tokio::sync::broadcast::error::RecvError::Lagged(_)) => { + continue; // skip missed events, keep streaming + }, + Err(tokio::sync::broadcast::error::RecvError::Closed) => { + let _ = tx + .send(Err(GrpcStatus::new( + GRPC_STATUS_UNAVAILABLE, + "server shutting down", + ))) + .await; + break; + }, + } + }, + _ = shutdown_rx.changed() => { + let _ = tx + .send(Err(GrpcStatus::new( + GRPC_STATUS_UNAVAILABLE, + "server shutting down", + ))) + .await; + break; + }, + } + } + }); + Ok(grpc_response(GrpcBody::Stream { rx: mpsc_rx, done: false })) + }) + }, _ => { let status = GrpcStatus::new(GRPC_STATUS_UNIMPLEMENTED, format!("Unknown method: {method}")); From a870be0cf357bcfabef73cbd86132a3867204e32 Mon Sep 17 00:00:00 2001 From: benthecarman Date: Fri, 27 Mar 2026 16:32:31 -0500 Subject: [PATCH 7/8] Update cli for gRPC Rename rest_service_address to grpc_service_address in CLI config and e2e test harness. Co-Authored-By: Claude Opus 4.6 (1M context) --- ldk-server-cli/src/config.rs | 24 ++++++++++++------------ ldk-server-cli/src/main.rs | 4 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/ldk-server-cli/src/config.rs b/ldk-server-cli/src/config.rs index 8cf0276b..2071f344 100644 --- a/ldk-server-cli/src/config.rs +++ b/ldk-server-cli/src/config.rs @@ -14,7 +14,7 @@ use serde::{Deserialize, Serialize}; const DEFAULT_CONFIG_FILE: &str = "config.toml"; const DEFAULT_CERT_FILE: &str = "tls.crt"; const API_KEY_FILE: &str = "api_key"; -pub const DEFAULT_REST_SERVICE_ADDRESS: &str = "127.0.0.1:3536"; +pub const DEFAULT_GRPC_SERVICE_ADDRESS: &str = "127.0.0.1:3536"; pub fn get_default_data_dir() -> Option { #[cfg(target_os = "macos")] @@ -67,8 +67,8 @@ pub struct TlsConfig { #[derive(Debug, Deserialize)] pub struct NodeConfig { - #[serde(default = "default_rest_service_address")] - pub rest_service_address: String, + #[serde(default = "default_grpc_service_address")] + pub grpc_service_address: String, network: String, } @@ -104,20 +104,20 @@ pub fn load_config(path: &PathBuf) -> Result { pub fn resolve_base_url(cli_base_url: Option, config: Option<&Config>) -> String { cli_base_url - .or_else(|| config.map(|config| config.node.rest_service_address.clone())) - .unwrap_or_else(default_rest_service_address) + .or_else(|| config.map(|config| config.node.grpc_service_address.clone())) + .unwrap_or_else(default_grpc_service_address) } -fn default_rest_service_address() -> String { - DEFAULT_REST_SERVICE_ADDRESS.to_string() +fn default_grpc_service_address() -> String { + DEFAULT_GRPC_SERVICE_ADDRESS.to_string() } #[cfg(test)] mod tests { - use super::{resolve_base_url, Config, DEFAULT_REST_SERVICE_ADDRESS}; + use super::{resolve_base_url, Config, DEFAULT_GRPC_SERVICE_ADDRESS}; #[test] - fn config_defaults_rest_service_address() { + fn config_defaults_grpc_service_address() { let config: Config = toml::from_str( r#" [node] @@ -126,7 +126,7 @@ mod tests { ) .unwrap(); - assert_eq!(config.node.rest_service_address, DEFAULT_REST_SERVICE_ADDRESS); + assert_eq!(config.node.grpc_service_address, DEFAULT_GRPC_SERVICE_ADDRESS); } #[test] @@ -135,7 +135,7 @@ mod tests { r#" [node] network = "regtest" - rest_service_address = "127.0.0.1:3002" + grpc_service_address = "127.0.0.1:3002" "#, ) .unwrap(); @@ -148,6 +148,6 @@ mod tests { #[test] fn resolve_base_url_falls_back_to_default() { - assert_eq!(resolve_base_url(None, None), DEFAULT_REST_SERVICE_ADDRESS); + assert_eq!(resolve_base_url(None, None), DEFAULT_GRPC_SERVICE_ADDRESS); } } diff --git a/ldk-server-cli/src/main.rs b/ldk-server-cli/src/main.rs index 6aec5d69..0c5b9c42 100644 --- a/ldk-server-cli/src/main.rs +++ b/ldk-server-cli/src/main.rs @@ -15,7 +15,7 @@ use clap_complete::{generate, Shell}; 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, resolve_base_url, - DEFAULT_REST_SERVICE_ADDRESS, + DEFAULT_GRPC_SERVICE_ADDRESS, }; use hex_conservative::DisplayHex; use ldk_server_client::client::LdkServerClient; @@ -86,7 +86,7 @@ struct Cli { short, long, help = format!( - "Base URL of the server. Defaults to config file or {DEFAULT_REST_SERVICE_ADDRESS}" + "Base URL of the server. Defaults to config file or {DEFAULT_GRPC_SERVICE_ADDRESS}" ) )] base_url: Option, From 2a689fd9de0c277bf034a262817664cf08808422 Mon Sep 17 00:00:00 2001 From: benthecarman Date: Fri, 27 Mar 2026 16:33:18 -0500 Subject: [PATCH 8/8] Update e2e tests CI --- ...ests-events-rabbitmq.yml => e2e-tests.yml} | 23 ++----------------- 1 file changed, 2 insertions(+), 21 deletions(-) rename .github/workflows/{integration-tests-events-rabbitmq.yml => e2e-tests.yml} (65%) diff --git a/.github/workflows/integration-tests-events-rabbitmq.yml b/.github/workflows/e2e-tests.yml similarity index 65% rename from .github/workflows/integration-tests-events-rabbitmq.yml rename to .github/workflows/e2e-tests.yml index db9239e0..852a23ae 100644 --- a/.github/workflows/integration-tests-events-rabbitmq.yml +++ b/.github/workflows/e2e-tests.yml @@ -1,4 +1,4 @@ -name: Integration Tests +name: End-to-End Tests on: [ push, pull_request ] @@ -10,23 +10,9 @@ concurrency: cancel-in-progress: true jobs: - integration-tests: + e2e-tests: runs-on: ubuntu-latest - services: - rabbitmq: - image: rabbitmq:3 - env: - RABBITMQ_DEFAULT_USER: guest - RABBITMQ_DEFAULT_PASS: guest - ports: - - 5672:5672 - options: >- - --health-cmd "rabbitmqctl node_health_check" - --health-interval 10s - --health-timeout 5s - --health-retries 5 - steps: - name: Checkout code uses: actions/checkout@v6 @@ -48,11 +34,6 @@ jobs: - name: Set bitcoind environment variable run: echo "BITCOIND_EXE=$( pwd )/bin/bitcoind-${{ runner.os }}-${{ runner.arch }}" >> "$GITHUB_ENV" - - name: Run RabbitMQ integration tests - run: cargo test --features integration-tests-events-rabbitmq --verbose --color=always -- --nocapture - env: - RUST_BACKTRACE: 1 - - name: Run end-to-end tests run: cargo test --manifest-path e2e-tests/Cargo.toml --verbose --color=always -- --test-threads=4 --nocapture env: