Skip to content

Commit 5744459

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. We keep outbound_htlc_minimum_msat optional because it is unavailable before receiving OpenChannel or AcceptChannel, and re-export ChannelCounterparty so Rust consumers can name the type.
1 parent 4b1326e commit 5744459

5 files changed

Lines changed: 302 additions & 58 deletions

File tree

src/ffi/types.rs

Lines changed: 242 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ pub use bitcoin::{Address, BlockHash, Network, OutPoint, ScriptBuf, Txid};
2525
pub use lightning::chain::channelmonitor::BalanceSource;
2626
use lightning::events::PaidBolt12Invoice as LdkPaidBolt12Invoice;
2727
pub use lightning::events::{ClosureReason, PaymentFailureReason};
28-
use lightning::ln::channel_state::ChannelShutdownState;
28+
use lightning::ln::channel_state::{ChannelShutdownState, CounterpartyForwardingInfo};
2929
use lightning::ln::channelmanager::PaymentId;
3030
use lightning::ln::msgs::DecodeError;
3131
pub use lightning::ln::types::ChannelId;
@@ -44,7 +44,7 @@ pub use lightning_liquidity::lsps0::ser::LSPSDateTime;
4444
pub use lightning_liquidity::lsps1::msgs::{
4545
LSPS1ChannelInfo, LSPS1OrderId, LSPS1OrderParams, LSPS1PaymentState,
4646
};
47-
use lightning_types::features::NodeFeatures as LdkNodeFeatures;
47+
use lightning_types::features::{InitFeatures as LdkInitFeatures, NodeFeatures as LdkNodeFeatures};
4848
pub use lightning_types::payment::{PaymentHash, PaymentPreimage, PaymentSecret};
4949
pub use lightning_types::string::UntrustedString;
5050
use vss_client::headers::{
@@ -1815,6 +1815,246 @@ impl From<LdkNodeFeatures> for NodeFeatures {
18151815
}
18161816
}
18171817

