Skip to content

Commit 8b5c797

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 bf7713d commit 8b5c797

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::{KVStore, KVStoreSync, MonitorUpdatingPersisterAsync};
2729
use lightning::util::ser::{Readable, Writeable, Writer};
2830
use lightning::util::sweep::OutputSweeper;
@@ -376,6 +378,56 @@ impl fmt::Display for UserChannelId {
376378
}
377379
}
378380

381+
/// Information needed for constructing an invoice route hint for this channel.
382+
#[cfg(feature = "uniffi")]
383+
#[uniffi::remote(Record)]
384+
pub struct CounterpartyForwardingInfo {
385+
/// Base routing fee in millisatoshis.
386+
pub fee_base_msat: u32,
387+
/// Amount in millionths of a satoshi the channel will charge per transferred satoshi.
388+
pub fee_proportional_millionths: u32,
389+
/// The minimum difference in cltv_expiry between an ingoing HTLC and its outgoing counterpart,
390+
/// such that the outgoing HTLC is forwardable to this counterparty.
391+
pub cltv_expiry_delta: u16,
392+
}
393+
394+
#[cfg(feature = "uniffi")]
395+
uniffi::custom_type!(InitFeatures, Vec<u8>, {
396+
remote,
397+
try_lift: |val| Ok(InitFeatures::from_le_bytes(val)),
398+
lower: |obj| obj.le_flags().to_vec(),
399+
});
400+
401+
/// Channel parameters which apply to our counterparty. These are split out from [`ChannelDetails`]
402+
/// to better separate parameters.
403+
#[derive(Clone, Debug, PartialEq)]
404+
#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
405+
pub struct ChannelCounterparty {
406+
/// The node_id of our counterparty
407+
pub node_id: PublicKey,
408+
/// The Features the channel counterparty provided upon last connection.
409+
/// Useful for routing as it is the most up-to-date copy of the counterparty's features and
410+
/// many routing-relevant features are present in the init context.
411+
pub features: InitFeatures,
412+
/// The value, in satoshis, that must always be held in the channel for our counterparty. This
413+
/// value ensures that if our counterparty broadcasts a revoked state, we can punish them by
414+
/// claiming at least this value on chain.
415+
///
416+
/// This value is not included in [`inbound_capacity_msat`] as it can never be spent.
417+
///
418+
/// [`inbound_capacity_msat`]: ChannelDetails::inbound_capacity_msat
419+
pub unspendable_punishment_reserve: u64,
420+
/// Information on the fees and requirements that the counterparty requires when forwarding
421+
/// payments to us through this channel.
422+
pub forwarding_info: Option<CounterpartyForwardingInfo>,
423+
/// The smallest value HTLC (in msat) the remote peer will accept, for this channel. This field
424+
/// is only `None` before we have received either the `OpenChannel` or `AcceptChannel` message
425+
/// from the remote peer, or for `ChannelCounterparty` objects serialized prior to LDK 0.0.107.
426+
pub outbound_htlc_minimum_msat: Option<u64>,
427+
/// The largest value HTLC (in msat) the remote peer currently will accept, for this channel.
428+
pub outbound_htlc_maximum_msat: Option<u64>,
429+
}
430+
379431
/// Details of a channel as returned by [`Node::list_channels`].
380432
///
381433
/// When a channel is spliced, most fields continue to refer to the original pre-splice channel
@@ -392,8 +444,8 @@ pub struct ChannelDetails {
392444
/// Note that this means this value is *not* persistent - it can change once during the
393445
/// lifetime of the channel.
394446
pub channel_id: ChannelId,
395-
/// The node ID of our the channel's counterparty.
396-
pub counterparty_node_id: PublicKey,
447+
/// Parameters which apply to our counterparty. See individual fields for more information.
448+
pub counterparty: ChannelCounterparty,
397449
/// The channel's funding transaction output, if we've negotiated the funding transaction with
398450
/// our counterparty already.
399451
///
@@ -509,28 +561,6 @@ pub struct ChannelDetails {
509561
/// The difference in the CLTV value between incoming HTLCs and an outbound HTLC forwarded over
510562
/// the channel.
511563
pub cltv_expiry_delta: Option<u16>,
512-
/// The value, in satoshis, that must always be held in the channel for our counterparty. This
513-
/// value ensures that if our counterparty broadcasts a revoked state, we can punish them by
514-
/// claiming at least this value on chain.
515-
///
516-
/// This value is not included in [`inbound_capacity_msat`] as it can never be spent.
517-
///
518-
/// [`inbound_capacity_msat`]: ChannelDetails::inbound_capacity_msat
519-
pub counterparty_unspendable_punishment_reserve: u64,
520-
/// The smallest value HTLC (in msat) the remote peer will accept, for this channel.
521-
///
522-
/// This field is only `None` before we have received either the `OpenChannel` or
523-
/// `AcceptChannel` message from the remote peer.
524-
pub counterparty_outbound_htlc_minimum_msat: Option<u64>,
525-
/// The largest value HTLC (in msat) the remote peer currently will accept, for this channel.
526-
pub counterparty_outbound_htlc_maximum_msat: Option<u64>,
527-
/// Base routing fee in millisatoshis.
528-
pub counterparty_forwarding_info_fee_base_msat: Option<u32>,
529-
/// Proportional fee, in millionths of a satoshi the channel will charge per transferred satoshi.
530-
pub counterparty_forwarding_info_fee_proportional_millionths: Option<u32>,
531-
/// The minimum difference in CLTV expiry between an ingoing HTLC and its outgoing counterpart,
532-
/// such that the outgoing HTLC is forwardable to this counterparty.
533-
pub counterparty_forwarding_info_cltv_expiry_delta: Option<u16>,
534564
/// The available outbound capacity for sending a single HTLC to the remote peer. This is
535565
/// similar to [`ChannelDetails::outbound_capacity_msat`] but it may be further restricted by
536566
/// the current state and per-HTLC limit(s). This is intended for use when routing, allowing us
@@ -564,7 +594,14 @@ impl From<LdkChannelDetails> for ChannelDetails {
564594
fn from(value: LdkChannelDetails) -> Self {
565595
ChannelDetails {
566596
channel_id: value.channel_id,
567-
counterparty_node_id: value.counterparty.node_id,
597+
counterparty: ChannelCounterparty {
598+
node_id: value.counterparty.node_id,
599+
features: value.counterparty.features,
600+
unspendable_punishment_reserve: value.counterparty.unspendable_punishment_reserve,
601+
forwarding_info: value.counterparty.forwarding_info,
602+
outbound_htlc_minimum_msat: value.counterparty.outbound_htlc_minimum_msat,
603+
outbound_htlc_maximum_msat: value.counterparty.outbound_htlc_maximum_msat,
604+
},
568605
funding_txo: value.funding_txo.map(|o| o.into_bitcoin_outpoint()),
569606
funding_redeem_script: value.funding_redeem_script,
570607
short_channel_id: value.short_channel_id,
@@ -585,26 +622,6 @@ impl From<LdkChannelDetails> for ChannelDetails {
585622
is_usable: value.is_usable,
586623
is_announced: value.is_announced,
587624
cltv_expiry_delta: value.config.map(|c| c.cltv_expiry_delta),
588-
counterparty_unspendable_punishment_reserve: value
589-
.counterparty
590-
.unspendable_punishment_reserve,
591-
counterparty_outbound_htlc_minimum_msat: value.counterparty.outbound_htlc_minimum_msat,
592-
counterparty_outbound_htlc_maximum_msat: value.counterparty.outbound_htlc_maximum_msat,
593-
counterparty_forwarding_info_fee_base_msat: value
594-
.counterparty
595-
.forwarding_info
596-
.as_ref()
597-
.map(|f| f.fee_base_msat),
598-
counterparty_forwarding_info_fee_proportional_millionths: value
599-
.counterparty
600-
.forwarding_info
601-
.as_ref()
602-
.map(|f| f.fee_proportional_millionths),
603-
counterparty_forwarding_info_cltv_expiry_delta: value
604-
.counterparty
605-
.forwarding_info
606-
.as_ref()
607-
.map(|f| f.cltv_expiry_delta),
608625
next_outbound_htlc_limit_msat: value.next_outbound_htlc_limit_msat,
609626
next_outbound_htlc_minimum_msat: value.next_outbound_htlc_minimum_msat,
610627
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
@@ -2114,7 +2114,7 @@ async fn lsps2_client_trusts_lsp() {
21142114
client_node
21152115
.list_channels()
21162116
.iter()
2117-
.find(|c| c.counterparty_node_id == service_node_id)
2117+
.find(|c| c.counterparty.node_id == service_node_id)
21182118
.unwrap()
21192119
.confirmations,
21202120
Some(0)
@@ -2123,7 +2123,7 @@ async fn lsps2_client_trusts_lsp() {
21232123
service_node
21242124
.list_channels()
21252125
.iter()
2126-
.find(|c| c.counterparty_node_id == client_node_id)
2126+
.find(|c| c.counterparty.node_id == client_node_id)
21272127
.unwrap()
21282128
.confirmations,
21292129
Some(0)
@@ -2158,7 +2158,7 @@ async fn lsps2_client_trusts_lsp() {
21582158
client_node
21592159
.list_channels()
21602160
.iter()
2161-
.find(|c| c.counterparty_node_id == service_node_id)
2161+
.find(|c| c.counterparty.node_id == service_node_id)
21622162
.unwrap()
21632163
.confirmations,
21642164
Some(6)
@@ -2167,7 +2167,7 @@ async fn lsps2_client_trusts_lsp() {
21672167
service_node
21682168
.list_channels()
21692169
.iter()
2170-
.find(|c| c.counterparty_node_id == client_node_id)
2170+
.find(|c| c.counterparty.node_id == client_node_id)
21712171
.unwrap()
21722172
.confirmations,
21732173
Some(6)
@@ -2286,7 +2286,7 @@ async fn lsps2_lsp_trusts_client_but_client_does_not_claim() {
22862286
client_node
22872287
.list_channels()
22882288
.iter()
2289-
.find(|c| c.counterparty_node_id == service_node_id)
2289+
.find(|c| c.counterparty.node_id == service_node_id)
22902290
.unwrap()
22912291
.confirmations,
22922292
Some(6)
@@ -2295,7 +2295,7 @@ async fn lsps2_lsp_trusts_client_but_client_does_not_claim() {
22952295
service_node
22962296
.list_channels()
22972297
.iter()
2298-
.find(|c| c.counterparty_node_id == client_node_id)
2298+
.find(|c| c.counterparty.node_id == client_node_id)
22992299
.unwrap()
23002300
.confirmations,
23012301
Some(6)
@@ -2738,7 +2738,7 @@ async fn open_channel_with_all_with_anchors() {
27382738
assert_eq!(channels.len(), 1);
27392739
let channel = &channels[0];
27402740
assert!(channel.channel_value_sats > premine_amount_sat - anchor_reserve_sat - 500);
2741-
assert_eq!(channel.counterparty_node_id, node_b.node_id());
2741+
assert_eq!(channel.counterparty.node_id, node_b.node_id());
27422742
assert_eq!(channel.funding_txo.unwrap(), funding_txo);
27432743

27442744
node_a.stop().unwrap();
@@ -2789,7 +2789,7 @@ async fn open_channel_with_all_without_anchors() {
27892789
assert_eq!(channels.len(), 1);
27902790
let channel = &channels[0];
27912791
assert!(channel.channel_value_sats > premine_amount_sat - 500);
2792-
assert_eq!(channel.counterparty_node_id, node_b.node_id());
2792+
assert_eq!(channel.counterparty.node_id, node_b.node_id());
27932793
assert_eq!(channel.funding_txo.unwrap(), funding_txo);
27942794

27952795
node_a.stop().unwrap();

0 commit comments

Comments
 (0)