Skip to content

Commit eb3be3a

Browse files
authored
Merge pull request #264 from elnafateh/expose-channel-snapshot-fields
Expose SCID, HTLC bounds, shutdown state, and reserve type on Channel
2 parents 2261d68 + 75df152 commit eb3be3a

4 files changed

Lines changed: 259 additions & 5 deletions

File tree

e2e-tests/tests/e2e.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use ldk_server_client::ldk_server_grpc::events::{
3131
ChannelClosureInitiator, ChannelState, ChannelStateChangeReasonKind, PaymentFailureReason,
3232
};
3333
use ldk_server_client::ldk_server_grpc::types::{
34-
bolt11_invoice_description, Bolt11InvoiceDescription,
34+
bolt11_invoice_description, Bolt11InvoiceDescription, ChannelShutdownState, ReserveType,
3535
};
3636
use ldk_server_grpc::types::payment_kind;
3737

@@ -799,7 +799,29 @@ async fn test_cli_list_channels() {
799799
let output = run_cli(&server_a, &["list-channels"]);
800800
let channels = output["channels"].as_array().unwrap();
801801
assert!(!channels.is_empty());
802-
assert_eq!(channels[0]["counterparty_node_id"], server_b.node_id());
802+
let channel = &channels[0];
803+
assert_eq!(channel["counterparty_node_id"], server_b.node_id());
804+
805+
// A funded, usable channel has a real short_channel_id and both SCID aliases set.
806+
assert!(channel["short_channel_id"].is_u64());
807+
assert!(channel["outbound_scid_alias"].is_u64());
808+
assert!(channel["inbound_scid_alias"].is_u64());
809+
810+
// HTLC bounds: the minimum is a non-optional field, and the maximum is always known
811+
// once the counterparty's reserve has been negotiated (true by the time a channel
812+
// is usable).
813+
assert!(channel["inbound_htlc_minimum_msat"].is_u64());
814+
assert!(channel["inbound_htlc_maximum_msat"].is_u64());
815+
816+
// A freshly opened, still-open channel is always NotShuttingDown.
817+
assert_eq!(
818+
channel["channel_shutdown_state"].as_i64(),
819+
Some(ChannelShutdownState::NotShuttingDown as i64)
820+
);
821+
822+
// This test opens a default (anchor) channel with no trusted_peers_no_reserve
823+
// configured, so the reserve type is deterministically Adaptive.
824+
assert_eq!(channel["reserve_type"].as_i64(), Some(ReserveType::Adaptive as i64));
803825
}
804826

805827
#[tokio::test]

ldk-server-grpc/src/proto/types.proto

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,39 @@ message ForwardedPayment{
260260

261261
}
262262

