diff --git a/e2e-tests/tests/e2e.rs b/e2e-tests/tests/e2e.rs index 9de61c63..e30f6941 100644 --- a/e2e-tests/tests/e2e.rs +++ b/e2e-tests/tests/e2e.rs @@ -31,7 +31,7 @@ use ldk_server_client::ldk_server_grpc::events::{ ChannelClosureInitiator, ChannelState, ChannelStateChangeReasonKind, }; use ldk_server_client::ldk_server_grpc::types::{ - bolt11_invoice_description, Bolt11InvoiceDescription, + bolt11_invoice_description, Bolt11InvoiceDescription, ChannelShutdownState, ReserveType, }; use ldk_server_grpc::types::payment_kind; @@ -799,7 +799,29 @@ async fn test_cli_list_channels() { let output = run_cli(&server_a, &["list-channels"]); let channels = output["channels"].as_array().unwrap(); assert!(!channels.is_empty()); - assert_eq!(channels[0]["counterparty_node_id"], server_b.node_id()); + let channel = &channels[0]; + assert_eq!(channel["counterparty_node_id"], server_b.node_id()); + + // A funded, usable channel has a real short_channel_id and both SCID aliases set. + assert!(channel["short_channel_id"].is_u64()); + assert!(channel["outbound_scid_alias"].is_u64()); + assert!(channel["inbound_scid_alias"].is_u64()); + + // HTLC bounds: the minimum is a non-optional field, and the maximum is always known + // once the counterparty's reserve has been negotiated (true by the time a channel + // is usable). + assert!(channel["inbound_htlc_minimum_msat"].is_u64()); + assert!(channel["inbound_htlc_maximum_msat"].is_u64()); + + // A freshly opened, still-open channel is always NotShuttingDown. + assert_eq!( + channel["channel_shutdown_state"].as_i64(), + Some(ChannelShutdownState::NotShuttingDown as i64) + ); + + // This test opens a default (anchor) channel with no trusted_peers_no_reserve + // configured, so the reserve type is deterministically Adaptive. + assert_eq!(channel["reserve_type"].as_i64(), Some(ReserveType::Adaptive as i64)); } #[tokio::test] diff --git a/ldk-server-grpc/src/proto/types.proto b/ldk-server-grpc/src/proto/types.proto index 654a3a80..4cb17191 100644 --- a/ldk-server-grpc/src/proto/types.proto +++ b/ldk-server-grpc/src/proto/types.proto @@ -260,6 +260,39 @@ message ForwardedPayment{ } +// ChannelShutdownState mirrors LDK's `lightning::ln::channel_state::ChannelShutdownState`, +// indicating how far along a channel is in the cooperative close process. +enum ChannelShutdownState { + CHANNEL_SHUTDOWN_STATE_UNSPECIFIED = 0; + // Channel has not sent or received a shutdown message. + CHANNEL_SHUTDOWN_STATE_NOT_SHUTTING_DOWN = 1; + // Local node has sent a shutdown message for this channel. + CHANNEL_SHUTDOWN_STATE_SHUTDOWN_INITIATED = 2; + // Shutdown message exchanges have concluded and the channels are in the midst of + // resolving all existing open HTLCs before closing can continue. + CHANNEL_SHUTDOWN_STATE_RESOLVING_HTLCS = 3; + // All HTLCs have been resolved, nodes are currently negotiating channel close onchain fee + // rates. + CHANNEL_SHUTDOWN_STATE_NEGOTIATING_CLOSING_FEE = 4; + // We've successfully negotiated a closing_signed dance. At this point the channel is about + // to be dropped. + CHANNEL_SHUTDOWN_STATE_SHUTDOWN_COMPLETE = 5; +} + +// ReserveType mirrors LDK Node's `ReserveType`, indicating the kind of on-chain reserve +// maintained for a channel, if any has been determined yet. +enum ReserveType { + RESERVE_TYPE_UNSPECIFIED = 0; + // An anchor outputs channel where we maintain a per-channel on-chain reserve for fee + // bumping force-close transactions. + RESERVE_TYPE_ADAPTIVE = 1; + // An anchor outputs channel where we do not maintain any reserve, because the counterparty + // is in our trusted_peers_no_reserve list. + RESERVE_TYPE_TRUSTED_PEERS_NO_RESERVE = 2; + // A legacy (pre-anchor) channel using only option_static_remotekey. + RESERVE_TYPE_LEGACY = 3; +} + message Channel { // The channel ID (prior to funding transaction generation, this is a random 32-byte // identifier, afterwards this is the transaction ID of the funding transaction XOR the @@ -386,6 +419,41 @@ message Channel { // The minimum difference in CLTV expiry between an ingoing HTLC and its outgoing counterpart, // such that the outgoing HTLC is forwardable to this counterparty. optional uint32 counterparty_forwarding_info_cltv_expiry_delta = 25; + + // The channel's `short_channel_id`, if we've negotiated the funding transaction with our + // counterparty already and it's reached the required number of confirmations. + // + // Note that if an inbound SCID alias is set, that will be used for invoices and inbound + // payments instead of this value. + optional uint64 short_channel_id = 26; + + // An optional `short_channel_id` alias for this channel, randomly generated by us and usable + // in place of `short_channel_id` to reference the channel in outbound routes when the channel + // has not yet been confirmed. + optional uint64 outbound_scid_alias = 27; + + // An optional `short_channel_id` alias for this channel, randomly generated by our + // counterparty and usable in place of `short_channel_id` in invoice route hints. Our + // counterparty will recognize the alias provided here in place of the `short_channel_id` + // when they see a payment to be routed to us. + optional uint64 inbound_scid_alias = 28; + + // The smallest value HTLC (in msat) we will accept, for this channel. + uint64 inbound_htlc_minimum_msat = 29; + + // The largest value HTLC (in msat) we currently will accept, for this channel. + optional uint64 inbound_htlc_maximum_msat = 30; + + // The current shutdown state of the channel, if any. + // + // Will be unset for objects serialized with LDK Node v0.1 and earlier. + optional ChannelShutdownState channel_shutdown_state = 31; + + // The type of on-chain reserve maintained for this channel. + // + // Will be unset until channel negotiation has completed and determined whether this channel + // uses anchor or legacy reserve behavior. + optional ReserveType reserve_type = 32; } // ChannelConfig represents the configuration settings for a channel in a Lightning Network node. diff --git a/ldk-server-grpc/src/types.rs b/ldk-server-grpc/src/types.rs index 1240cb46..5d791ad9 100644 --- a/ldk-server-grpc/src/types.rs +++ b/ldk-server-grpc/src/types.rs @@ -464,6 +464,41 @@ pub struct Channel { /// such that the outgoing HTLC is forwardable to this counterparty. #[prost(uint32, optional, tag = "25")] pub counterparty_forwarding_info_cltv_expiry_delta: ::core::option::Option, + /// The channel's `short_channel_id`, if we've negotiated the funding transaction with our + /// counterparty already and it's reached the required number of confirmations. + /// + /// Note that if an inbound SCID alias is set, that will be used for invoices and inbound + /// payments instead of this value. + #[prost(uint64, optional, tag = "26")] + pub short_channel_id: ::core::option::Option, + /// An optional `short_channel_id` alias for this channel, randomly generated by us and usable + /// in place of `short_channel_id` to reference the channel in outbound routes when the channel + /// has not yet been confirmed. + #[prost(uint64, optional, tag = "27")] + pub outbound_scid_alias: ::core::option::Option, + /// An optional `short_channel_id` alias for this channel, randomly generated by our + /// counterparty and usable in place of `short_channel_id` in invoice route hints. Our + /// counterparty will recognize the alias provided here in place of the `short_channel_id` + /// when they see a payment to be routed to us. + #[prost(uint64, optional, tag = "28")] + pub inbound_scid_alias: ::core::option::Option, + /// The smallest value HTLC (in msat) we will accept, for this channel. + #[prost(uint64, tag = "29")] + pub inbound_htlc_minimum_msat: u64, + /// The largest value HTLC (in msat) we currently will accept, for this channel. + #[prost(uint64, optional, tag = "30")] + pub inbound_htlc_maximum_msat: ::core::option::Option, + /// The current shutdown state of the channel, if any. + /// + /// Will be unset for objects serialized with LDK Node v0.1 and earlier. + #[prost(enumeration = "ChannelShutdownState", optional, tag = "31")] + pub channel_shutdown_state: ::core::option::Option, + /// The type of on-chain reserve maintained for this channel. + /// + /// Will be unset until channel negotiation has completed and determined whether this channel + /// uses anchor or legacy reserve behavior. + #[prost(enumeration = "ReserveType", optional, tag = "32")] + pub reserve_type: ::core::option::Option, } /// ChannelConfig represents the configuration settings for a channel in a Lightning Network node. /// See more: @@ -1352,6 +1387,99 @@ impl Network { } } } +/// ChannelShutdownState mirrors LDK's `lightning::ln::channel_state::ChannelShutdownState`, +/// indicating how far along a channel is in the cooperative close process. +#[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 ChannelShutdownState { + Unspecified = 0, + /// Channel has not sent or received a shutdown message. + NotShuttingDown = 1, + /// Local node has sent a shutdown message for this channel. + ShutdownInitiated = 2, + /// Shutdown message exchanges have concluded and the channels are in the midst of + /// resolving all existing open HTLCs before closing can continue. + ResolvingHtlcs = 3, + /// All HTLCs have been resolved, nodes are currently negotiating channel close onchain fee + /// rates. + NegotiatingClosingFee = 4, + /// We've successfully negotiated a closing_signed dance. At this point the channel is about + /// to be dropped. + ShutdownComplete = 5, +} +impl ChannelShutdownState { + /// 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 { + ChannelShutdownState::Unspecified => "CHANNEL_SHUTDOWN_STATE_UNSPECIFIED", + ChannelShutdownState::NotShuttingDown => "CHANNEL_SHUTDOWN_STATE_NOT_SHUTTING_DOWN", + ChannelShutdownState::ShutdownInitiated => "CHANNEL_SHUTDOWN_STATE_SHUTDOWN_INITIATED", + ChannelShutdownState::ResolvingHtlcs => "CHANNEL_SHUTDOWN_STATE_RESOLVING_HTLCS", + ChannelShutdownState::NegotiatingClosingFee => { + "CHANNEL_SHUTDOWN_STATE_NEGOTIATING_CLOSING_FEE" + }, + ChannelShutdownState::ShutdownComplete => "CHANNEL_SHUTDOWN_STATE_SHUTDOWN_COMPLETE", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "CHANNEL_SHUTDOWN_STATE_UNSPECIFIED" => Some(Self::Unspecified), + "CHANNEL_SHUTDOWN_STATE_NOT_SHUTTING_DOWN" => Some(Self::NotShuttingDown), + "CHANNEL_SHUTDOWN_STATE_SHUTDOWN_INITIATED" => Some(Self::ShutdownInitiated), + "CHANNEL_SHUTDOWN_STATE_RESOLVING_HTLCS" => Some(Self::ResolvingHtlcs), + "CHANNEL_SHUTDOWN_STATE_NEGOTIATING_CLOSING_FEE" => Some(Self::NegotiatingClosingFee), + "CHANNEL_SHUTDOWN_STATE_SHUTDOWN_COMPLETE" => Some(Self::ShutdownComplete), + _ => None, + } + } +} +/// ReserveType mirrors LDK Node's `ReserveType`, indicating the kind of on-chain reserve +/// maintained for a channel, if any has been determined yet. +#[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 ReserveType { + Unspecified = 0, + /// An anchor outputs channel where we maintain a per-channel on-chain reserve for fee + /// bumping force-close transactions. + Adaptive = 1, + /// An anchor outputs channel where we do not maintain any reserve, because the counterparty + /// is in our trusted_peers_no_reserve list. + TrustedPeersNoReserve = 2, + /// A legacy (pre-anchor) channel using only option_static_remotekey. + Legacy = 3, +} +impl ReserveType { + /// 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 { + ReserveType::Unspecified => "RESERVE_TYPE_UNSPECIFIED", + ReserveType::Adaptive => "RESERVE_TYPE_ADAPTIVE", + ReserveType::TrustedPeersNoReserve => "RESERVE_TYPE_TRUSTED_PEERS_NO_RESERVE", + ReserveType::Legacy => "RESERVE_TYPE_LEGACY", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "RESERVE_TYPE_UNSPECIFIED" => Some(Self::Unspecified), + "RESERVE_TYPE_ADAPTIVE" => Some(Self::Adaptive), + "RESERVE_TYPE_TRUSTED_PEERS_NO_RESERVE" => Some(Self::TrustedPeersNoReserve), + "RESERVE_TYPE_LEGACY" => Some(Self::Legacy), + _ => None, + } + } +} /// Indicates whether the balance is derived from a cooperative close, a force-close (for holder or counterparty), /// or whether it is for an HTLC. #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] diff --git a/ldk-server/src/util/proto_adapter.rs b/ldk-server/src/util/proto_adapter.rs index 42c3738c..82277210 100644 --- a/ldk-server/src/util/proto_adapter.rs +++ b/ldk-server/src/util/proto_adapter.rs @@ -23,7 +23,10 @@ use ldk_node::lightning_types::features::NodeFeatures; use ldk_node::payment::{ ConfirmationStatus, PaymentDetails, PaymentDirection, PaymentKind, PaymentStatus, }; -use ldk_node::{ChannelDetails, LightningBalance, PeerDetails, PendingSweepBalance}; +use ldk_node::{ + ChannelDetails, ChannelShutdownState, LightningBalance, PeerDetails, PendingSweepBalance, + ReserveType, +}; use ldk_server_grpc::types::confirmation_status::Status::{Confirmed, Unconfirmed}; use ldk_server_grpc::types::lightning_balance::BalanceType::{ ClaimableAwaitingConfirmations, ClaimableOnChannelClose, ContentiousClaimable, @@ -36,8 +39,9 @@ use ldk_server_grpc::types::pending_sweep_balance::BalanceType::{ AwaitingThresholdConfirmations, BroadcastAwaitingConfirmation, PendingBroadcast, }; use ldk_server_grpc::types::{ - bolt11_invoice_description, Channel, Feature, ForwardedPayment, HtlcLocator, OutPoint, Payment, - Peer, + bolt11_invoice_description, Channel, ChannelShutdownState as ProtoChannelShutdownState, + Feature, ForwardedPayment, HtlcLocator, OutPoint, Payment, Peer, + ReserveType as ProtoReserveType, }; use crate::api::error::LdkServerError; @@ -52,6 +56,28 @@ pub(crate) fn peer_to_proto(peer: PeerDetails) -> Peer { } } +pub(crate) fn channel_shutdown_state_to_proto( + state: &ChannelShutdownState, +) -> ProtoChannelShutdownState { + match state { + ChannelShutdownState::NotShuttingDown => ProtoChannelShutdownState::NotShuttingDown, + ChannelShutdownState::ShutdownInitiated => ProtoChannelShutdownState::ShutdownInitiated, + ChannelShutdownState::ResolvingHTLCs => ProtoChannelShutdownState::ResolvingHtlcs, + ChannelShutdownState::NegotiatingClosingFee => { + ProtoChannelShutdownState::NegotiatingClosingFee + }, + ChannelShutdownState::ShutdownComplete => ProtoChannelShutdownState::ShutdownComplete, + } +} + +pub(crate) fn reserve_type_to_proto(reserve_type: &ReserveType) -> ProtoReserveType { + match reserve_type { + ReserveType::Adaptive => ProtoReserveType::Adaptive, + ReserveType::TrustedPeersNoReserve => ProtoReserveType::TrustedPeersNoReserve, + ReserveType::Legacy => ProtoReserveType::Legacy, + } +} + pub(crate) fn channel_to_proto(channel: ChannelDetails) -> Channel { let counterparty = channel.counterparty; @@ -92,6 +118,16 @@ pub(crate) fn channel_to_proto(channel: ChannelDetails) -> Channel { .forwarding_info .as_ref() .map(|info| info.cltv_expiry_delta as u32), + short_channel_id: channel.short_channel_id, + outbound_scid_alias: channel.outbound_scid_alias, + inbound_scid_alias: channel.inbound_scid_alias, + inbound_htlc_minimum_msat: channel.inbound_htlc_minimum_msat, + inbound_htlc_maximum_msat: channel.inbound_htlc_maximum_msat, + channel_shutdown_state: channel + .channel_shutdown_state + .as_ref() + .map(|s| channel_shutdown_state_to_proto(s) as i32), + reserve_type: channel.reserve_type.as_ref().map(|r| reserve_type_to_proto(r) as i32), } }