Skip to content

Commit 97d5d71

Browse files
committed
Expose per-channel features in ChannelDetails
We previously flattened ChannelCounterparty fields into ChannelDetails as individual counterparty_* fields, and InitFeatures was entirely omitted. This made it impossible for consumers to access per-peer feature flags, and awkward to access counterparty forwarding information without navigating the flattened field names. This commit replaces the flattened fields with a structured ChannelCounterparty type that mirrors LDK's ChannelCounterparty, exposing InitFeatures and CounterpartyForwardingInfo that were previously inaccessible. Breaking change!
1 parent a555133 commit 97d5d71

2 files changed

Lines changed: 70 additions & 53 deletions

File tree

src/types.rs

Lines changed: 62 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@ use bitcoin_payment_instructions::onion_message_resolver::LDKOnionMessageDNSSECH
1616
use lightning::chain::chainmonitor;
1717
use lightning::impl_writeable_tlv_based;
1818
use lightning::ln::channel_state::ChannelDetails as LdkChannelDetails;
19+
pub use lightning::ln::channel_state::CounterpartyForwardingInfo;
1920
use lightning::ln::msgs::{RoutingMessageHandler, SocketAddress};
2021
use lightning::ln::peer_handler::IgnoringMessageHandler;
2122
use lightning::ln::types::ChannelId;
2223
use lightning::routing::gossip;
2324
use lightning::routing::router::DefaultRouter;
2425
use lightning::routing::scoring::{CombinedScorer, ProbabilisticScoringFeeParameters};
2526
use lightning::sign::InMemorySigner;
27+
use lightning::types::features::InitFeatures;
2628
use lightning::util::persist::{
2729
KVStore, KVStoreSync, MonitorUpdatingPersister, MonitorUpdatingPersisterAsync,
2830
};
@@ -346,6 +348,56 @@ impl fmt::Display for UserChannelId {
346348
}
347349
}
348350