263+
// ChannelShutdownState mirrors LDK's `lightning::ln::channel_state::ChannelShutdownState`,
264+
// indicating how far along a channel is in the cooperative close process.
265+
enum ChannelShutdownState {
266+
CHANNEL_SHUTDOWN_STATE_UNSPECIFIED = 0;
267+
// Channel has not sent or received a shutdown message.
268+
CHANNEL_SHUTDOWN_STATE_NOT_SHUTTING_DOWN = 1;
269+
// Local node has sent a shutdown message for this channel.
270+
CHANNEL_SHUTDOWN_STATE_SHUTDOWN_INITIATED = 2;
271+
// Shutdown message exchanges have concluded and the channels are in the midst of
272+
// resolving all existing open HTLCs before closing can continue.
273+
CHANNEL_SHUTDOWN_STATE_RESOLVING_HTLCS = 3;
274+
// All HTLCs have been resolved, nodes are currently negotiating channel close onchain fee
275+
// rates.
276+
CHANNEL_SHUTDOWN_STATE_NEGOTIATING_CLOSING_FEE = 4;
277+
// We've successfully negotiated a closing_signed dance. At this point the channel is about
278+
// to be dropped.
279+
CHANNEL_SHUTDOWN_STATE_SHUTDOWN_COMPLETE = 5;
280+
}
281+
282+
// ReserveType mirrors LDK Node's `ReserveType`, indicating the kind of on-chain reserve
283+
// maintained for a channel, if any has been determined yet.
284+
enum ReserveType {
285+
RESERVE_TYPE_UNSPECIFIED = 0;
286+
// An anchor outputs channel where we maintain a per-channel on-chain reserve for fee
287+
// bumping force-close transactions.
288+
RESERVE_TYPE_ADAPTIVE = 1;
289+
// An anchor outputs channel where we do not maintain any reserve, because the counterparty
290+
// is in our trusted_peers_no_reserve list.
291+
RESERVE_TYPE_TRUSTED_PEERS_NO_RESERVE = 2;
292+
// A legacy (pre-anchor) channel using only option_static_remotekey.
293+
RESERVE_TYPE_LEGACY = 3;
294+
}
295+
263296
message Channel {
264297
// The channel ID (prior to funding transaction generation, this is a random 32-byte
265298
// identifier, afterwards this is the transaction ID of the funding transaction XOR the
@@ -386,6 +419,41 @@ message Channel {
386419
// The minimum difference in CLTV expiry between an ingoing HTLC and its outgoing counterpart,
387420
// such that the outgoing HTLC is forwardable to this counterparty.
388421
optional uint32 counterparty_forwarding_info_cltv_expiry_delta = 25;
422+
423+
// The channel's `short_channel_id`, if we've negotiated the funding transaction with our
424+
// counterparty already and it's reached the required number of confirmations.
425+
//
426+
// Note that if an inbound SCID alias is set, that will be used for invoices and inbound
427+
// payments instead of this value.
428+
optional uint64 short_channel_id = 26;
429+
430+
// An optional `short_channel_id` alias for this channel, randomly generated by us and usable
431+
// in place of `short_channel_id` to reference the channel in outbound routes when the channel
432+
// has not yet been confirmed.
433+
optional uint64 outbound_scid_alias = 27;
434+
435+
// An optional `short_channel_id` alias for this channel, randomly generated by our
436+
// counterparty and usable in place of `short_channel_id` in invoice route hints. Our
437+
// counterparty will recognize the alias provided here in place of the `short_channel_id`
438+
// when they see a payment to be routed to us.
439+
optional uint64 inbound_scid_alias = 28;
440+
441+
// The smallest value HTLC (in msat) we will accept, for this channel.
442+
uint64 inbound_htlc_minimum_msat = 29;
443+
444+
// The largest value HTLC (in msat) we currently will accept, for this channel.
445+
optional uint64 inbound_htlc_maximum_msat = 30;
446+
447+
// The current shutdown state of the channel, if any.
448+
//
449+
// Will be unset for objects serialized with LDK Node v0.1 and earlier.
450+
optional ChannelShutdownState channel_shutdown_state = 31;
451+
452+
// The type of on-chain reserve maintained for this channel.
453+
//
454+
// Will be unset until channel negotiation has completed and determined whether this channel
455+
// uses anchor or legacy reserve behavior.
456+
optional ReserveType reserve_type = 32;
389457
}
390458

391459
// ChannelConfig represents the configuration settings for a channel in a Lightning Network node.

ldk-server-grpc/src/types.rs

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,41 @@ pub struct Channel {
464464
/// such that the outgoing HTLC is forwardable to this counterparty.
465465
#[prost(uint32, optional, tag = "25")]
466466
pub counterparty_forwarding_info_cltv_expiry_delta: ::core::option::Option<u32>,
467+
/// The channel's `short_channel_id`, if we've negotiated the funding transaction with our
468+
/// counterparty already and it's reached the required number of confirmations.
469+
///
470+
/// Note that if an inbound SCID alias is set, that will be used for invoices and inbound
471+
/// payments instead of this value.
472+
#[prost(uint64, optional, tag = "26")]
473+
pub short_channel_id: ::core::option::Option<u64>,
474+
/// An optional `short_channel_id` alias for this channel, randomly generated by us and usable
475+
/// in place of `short_channel_id` to reference the channel in outbound routes when the channel
476+
/// has not yet been confirmed.
477+
#[prost(uint64, optional, tag = "27")]
478+
pub outbound_scid_alias: ::core::option::Option<u64>,
479+
/// An optional `short_channel_id` alias for this channel, randomly generated by our
480+
/// counterparty and usable in place of `short_channel_id` in invoice route hints. Our
481+
/// counterparty will recognize the alias provided here in place of the `short_channel_id`
482+
/// when they see a payment to be routed to us.
483+
#[prost(uint64, optional, tag = "28")]
484+
pub inbound_scid_alias: ::core::option::Option<u64>,
485+
/// The smallest value HTLC (in msat) we will accept, for this channel.
486+
#[prost(uint64, tag = "29")]
487+
pub inbound_htlc_minimum_msat: u64,
488+
/// The largest value HTLC (in msat) we currently will accept, for this channel.
489+
#[prost(uint64, optional, tag = "30")]
490+
pub inbound_htlc_maximum_msat: ::core::option::Option<u64>,
491+
/// The current shutdown state of the channel, if any.
492+
///
493+
/// Will be unset for objects serialized with LDK Node v0.1 and earlier.
494+
#[prost(enumeration = "ChannelShutdownState", optional, tag = "31")]
495+
pub channel_shutdown_state: ::core::option::Option<i32>,
496+
/// The type of on-chain reserve maintained for this channel.
497+
///
498+
/// Will be unset until channel negotiation has completed and determined whether this channel
499+
/// uses anchor or legacy reserve behavior.
500+
#[prost(enumeration = "ReserveType", optional, tag = "32")]
501+
pub reserve_type: ::core::option::Option<i32>,
467502
}
468503
/// ChannelConfig represents the configuration settings for a channel in a Lightning Network node.
469504
/// See more: <https://docs.rs/lightning/latest/lightning/util/config/struct.ChannelConfig.html>
@@ -1352,6 +1387,99 @@ impl Network {
13521387
}
13531388
}
13541389
}
1390+
/// ChannelShutdownState mirrors LDK's `lightning::ln::channel_state::ChannelShutdownState`,
1391+
/// indicating how far along a channel is in the cooperative close process.
1392+
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1393+
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
1394+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
1395+
#[repr(i32)]
1396+
pub enum ChannelShutdownState {
1397+
Unspecified = 0,
1398+
/// Channel has not sent or received a shutdown message.
1399+
NotShuttingDown = 1,
1400+
/// Local node has sent a shutdown message for this channel.
1401+
ShutdownInitiated = 2,
1402+
/// Shutdown message exchanges have concluded and the channels are in the midst of
1403+
/// resolving all existing open HTLCs before closing can continue.
1404+
ResolvingHtlcs = 3,
1405+
/// All HTLCs have been resolved, nodes are currently negotiating channel close onchain fee
1406+
/// rates.
1407+
NegotiatingClosingFee = 4,
1408+
/// We've successfully negotiated a closing_signed dance. At this point the channel is about
1409+
/// to be dropped.
1410+
ShutdownComplete = 5,
1411+
}
1412+
impl ChannelShutdownState {
1413+
/// String value of the enum field names used in the ProtoBuf definition.
1414+
///
1415+
/// The values are not transformed in any way and thus are considered stable
1416+
/// (if the ProtoBuf definition does not change) and safe for programmatic use.
1417+
pub fn as_str_name(&self) -> &'static str {
1418+
match self {
1419+
ChannelShutdownState::Unspecified => "CHANNEL_SHUTDOWN_STATE_UNSPECIFIED",
1420+
ChannelShutdownState::NotShuttingDown => "CHANNEL_SHUTDOWN_STATE_NOT_SHUTTING_DOWN",
1421+
ChannelShutdownState::ShutdownInitiated => "CHANNEL_SHUTDOWN_STATE_SHUTDOWN_INITIATED",
1422+
ChannelShutdownState::ResolvingHtlcs => "CHANNEL_SHUTDOWN_STATE_RESOLVING_HTLCS",
1423+
ChannelShutdownState::NegotiatingClosingFee => {
1424+
"CHANNEL_SHUTDOWN_STATE_NEGOTIATING_CLOSING_FEE"
1425+
},
1426+
ChannelShutdownState::ShutdownComplete => "CHANNEL_SHUTDOWN_STATE_SHUTDOWN_COMPLETE",
1427+
}
1428+
}
1429+
/// Creates an enum from field names used in the ProtoBuf definition.
1430+
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
1431+
match value {
1432+
"CHANNEL_SHUTDOWN_STATE_UNSPECIFIED" => Some(Self::Unspecified),
1433+
"CHANNEL_SHUTDOWN_STATE_NOT_SHUTTING_DOWN" => Some(Self::NotShuttingDown),
1434+
"CHANNEL_SHUTDOWN_STATE_SHUTDOWN_INITIATED" => Some(Self::ShutdownInitiated),
1435+
"CHANNEL_SHUTDOWN_STATE_RESOLVING_HTLCS" => Some(Self::ResolvingHtlcs),
1436+
"CHANNEL_SHUTDOWN_STATE_NEGOTIATING_CLOSING_FEE" => Some(Self::NegotiatingClosingFee),
1437+
"CHANNEL_SHUTDOWN_STATE_SHUTDOWN_COMPLETE" => Some(Self::ShutdownComplete),
1438+
_ => None,
1439+
}
1440+
}
1441+
}
1442+
/// ReserveType mirrors LDK Node's `ReserveType`, indicating the kind of on-chain reserve
1443+
/// maintained for a channel, if any has been determined yet.
1444+
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1445+
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
1446+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
1447+
#[repr(i32)]
1448+
pub enum ReserveType {
1449+
Unspecified = 0,
1450+
/// An anchor outputs channel where we maintain a per-channel on-chain reserve for fee
1451+
/// bumping force-close transactions.
1452+
Adaptive = 1,
1453+
/// An anchor outputs channel where we do not maintain any reserve, because the counterparty
1454+
/// is in our trusted_peers_no_reserve list.
1455+
TrustedPeersNoReserve = 2,
1456+
/// A legacy (pre-anchor) channel using only option_static_remotekey.
1457+
Legacy = 3,
1458+
}
1459+
impl ReserveType {
1460+
/// String value of the enum field names used in the ProtoBuf definition.
1461+
///
1462+
/// The values are not transformed in any way and thus are considered stable
1463+
/// (if the ProtoBuf definition does not change) and safe for programmatic use.
1464+
pub fn as_str_name(&self) -> &'static str {
1465+
match self {
1466+
ReserveType::Unspecified => "RESERVE_TYPE_UNSPECIFIED",
1467+
ReserveType::Adaptive => "RESERVE_TYPE_ADAPTIVE",
1468+
ReserveType::TrustedPeersNoReserve => "RESERVE_TYPE_TRUSTED_PEERS_NO_RESERVE",
1469+
ReserveType::Legacy => "RESERVE_TYPE_LEGACY",
1470+
}
1471+
}
1472+
/// Creates an enum from field names used in the ProtoBuf definition.
1473+
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
1474+
match value {
1475+
"RESERVE_TYPE_UNSPECIFIED" => Some(Self::Unspecified),
1476+
"RESERVE_TYPE_ADAPTIVE" => Some(Self::Adaptive),
1477+
"RESERVE_TYPE_TRUSTED_PEERS_NO_RESERVE" => Some(Self::TrustedPeersNoReserve),
1478+
"RESERVE_TYPE_LEGACY" => Some(Self::Legacy),
1479+
_ => None,
1480+
}
1481+
}
1482+
}
13551483
/// Indicates whether the balance is derived from a cooperative close, a force-close (for holder or counterparty),
13561484
/// or whether it is for an HTLC.
13571485
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]

