Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions e2e-tests/tests/e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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]
Expand Down
68 changes: 68 additions & 0 deletions ldk-server-grpc/src/proto/types.proto
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down
128 changes: 128 additions & 0 deletions ldk-server-grpc/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u32>,
/// 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<u64>,
/// 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<u64>,
/// 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<u64>,
/// 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<u64>,
/// 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<i32>,
/// 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<i32>,
}
/// ChannelConfig represents the configuration settings for a channel in a Lightning Network node.
/// See more: <https://docs.rs/lightning/latest/lightning/util/config/struct.ChannelConfig.html>
Expand Down Expand Up @@ -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<Self> {
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<Self> {
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))]
Expand Down
42 changes: 39 additions & 3 deletions ldk-server/src/util/proto_adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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;
Expand All @@ -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;

Expand Down Expand Up @@ -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),
}
}

Expand Down