351+
/// Information needed for constructing an invoice route hint for this channel.
352+
#[cfg(feature = "uniffi")]
353+
#[uniffi::remote(Record)]
354+
pub struct CounterpartyForwardingInfo {
355+
/// Base routing fee in millisatoshis.
356+
pub fee_base_msat: u32,
357+
/// Amount in millionths of a satoshi the channel will charge per transferred satoshi.
358+
pub fee_proportional_millionths: u32,
359+
/// The minimum difference in cltv_expiry between an ingoing HTLC and its outgoing counterpart,
360+
/// such that the outgoing HTLC is forwardable to this counterparty.
361+
pub cltv_expiry_delta: u16,
362+
}
363+
364+
#[cfg(feature = "uniffi")]
365+
uniffi::custom_type!(InitFeatures, Vec<u8>, {
366+
remote,
367+
try_lift: |val| Ok(InitFeatures::from_le_bytes(val)),
368+
lower: |obj| obj.le_flags().to_vec(),
369+
});
370+
371+
/// Channel parameters which apply to our counterparty. These are split out from [`ChannelDetails`]
372+
/// to better separate parameters.
373+
#[derive(Clone, Debug, PartialEq)]
374+
#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
375+
pub struct ChannelCounterparty {
376+
/// The node_id of our counterparty
377+
pub node_id: PublicKey,
378+
/// The Features the channel counterparty provided upon last connection.
379+
/// Useful for routing as it is the most up-to-date copy of the counterparty's features and
380+
/// many routing-relevant features are present in the init context.
381+
pub features: InitFeatures,
382+
/// The value, in satoshis, that must always be held in the channel for our counterparty. This
383+
/// value ensures that if our counterparty broadcasts a revoked state, we can punish them by
384+
/// claiming at least this value on chain.
385+
///
386+
/// This value is not included in [`inbound_capacity_msat`] as it can never be spent.
387+
///
388+
/// [`inbound_capacity_msat`]: ChannelDetails::inbound_capacity_msat
389+
pub unspendable_punishment_reserve: u64,
390+
/// Information on the fees and requirements that the counterparty requires when forwarding
391+
/// payments to us through this channel.
392+
pub forwarding_info: Option<CounterpartyForwardingInfo>,
393+
/// The smallest value HTLC (in msat) the remote peer will accept, for this channel. This field
394+
/// is only `None` before we have received either the `OpenChannel` or `AcceptChannel` message
395+
/// from the remote peer, or for `ChannelCounterparty` objects serialized prior to LDK 0.0.107.
396+
pub outbound_htlc_minimum_msat: Option<u64>,
397+
/// The largest value HTLC (in msat) the remote peer currently will accept, for this channel.
398+
pub outbound_htlc_maximum_msat: Option<u64>,
399+
}
400+
349401
/// Details of a channel as returned by [`Node::list_channels`].
350402
///
351403
/// When a channel is spliced, most fields continue to refer to the original pre-splice channel
@@ -362,8 +414,8 @@ pub struct ChannelDetails {
362414
/// Note that this means this value is *not* persistent - it can change once during the
363415
/// lifetime of the channel.
364416
pub channel_id: ChannelId,
365-
/// The node ID of our the channel's counterparty.
366-
pub counterparty_node_id: PublicKey,
417+
/// Parameters which apply to our counterparty. See individual fields for more information.
418+
pub counterparty: ChannelCounterparty,
367419
/// The channel's funding transaction output, if we've negotiated the funding transaction with
368420
/// our counterparty already.
369421
///
@@ -479,28 +531,6 @@ pub struct ChannelDetails {
479531
/// The difference in the CLTV value between incoming HTLCs and an outbound HTLC forwarded over
480532
/// the channel.
481533
pub cltv_expiry_delta: Option<u16>,
482-
/// The value, in satoshis, that must always be held in the channel for our counterparty. This
483-
/// value ensures that if our counterparty broadcasts a revoked state, we can punish them by
484-
/// claiming at least this value on chain.
485-
///
486-
/// This value is not included in [`inbound_capacity_msat`] as it can never be spent.
487-
///
488-
/// [`inbound_capacity_msat`]: ChannelDetails::inbound_capacity_msat
489-
pub counterparty_unspendable_punishment_reserve: u64,
490-
/// The smallest value HTLC (in msat) the remote peer will accept, for this channel.
491-
///
492-
/// This field is only `None` before we have received either the `OpenChannel` or
493-
/// `AcceptChannel` message from the remote peer.
494-
pub counterparty_outbound_htlc_minimum_msat: Option<u64>,
495-
/// The largest value HTLC (in msat) the remote peer currently will accept, for this channel.
496-
pub counterparty_outbound_htlc_maximum_msat: Option<u64>,
497-
/// Base routing fee in millisatoshis.
498-
pub counterparty_forwarding_info_fee_base_msat: Option<u32>,
499-
/// Proportional fee, in millionths of a satoshi the channel will charge per transferred satoshi.
500-
pub counterparty_forwarding_info_fee_proportional_millionths: Option<u32>,
501-
/// The minimum difference in CLTV expiry between an ingoing HTLC and its outgoing counterpart,
502-
/// such that the outgoing HTLC is forwardable to this counterparty.
503-
pub counterparty_forwarding_info_cltv_expiry_delta: Option<u16>,
504534
/// The available outbound capacity for sending a single HTLC to the remote peer. This is
505535
/// similar to [`ChannelDetails::outbound_capacity_msat`] but it may be further restricted by
506536
/// the current state and per-HTLC limit(s). This is intended for use when routing, allowing us
@@ -534,7 +564,14 @@ impl From<LdkChannelDetails> for ChannelDetails {
534564
fn from(value: LdkChannelDetails) -> Self {
535565
ChannelDetails {
536566
channel_id: value.channel_id,
537-
counterparty_node_id: value.counterparty.node_id,
567+
counterparty: ChannelCounterparty {
568+
node_id: value.counterparty.node_id,
569+
features: value.counterparty.features,
570+
unspendable_punishment_reserve: value.counterparty.unspendable_punishment_reserve,
571+
forwarding_info: value.counterparty.forwarding_info,
572+
outbound_htlc_minimum_msat: value.counterparty.outbound_htlc_minimum_msat,
573+
outbound_htlc_maximum_msat: value.counterparty.outbound_htlc_maximum_msat,
574+
},
538575
funding_txo: value.funding_txo.map(|o| o.into_bitcoin_outpoint()),
539576
funding_redeem_script: value.funding_redeem_script,
540577
short_channel_id: value.short_channel_id,
@@ -555,26 +592,6 @@ impl From<LdkChannelDetails> for ChannelDetails {
555592
is_usable: value.is_usable,
556593
is_announced: value.is_announced,
557594
cltv_expiry_delta: value.config.map(|c| c.cltv_expiry_delta),
558-
counterparty_unspendable_punishment_reserve: value
559-
.counterparty
560-
.unspendable_punishment_reserve,
561-
counterparty_outbound_htlc_minimum_msat: value.counterparty.outbound_htlc_minimum_msat,
562-
counterparty_outbound_htlc_maximum_msat: value.counterparty.outbound_htlc_maximum_msat,
563-
counterparty_forwarding_info_fee_base_msat: value
564-
.counterparty
565-
.forwarding_info
566-
.as_ref()
567-
.map(|f| f.fee_base_msat),
568-
counterparty_forwarding_info_fee_proportional_millionths: value
569-
.counterparty
570-
.forwarding_info
571-
.as_ref()
572-
.map(|f| f.fee_proportional_millionths),
573-
counterparty_forwarding_info_cltv_expiry_delta: value
574-
.counterparty
575-
.forwarding_info
576-
.as_ref()
577-
.map(|f| f.cltv_expiry_delta),
578595
next_outbound_htlc_limit_msat: value.next_outbound_htlc_limit_msat,
579596
next_outbound_htlc_minimum_msat: value.next_outbound_htlc_minimum_msat,
580597
force_close_spend_delay: value.force_close_spend_delay,

tests/integration_tests_rust.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2112,7 +2112,7 @@ async fn lsps2_client_trusts_lsp() {
21122112
client_node
21132113
.list_channels()
21142114
.iter()
2115-
.find(|c| c.counterparty_node_id == service_node_id)
2115+
.find(|c| c.counterparty.node_id == service_node_id)
21162116
.unwrap()
21172117
.confirmations,
21182118
Some(0)
@@ -2121,7 +2121,7 @@ async fn lsps2_client_trusts_lsp() {
21212121
service_node
21222122
.list_channels()
21232123
.iter()
2124-
.find(|c| c.counterparty_node_id == client_node_id)
2124+
.find(|c| c.counterparty.node_id == client_node_id)
21252125
.unwrap()
21262126
.confirmations,
21272127
Some(0)
@@ -2156,7 +2156,7 @@ async fn lsps2_client_trusts_lsp() {
21562156
client_node
21572157
.list_channels()
21582158
.iter()
2159-
.find(|c| c.counterparty_node_id == service_node_id)
2159+
.find(|c| c.counterparty.node_id == service_node_id)
21602160
.unwrap()
21612161
.confirmations,
21622162
Some(6)
@@ -2165,7 +2165,7 @@ async fn lsps2_client_trusts_lsp() {
21652165
service_node
21662166
.list_channels()
21672167
.iter()
2168-
.find(|c| c.counterparty_node_id == client_node_id)
2168+
.find(|c| c.counterparty.node_id == client_node_id)
21692169
.unwrap()
21702170
.confirmations,
21712171
Some(6)
@@ -2284,7 +2284,7 @@ async fn lsps2_lsp_trusts_client_but_client_does_not_claim() {
22842284
client_node
22852285
.list_channels()
22862286
.iter()
2287-
.find(|c| c.counterparty_node_id == service_node_id)
2287+
.find(|c| c.counterparty.node_id == service_node_id)
22882288
.unwrap()
22892289
.confirmations,
22902290
Some(6)
@@ -2293,7 +2293,7 @@ async fn lsps2_lsp_trusts_client_but_client_does_not_claim() {
22932293
service_node
22942294
.list_channels()
22952295
.iter()
2296-
.find(|c| c.counterparty_node_id == client_node_id)
2296+
.find(|c| c.counterparty.node_id == client_node_id)
22972297
.unwrap()
22982298
.confirmations,
22992299
Some(6)
@@ -2670,7 +2670,7 @@ async fn open_channel_with_all_with_anchors() {
26702670
assert_eq!(channels.len(), 1);
26712671
let channel = &channels[0];
26722672
assert!(channel.channel_value_sats > premine_amount_sat - anchor_reserve_sat - 500);
2673-
assert_eq!(channel.counterparty_node_id, node_b.node_id());
2673+
assert_eq!(channel.counterparty.node_id, node_b.node_id());
26742674
assert_eq!(channel.funding_txo.unwrap(), funding_txo);
26752675

26762676
node_a.stop().unwrap();
@@ -2721,7 +2721,7 @@ async fn open_channel_with_all_without_anchors() {
27212721
assert_eq!(channels.len(), 1);
27222722
let channel = &channels[0];
27232723
assert!(channel.channel_value_sats > premine_amount_sat - 500);
2724-
assert_eq!(channel.counterparty_node_id, node_b.node_id());
2724+
assert_eq!(channel.counterparty.node_id, node_b.node_id());
27252725
assert_eq!(channel.funding_txo.unwrap(), funding_txo);
27262726

27272727
node_a.stop().unwrap();

0 commit comments

Comments
 (0)