ldk-server/src/util/proto_adapter.rs

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@ use ldk_node::lightning_types::features::NodeFeatures;
2323
use ldk_node::payment::{
2424
ConfirmationStatus, PaymentDetails, PaymentDirection, PaymentKind, PaymentStatus,
2525
};
26-
use ldk_node::{ChannelDetails, LightningBalance, PeerDetails, PendingSweepBalance};
26+
use ldk_node::{
27+
ChannelDetails, ChannelShutdownState, LightningBalance, PeerDetails, PendingSweepBalance,
28+
ReserveType,
29+
};
2730
use ldk_server_grpc::types::confirmation_status::Status::{Confirmed, Unconfirmed};
2831
use ldk_server_grpc::types::lightning_balance::BalanceType::{
2932
ClaimableAwaitingConfirmations, ClaimableOnChannelClose, ContentiousClaimable,
@@ -36,8 +39,9 @@ use ldk_server_grpc::types::pending_sweep_balance::BalanceType::{
3639
AwaitingThresholdConfirmations, BroadcastAwaitingConfirmation, PendingBroadcast,
3740
};
3841
use ldk_server_grpc::types::{
39-
bolt11_invoice_description, Channel, Feature, ForwardedPayment, HtlcLocator, OutPoint, Payment,
40-
Peer,
42+
bolt11_invoice_description, Channel, ChannelShutdownState as ProtoChannelShutdownState,
43+
Feature, ForwardedPayment, HtlcLocator, OutPoint, Payment, Peer,
44+
ReserveType as ProtoReserveType,
4145
};
4246

4347
use crate::api::error::LdkServerError;
@@ -52,6 +56,28 @@ pub(crate) fn peer_to_proto(peer: PeerDetails) -> Peer {
5256
}
5357
}
5458