1818+
#[derive(Debug, Clone, PartialEq, Eq, uniffi::Object)]
1819+
pub struct InitFeatures {
1820+
pub(crate) inner: LdkInitFeatures,
1821+
}
1822+
1823+
impl InitFeatures {
1824+
/// Constructs init features from big-endian BOLT 9 encoded bytes.
1825+
#[uniffi::constructor]
1826+
pub fn from_bytes(bytes: &[u8]) -> Self {
1827+
Self { inner: LdkInitFeatures::from_be_bytes(bytes.to_vec()).into() }
1828+
}
1829+
1830+
/// Returns the BOLT 9 big-endian encoded representation of these features.
1831+
pub fn to_bytes(&self) -> Vec<u8> {
1832+
self.inner.encode()
1833+
}
1834+
1835+
/// Whether the peer supports `option_static_remotekey`.
1836+
///
1837+
/// This ensures the non-broadcaster's output pays directly to their specified key,
1838+
/// simplifying recovery if a channel is force-closed.
1839+
pub fn supports_static_remote_key(&self) -> bool {
1840+
self.inner.supports_static_remote_key()
1841+
}
1842+
1843+
/// Whether the peer supports `option_anchors_zero_fee_htlc_tx`.
1844+
///
1845+
/// Anchor channels allow fee-bumping commitment transactions after broadcast,
1846+
/// improving on-chain fee management.
1847+
pub fn supports_anchors_zero_fee_htlc_tx(&self) -> bool {
1848+
self.inner.supports_anchors_zero_fee_htlc_tx()
1849+
}
1850+
1851+
/// Whether the peer supports `option_anchors_nonzero_fee_htlc_tx`.
1852+
///
1853+
/// The initial version of anchor outputs, which was later found to be
1854+
/// vulnerable and superseded by `option_anchors_zero_fee_htlc_tx`.
1855+
pub fn supports_anchors_nonzero_fee_htlc_tx(&self) -> bool {
1856+
self.inner.supports_anchors_nonzero_fee_htlc_tx()
1857+
}
1858+
1859+
/// Whether the peer supports `option_support_large_channel`.
1860+
///
1861+
/// When supported, channels larger than 2^24 satoshis (≈0.168 BTC) may be opened.
1862+
pub fn supports_wumbo(&self) -> bool {
1863+
self.inner.supports_wumbo()
1864+
}
1865+
1866+
/// Whether the peer supports `option_route_blinding`.
1867+
///
1868+
/// Route blinding allows the recipient to hide their node identity and
1869+
/// last-hop channel from the sender.
1870+
pub fn supports_route_blinding(&self) -> bool {
1871+
self.inner.supports_route_blinding()
1872+
}
1873+
1874+
/// Whether the peer supports `option_onion_messages`.
1875+
///
1876+
/// Onion messages enable communication over the Lightning Network without
1877+
/// requiring a payment, used by BOLT 12 offers and async payments.
1878+
pub fn supports_onion_messages(&self) -> bool {
1879+
self.inner.supports_onion_messages()
1880+
}
1881+
1882+
/// Whether the peer supports `option_scid_alias`.
1883+
///
1884+
/// When supported, the peer will only forward using short channel ID aliases,
1885+
/// preventing the real channel UTXO from being revealed during routing.
1886+
pub fn supports_scid_privacy(&self) -> bool {
1887+
self.inner.supports_scid_privacy()
1888+
}
1889+
1890+
/// Whether the peer supports `option_zeroconf`.
1891+
///
1892+
/// Zero-conf channels can be used immediately without waiting for
1893+
/// on-chain funding confirmations.
1894+
pub fn supports_zero_conf(&self) -> bool {
1895+
self.inner.supports_zero_conf()
1896+
}
1897+
1898+
/// Whether the peer supports `option_dual_fund`.
1899+
///
1900+
/// Dual-funded channels allow both parties to contribute funds
1901+
/// to the channel opening transaction.
1902+
pub fn supports_dual_fund(&self) -> bool {
1903+
self.inner.supports_dual_fund()
1904+
}
1905+
1906+
/// Whether the peer supports `option_quiesce`.
1907+
///
1908+
/// Quiescence is a prerequisite for splicing, allowing both sides to
1909+
/// pause HTLC activity before modifying the funding transaction.
1910+
pub fn supports_quiescence(&self) -> bool {
1911+
self.inner.supports_quiescence()
1912+
}
1913+
1914+
/// Whether the peer supports `option_data_loss_protect`.
1915+
///
1916+
/// Allows a node that has fallen behind (e.g., restored from backup)
1917+
/// to detect that it is out of date and close the channel safely.
1918+
pub fn supports_data_loss_protect(&self) -> bool {
1919+
self.inner.supports_data_loss_protect()
1920+
}
1921+
1922+
/// Whether the peer supports `option_upfront_shutdown_script`.
1923+
///
1924+
/// Commits to a shutdown scriptpubkey when opening a channel,
1925+
/// preventing a compromised key from redirecting closing funds.
1926+
pub fn supports_upfront_shutdown_script(&self) -> bool {
1927+
self.inner.supports_upfront_shutdown_script()
1928+
}
1929+
1930+
/// Whether the peer supports `gossip_queries`.
1931+
///
1932+
/// Indicates the peer has useful gossip to share and supports
1933+
/// gossip query messages for synchronization.
1934+
pub fn supports_gossip_queries(&self) -> bool {
1935+
self.inner.supports_gossip_queries()
1936+
}
1937+
1938+
/// Whether the peer supports `var_onion_optin`.
1939+
///
1940+
/// Requires variable-length routing onion payloads, which is
1941+
/// assumed to be supported by all modern Lightning nodes.
1942+
pub fn supports_variable_length_onion(&self) -> bool {
1943+
self.inner.supports_variable_length_onion()
1944+
}
1945+
1946+
/// Whether the peer supports `payment_secret`.
1947+
///
1948+
/// Payment secrets prevent forwarding nodes from probing
1949+
/// payment recipients. Assumed to be supported by all modern nodes.
1950+
pub fn supports_payment_secret(&self) -> bool {
1951+
self.inner.supports_payment_secret()
1952+
}
1953+
1954+
/// Whether the peer supports `basic_mpp`.
1955+
///
1956+
/// Multi-part payments allow splitting a payment across multiple
1957+
/// routes for improved reliability and liquidity utilization.
1958+
pub fn supports_basic_mpp(&self) -> bool {
1959+
self.inner.supports_basic_mpp()
1960+
}
1961+
1962+
/// Whether the peer supports `opt_shutdown_anysegwit`.
1963+
///
1964+
/// Allows future segwit versions in the shutdown script,
1965+
/// enabling closing to Taproot or later output types.
1966+
pub fn supports_shutdown_anysegwit(&self) -> bool {
1967+
self.inner.supports_shutdown_anysegwit()
1968+
}
1969+
1970+
/// Whether the peer supports `option_channel_type`.
1971+
///
1972+
/// Supports explicit channel type negotiation during channel opening.
1973+
pub fn supports_channel_type(&self) -> bool {
1974+
self.inner.supports_channel_type()
1975+
}
1976+
1977+
/// Whether the peer supports `option_trampoline`.
1978+
///
1979+
/// Trampoline routing allows lightweight nodes to delegate
1980+
/// pathfinding to an intermediate trampoline node.
1981+
pub fn supports_trampoline_routing(&self) -> bool {
1982+
self.inner.supports_trampoline_routing()
1983+
}
1984+
1985+
/// Whether the peer supports `option_simple_close`.
1986+
///
1987+
/// Simplified closing negotiation reduces the number of
1988+
/// round trips needed for a cooperative channel close.
1989+
pub fn supports_simple_close(&self) -> bool {
1990+
self.inner.supports_simple_close()
1991+
}
1992+
1993+
/// Whether the peer supports `option_splice`.
1994+
///
1995+
/// Splicing allows replacing the funding transaction with a new one,
1996+
/// enabling on-the-fly capacity changes without closing the channel.
1997+
pub fn supports_splicing(&self) -> bool {
1998+
self.inner.supports_splicing()
1999+
}
2000+
2001+
/// Whether the peer supports `option_provide_storage`.
2002+
///
2003+
/// Indicates the node offers to store encrypted backup data
2004+
/// on behalf of its peers.
2005+
pub fn supports_provide_storage(&self) -> bool {
2006+
self.inner.supports_provide_storage()
2007+
}
2008+
2009+
/// Whether the peer set `initial_routing_sync`.
2010+
///
2011+
/// Indicates the sending node needs a complete routing information dump.
2012+
/// Per BOLT #9, this feature has no even (required) bit.
2013+
pub fn initial_routing_sync(&self) -> bool {
2014+
self.inner.initial_routing_sync()
2015+
}
2016+
2017+
/// Whether the peer supports `option_taproot`.
2018+
///
2019+
/// Taproot channels use MuSig2-based multisig for funding outputs,
2020+
/// improving privacy and efficiency.
2021+
pub fn supports_taproot(&self) -> bool {
2022+
self.inner.supports_taproot()
2023+
}
2024+
2025+
/// Whether the peer supports `option_zero_fee_commitments`.
2026+
///
2027+
/// A channel type which always uses zero transaction fee on commitment
2028+
/// transactions, combined with anchor outputs.
2029+
pub fn supports_anchor_zero_fee_commitments(&self) -> bool {
2030+
self.inner.supports_anchor_zero_fee_commitments()
2031+
}
2032+
2033+
/// Whether the peer supports HTLC hold.
2034+
///
2035+
/// Supports holding HTLCs and forwarding on receipt of an onion message.
2036+
pub fn supports_htlc_hold(&self) -> bool {
2037+
self.inner.supports_htlc_hold()
2038+
}
2039+
}
2040+
2041+
impl From<LdkInitFeatures> for InitFeatures {
2042+
fn from(ldk_init: LdkInitFeatures) -> Self {
2043+
Self { inner: ldk_init }
2044+
}
2045+
}
2046+
/// Information needed for constructing an invoice route hint for this channel.
2047+
#[uniffi::remote(Record)]
2048+
pub struct CounterpartyForwardingInfo {
2049+
/// Base routing fee in millisatoshis.
2050+
pub fee_base_msat: u32,
2051+
/// Amount in millionths of a satoshi the channel will charge per transferred satoshi.
2052+
pub fee_proportional_millionths: u32,
2053+
/// The minimum difference in cltv_expiry between an ingoing HTLC and its outgoing counterpart,
2054+
/// such that the outgoing HTLC is forwardable to this counterparty.
2055+
pub cltv_expiry_delta: u16,
2056+
}
2057+
18182058
#[cfg(test)]
18192059
mod tests {
18202060
use std::num::NonZeroU64;

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ use types::{
179179
HRNResolver, KeysManager, OnionMessenger, PaymentStore, PeerManager, Router, Scorer, Sweeper,
180180
Wallet,
181181
};
182-
pub use types::{ChannelDetails, CustomTlvRecord, PeerDetails, UserChannelId};
182+
pub use types::{ChannelCounterparty, ChannelDetails, CustomTlvRecord, PeerDetails, UserChannelId};
183183
pub use vss_client;
184184

185185
use crate::ffi::maybe_wrap;

0 commit comments

Comments
 (0)