diff --git a/ldk-server-grpc/build.rs b/ldk-server-grpc/build.rs index 726d3b4f..14a35806 100644 --- a/ldk-server-grpc/build.rs +++ b/ldk-server-grpc/build.rs @@ -74,6 +74,10 @@ fn generate_protos() { "api.GetNodeInfoResponse.network", "#[cfg_attr(feature = \"serde\", serde(serialize_with = \"crate::serde_utils::serialize_network\"))]", ) + .field_attribute( + "types.DirectedShortChannelId.direction", + "#[cfg_attr(feature = \"serde\", serde(serialize_with = \"crate::serde_utils::serialize_channel_direction\"))]", + ) .field_attribute( "api.UnifiedSendResponse.payment_result", "#[cfg_attr(feature = \"serde\", serde(flatten))]", diff --git a/ldk-server-grpc/src/proto/types.proto b/ldk-server-grpc/src/proto/types.proto index ae1f8e2e..83be8be8 100644 --- a/ldk-server-grpc/src/proto/types.proto +++ b/ldk-server-grpc/src/proto/types.proto @@ -897,19 +897,41 @@ message OfferQuantity { // A blinded path to the offer recipient. message BlindedPath { - // The hex-encoded public key of the introduction node, if available. - // If the introduction node is a directed short channel ID, this will be empty - // and `introduction_scid` will be set instead. - optional string introduction_node_id = 1; + // Identifies the introduction node of the blinded path, either directly by + // node id or indirectly via a directed short channel ID. + oneof introduction_node { + // The hex-encoded public key of the introduction node. + string node_id = 1; + + // The directed short channel ID identifying the introduction node. + DirectedShortChannelId directed_scid = 2; + } // The hex-encoded blinding point. - string blinding_point = 2; + string blinding_point = 3; // The number of blinded hops in the path. - uint32 num_hops = 3; + uint32 num_hops = 4; +} + +// A short channel ID together with a direction byte identifying one of the +// channel's two endpoints. +message DirectedShortChannelId { + // The short channel ID. + uint64 scid = 1; + + // Which endpoint of the channel is being referred to. + ChannelDirection direction = 2; +} + +// Identifies one of the two endpoints of a channel, by lexicographic order of +// node ids. +enum ChannelDirection { + // The endpoint whose node id is lexicographically smaller. + NODE_ONE = 0; - // If the introduction node is a directed short channel ID rather than a node ID. - optional uint64 introduction_scid = 4; + // The endpoint whose node id is lexicographically greater. + NODE_TWO = 1; } // A feature bit advertised in a BOLT11 invoice. diff --git a/ldk-server-grpc/src/serde_utils.rs b/ldk-server-grpc/src/serde_utils.rs index 588bfc49..d4daed34 100644 --- a/ldk-server-grpc/src/serde_utils.rs +++ b/ldk-server-grpc/src/serde_utils.rs @@ -38,6 +38,7 @@ stringify_enum_serializer!(serialize_payment_direction, crate::types::PaymentDir stringify_enum_serializer!(serialize_payment_status, crate::types::PaymentStatus); stringify_enum_serializer!(serialize_balance_source, crate::types::BalanceSource); stringify_enum_serializer!(serialize_network, crate::types::Network); +stringify_enum_serializer!(serialize_channel_direction, crate::types::ChannelDirection); /// Serializes `Option` as a hex string (or null). pub fn serialize_opt_bytes_hex( diff --git a/ldk-server-grpc/src/types.rs b/ldk-server-grpc/src/types.rs index 417fe391..67e52ca8 100644 --- a/ldk-server-grpc/src/types.rs +++ b/ldk-server-grpc/src/types.rs @@ -1180,20 +1180,52 @@ pub mod offer_quantity { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct BlindedPath { - /// The hex-encoded public key of the introduction node, if available. - /// If the introduction node is a directed short channel ID, this will be empty - /// and `introduction_scid` will be set instead. - #[prost(string, optional, tag = "1")] - pub introduction_node_id: ::core::option::Option<::prost::alloc::string::String>, /// The hex-encoded blinding point. - #[prost(string, tag = "2")] + #[prost(string, tag = "3")] pub blinding_point: ::prost::alloc::string::String, /// The number of blinded hops in the path. - #[prost(uint32, tag = "3")] + #[prost(uint32, tag = "4")] pub num_hops: u32, - /// If the introduction node is a directed short channel ID rather than a node ID. - #[prost(uint64, optional, tag = "4")] - pub introduction_scid: ::core::option::Option, + /// Identifies the introduction node of the blinded path, either directly by + /// node id or indirectly via a directed short channel ID. + #[prost(oneof = "blinded_path::IntroductionNode", tags = "1, 2")] + pub introduction_node: ::core::option::Option, +} +/// Nested message and enum types in `BlindedPath`. +pub mod blinded_path { + /// Identifies the introduction node of the blinded path, either directly by + /// node id or indirectly via a directed short channel ID. + #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] + #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum IntroductionNode { + /// The hex-encoded public key of the introduction node. + #[prost(string, tag = "1")] + NodeId(::prost::alloc::string::String), + /// The directed short channel ID identifying the introduction node. + #[prost(message, tag = "2")] + DirectedScid(super::DirectedShortChannelId), + } +} +/// A short channel ID together with a direction byte identifying one of the +/// channel's two endpoints. +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] +#[cfg_attr(feature = "serde", serde(default))] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DirectedShortChannelId { + /// The short channel ID. + #[prost(uint64, tag = "1")] + pub scid: u64, + /// Which endpoint of the channel is being referred to. + #[prost(enumeration = "ChannelDirection", tag = "2")] + #[cfg_attr( + feature = "serde", + serde(serialize_with = "crate::serde_utils::serialize_channel_direction") + )] + pub direction: i32, } /// A feature bit advertised in a BOLT11 invoice. #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -1361,3 +1393,35 @@ impl BalanceSource { } } } +/// Identifies one of the two endpoints of a channel, by lexicographic order of +/// node ids. +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum ChannelDirection { + /// The endpoint whose node id is lexicographically smaller. + NodeOne = 0, + /// The endpoint whose node id is lexicographically greater. + NodeTwo = 1, +} +impl ChannelDirection { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + ChannelDirection::NodeOne => "NODE_ONE", + ChannelDirection::NodeTwo => "NODE_TWO", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "NODE_ONE" => Some(Self::NodeOne), + "NODE_TWO" => Some(Self::NodeTwo), + _ => None, + } + } +} diff --git a/ldk-server/src/api/decode_offer.rs b/ldk-server/src/api/decode_offer.rs index 6d341004..7669ba5d 100644 --- a/ldk-server/src/api/decode_offer.rs +++ b/ldk-server/src/api/decode_offer.rs @@ -16,9 +16,13 @@ use ldk_node::lightning::bitcoin::Network; use ldk_node::lightning::offers::offer::Offer; use ldk_node::lightning_types::features::OfferFeatures; use ldk_server_grpc::api::{DecodeOfferRequest, DecodeOfferResponse}; +use ldk_server_grpc::types::blinded_path::IntroductionNode; 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 ldk_server_grpc::types::{ + BlindedPath, ChannelDirection, CurrencyAmount, DirectedShortChannelId, OfferAmount, + OfferQuantity, +}; use crate::api::decode_features; use crate::api::error::LdkServerError; @@ -70,20 +74,32 @@ pub(crate) async fn handle_decode_offer_request( .paths() .iter() .map(|path| { - let (introduction_node_id, introduction_scid) = match path.introduction_node() { + let introduction_node = match path.introduction_node() { ldk_node::lightning::blinded_path::IntroductionNode::NodeId(pk) => { - (Some(pk.to_string()), None) + IntroductionNode::NodeId(pk.to_string()) }, ldk_node::lightning::blinded_path::IntroductionNode::DirectedShortChannelId( - _dir, + dir, scid, - ) => (None, Some(*scid)), + ) => { + let direction = match dir { + ldk_node::lightning::blinded_path::Direction::NodeOne => { + ChannelDirection::NodeOne + }, + ldk_node::lightning::blinded_path::Direction::NodeTwo => { + ChannelDirection::NodeTwo + }, + }; + IntroductionNode::DirectedScid(DirectedShortChannelId { + scid: *scid, + direction: direction as i32, + }) + }, }; BlindedPath { - introduction_node_id, + introduction_node: Some(introduction_node), blinding_point: path.blinding_point().to_string(), num_hops: path.blinded_hops().len() as u32, - introduction_scid, } }) .collect();