59+
pub(crate) fn channel_shutdown_state_to_proto(
60+
state: &ChannelShutdownState,
61+
) -> ProtoChannelShutdownState {
62+
match state {
63+
ChannelShutdownState::NotShuttingDown => ProtoChannelShutdownState::NotShuttingDown,
64+
ChannelShutdownState::ShutdownInitiated => ProtoChannelShutdownState::ShutdownInitiated,
65+
ChannelShutdownState::ResolvingHTLCs => ProtoChannelShutdownState::ResolvingHtlcs,
66+
ChannelShutdownState::NegotiatingClosingFee => {
67+
ProtoChannelShutdownState::NegotiatingClosingFee
68+
},
69+
ChannelShutdownState::ShutdownComplete => ProtoChannelShutdownState::ShutdownComplete,
70+
}
71+
}
72+
73+
pub(crate) fn reserve_type_to_proto(reserve_type: &ReserveType) -> ProtoReserveType {
74+
match reserve_type {
75+
ReserveType::Adaptive => ProtoReserveType::Adaptive,
76+
ReserveType::TrustedPeersNoReserve => ProtoReserveType::TrustedPeersNoReserve,
77+
ReserveType::Legacy => ProtoReserveType::Legacy,
78+
}
79+
}
80+
5581
pub(crate) fn channel_to_proto(channel: ChannelDetails) -> Channel {
5682
let counterparty = channel.counterparty;
5783

@@ -92,6 +118,16 @@ pub(crate) fn channel_to_proto(channel: ChannelDetails) -> Channel {
92118
.forwarding_info
93119
.as_ref()
94120
.map(|info| info.cltv_expiry_delta as u32),
121+
short_channel_id: channel.short_channel_id,
122+
outbound_scid_alias: channel.outbound_scid_alias,
123+
inbound_scid_alias: channel.inbound_scid_alias,
124+
inbound_htlc_minimum_msat: channel.inbound_htlc_minimum_msat,
125+
inbound_htlc_maximum_msat: channel.inbound_htlc_maximum_msat,
126+
channel_shutdown_state: channel
127+
.channel_shutdown_state
128+
.as_ref()
129+
.map(|s| channel_shutdown_state_to_proto(s) as i32),
130+
reserve_type: channel.reserve_type.as_ref().map(|r| reserve_type_to_proto(r) as i32),
95131
}
96132
}
97133

0 commit comments

Comments
 (0)