Skip to content

Commit 7ffa252

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. Additionally, we expose the complete set of supported features present in init message to FFI.
1 parent 109978d commit 7ffa252

3 files changed

Lines changed: 304 additions & 55 deletions

File tree

src/ffi/types.rs

Lines changed: 244 additions & 1 deletion
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,6 +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::InitFeatures as LdkInitFeatures;
4748
pub use lightning_types::payment::{PaymentHash, PaymentPreimage, PaymentSecret};
4849
pub use lightning_types::string::UntrustedString;
4950
use vss_client::headers::{
@@ -1519,6 +1520,248 @@ pub enum ClosureReason {
15191520
},
15201521
}
15211522

1523+
#[derive(Debug, Clone, PartialEq, Eq, uniffi::Object)]
1524+
#[uniffi::export(Debug, Eq)]
1525+
pub struct InitFeatures {
1526+
pub(crate) inner: LdkInitFeatures,
1527+
}
1528+
1529+
impl InitFeatures {
1530+
/// Constructs init features from big-endian BOLT 9 encoded bytes.
1531+
#[uniffi::constructor]
1532+
pub fn from_bytes(bytes: &[u8]) -> Self {
1533+
Self { inner: LdkInitFeatures::from_be_bytes(bytes.to_vec()).into() }
1534+
}
1535+
1536+
/// Returns the BOLT 9 big-endian encoded representation of these features.
1537+
pub fn to_bytes(&self) -> Vec<u8> {
1538+
self.inner.encode()
1539+
}
1540+
1541+
/// Whether the peer supports `option_static_remotekey` (bit 13).
1542+
///
1543+
/// This ensures the non-broadcaster's output pays directly to their specified key,
1544+
/// simplifying recovery if a channel is force-closed.
1545+
pub fn supports_static_remote_key(&self) -> bool {
1546+
self.inner.supports_static_remote_key()
1547+
}
1548+
1549+
/// Whether the peer supports `option_anchors_zero_fee_htlc_tx` (bit 23).
1550+
///
1551+
/// Anchor channels allow fee-bumping commitment transactions after broadcast,
1552+
/// improving on-chain fee management.
1553+
pub fn supports_anchors_zero_fee_htlc_tx(&self) -> bool {
1554+
self.inner.supports_anchors_zero_fee_htlc_tx()
1555+
}
1556+
1557+
/// Whether the peer supports `option_anchors_nonzero_fee_htlc_tx` (bit 21).
1558+
///
1559+
/// The initial version of anchor outputs, which was later found to be
1560+
/// vulnerable and superseded by `option_anchors_zero_fee_htlc_tx`.
1561+
pub fn supports_anchors_nonzero_fee_htlc_tx(&self) -> bool {
1562+
self.inner.supports_anchors_nonzero_fee_htlc_tx()
1563+
}
1564+
1565+
/// Whether the peer supports `option_support_large_channel` (bit 19).
1566+
///
1567+
/// When supported, channels larger than 2^24 satoshis (≈0.168 BTC) may be opened.
1568+
pub fn supports_wumbo(&self) -> bool {
1569+
self.inner.supports_wumbo()
1570+
}
1571+
1572+
/// Whether the peer supports `option_route_blinding` (bit 25).
1573+
///
1574+
/// Route blinding allows the recipient to hide their node identity and
1575+
/// last-hop channel from the sender.
1576+
pub fn supports_route_blinding(&self) -> bool {
1577+
self.inner.supports_route_blinding()
1578+
}
1579+
1580+
/// Whether the peer supports `option_onion_messages` (bit 39).
1581+
///
1582+
/// Onion messages enable communication over the Lightning Network without
1583+
/// requiring a payment, used by BOLT 12 offers and async payments.
1584+
pub fn supports_onion_messages(&self) -> bool {
1585+
self.inner.supports_onion_messages()
1586+
}
1587+
1588+
/// Whether the peer supports `option_scid_alias` (bit 47).
1589+
///
1590+
/// When supported, the peer will only forward using short channel ID aliases,
1591+
/// preventing the real channel UTXO from being revealed during routing.
1592+
pub fn supports_scid_privacy(&self) -> bool {
1593+
self.inner.supports_scid_privacy()
1594+
}
1595+
1596+
/// Whether the peer supports `option_zeroconf` (bit 51).
1597+
///
1598+
/// Zero-conf channels can be used immediately without waiting for
1599+
/// on-chain funding confirmations.
1600+
pub fn supports_zero_conf(&self) -> bool {
1601+
self.inner.supports_zero_conf()
1602+
}
1603+
1604+
/// Whether the peer supports `option_dual_fund` (bit 29).
1605+
///
1606+
/// Dual-funded channels allow both parties to contribute funds
1607+
/// to the channel opening transaction.
1608+
pub fn supports_dual_fund(&self) -> bool {
1609+
self.inner.supports_dual_fund()
1610+
}
1611+
1612+
/// Whether the peer supports `option_quiesce` (bit 35).
1613+
///
1614+
/// Quiescence is a prerequisite for splicing, allowing both sides to
1615+
/// pause HTLC activity before modifying the funding transaction.
1616+
pub fn supports_quiescence(&self) -> bool {
1617+
self.inner.supports_quiescence()
1618+
}
1619+
1620+
/// Whether the peer supports `option_data_loss_protect` (bit 1).
1621+
///
1622+
/// Allows a node that has fallen behind (e.g., restored from backup)
1623+
/// to detect that it is out of date and close the channel safely.
1624+
pub fn supports_data_loss_protect(&self) -> bool {
1625+
self.inner.supports_data_loss_protect()
1626+
}
1627+
1628+
/// Whether the peer supports `option_upfront_shutdown_script` (bit 5).
1629+
///
1630+
/// Commits to a shutdown scriptpubkey when opening a channel,
1631+
/// preventing a compromised key from redirecting closing funds.
1632+
pub fn supports_upfront_shutdown_script(&self) -> bool {
1633+
self.inner.supports_upfront_shutdown_script()
1634+
}
1635+
1636+
/// Whether the peer supports `gossip_queries` (bit 7).
1637+
///
1638+
/// Indicates the peer has useful gossip to share and supports
1639+
/// gossip query messages for synchronization.
1640+
pub fn supports_gossip_queries(&self) -> bool {
1641+
self.inner.supports_gossip_queries()
1642+
}
1643+
1644+
/// Whether the peer supports `var_onion_optin` (bit 9).
1645+
///
1646+
/// Requires variable-length routing onion payloads, which is
1647+
/// assumed to be supported by all modern Lightning nodes.
1648+
pub fn supports_variable_length_onion(&self) -> bool {
1649+
self.inner.supports_variable_length_onion()
1650+
}
1651+
1652+
/// Whether the peer supports `payment_secret` (bit 15).
1653+
///
1654+
/// Payment secrets prevent forwarding nodes from probing
1655+
/// payment recipients. Assumed to be supported by all modern nodes.
1656+
pub fn supports_payment_secret(&self) -> bool {
1657+
self.inner.supports_payment_secret()
1658+
}
1659+
1660+
/// Whether the peer supports `basic_mpp` (bit 17).
1661+
///
1662+
/// Multi-part payments allow splitting a payment across multiple
1663+
/// routes for improved reliability and liquidity utilization.
1664+
pub fn supports_basic_mpp(&self) -> bool {
1665+
self.inner.supports_basic_mpp()
1666+
}
1667+
1668+
/// Whether the peer supports `opt_shutdown_anysegwit` (bit 27).
1669+
///
1670+
/// Allows future segwit versions in the shutdown script,
1671+
/// enabling closing to Taproot or later output types.
1672+
pub fn supports_shutdown_anysegwit(&self) -> bool {
1673+
self.inner.supports_shutdown_anysegwit()
1674+
}
1675+
1676+
/// Whether the peer supports `option_channel_type` (bit 45).
1677+
///
1678+
/// Supports explicit channel type negotiation during channel opening.
1679+
pub fn supports_channel_type(&self) -> bool {
1680+
self.inner.supports_channel_type()
1681+
}
1682+
1683+
/// Whether the peer supports `option_trampoline` (bit 57).
1684+
///
1685+
/// Trampoline routing allows lightweight nodes to delegate
1686+
/// pathfinding to an intermediate trampoline node.
1687+
pub fn supports_trampoline_routing(&self) -> bool {
1688+
self.inner.supports_trampoline_routing()
1689+
}
1690+
1691+
/// Whether the peer supports `option_simple_close` (bit 61).
1692+
///
1693+
/// Simplified closing negotiation reduces the number of
1694+
/// round trips needed for a cooperative channel close.
1695+
pub fn supports_simple_close(&self) -> bool {
1696+
self.inner.supports_simple_close()
1697+
}
1698+
1699+
/// Whether the peer supports `option_splice` (bit 63).
1700+
///
1701+
/// Splicing allows replacing the funding transaction with a new one,
1702+
/// enabling on-the-fly capacity changes without closing the channel.
1703+
pub fn supports_splicing(&self) -> bool {
1704+
self.inner.supports_splicing()
1705+
}
1706+
1707+
/// Whether the peer supports `option_provide_storage` (bit 43).
1708+
///
1709+
/// Indicates the node offers to store encrypted backup data
1710+
/// on behalf of its peers.
1711+
pub fn supports_provide_storage(&self) -> bool {
1712+
self.inner.supports_provide_storage()
1713+
}
1714+
1715+
/// Whether the peer set `initial_routing_sync` (bit 3).
1716+
///
1717+
/// Indicates the sending node needs a complete routing information dump.
1718+
/// Per BOLT #9, this feature has no even (required) bit.
1719+
pub fn initial_routing_sync(&self) -> bool {
1720+
self.inner.initial_routing_sync()
1721+
}
1722+
1723+
/// Whether the peer supports `option_taproot` (bit 31).
1724+
///
1725+
/// Taproot channels use MuSig2-based multisig for funding outputs,
1726+
/// improving privacy and efficiency.
1727+
pub fn supports_taproot(&self) -> bool {
1728+
self.inner.supports_taproot()
1729+
}
1730+
1731+
/// Whether the peer supports `option_zero_fee_commitments` (bit 141, experimental).
1732+
///
1733+
/// A channel type which always uses zero transaction fee on commitment
1734+
/// transactions, combined with anchor outputs.
1735+
pub fn supports_anchor_zero_fee_commitments(&self) -> bool {
1736+
self.inner.supports_anchor_zero_fee_commitments()
1737+
}
1738+
1739+
/// Whether the peer supports HTLC hold (bit 153, experimental).
1740+
///
1741+
/// Supports holding HTLCs and forwarding on receipt of an onion message.
1742+
pub fn supports_htlc_hold(&self) -> bool {
1743+
self.inner.supports_htlc_hold()
1744+
}
1745+
}
1746+
1747+
impl From<LdkInitFeatures> for InitFeatures {
1748+
fn from(ldk_init: LdkInitFeatures) -> Self {
1749+
Self { inner: ldk_init }
1750+
}
1751+
}
1752+
1753+
/// Information needed for constructing an invoice route hint for this channel.
1754+
#[uniffi::remote(Record)]
1755+
pub struct CounterpartyForwardingInfo {
1756+
/// Base routing fee in millisatoshis.
1757+
pub fee_base_msat: u32,
1758+
/// Amount in millionths of a satoshi the channel will charge per transferred satoshi.
1759+
pub fee_proportional_millionths: u32,
1760+
/// The minimum difference in cltv_expiry between an ingoing HTLC and its outgoing counterpart,
1761+
/// such that the outgoing HTLC is forwardable to this counterparty.
1762+
pub cltv_expiry_delta: u16,
1763+
}
1764+
15221765
#[cfg(test)]
15231766
mod tests {
15241767
use std::num::NonZeroU64;

0 commit comments

Comments
 (0)