Skip to content

Commit 35e4968

Browse files
committed
Expose NetworkGraph accessors
Previously, we omitted exposing details for the sake of simplicity. However, querying the network graph has a lot of utility, and is also very useful for debugging purposes. Here, we therefore give users access to the network graph, and expose slightly simplified versions of `ChannelInfo`/`NodeInfo` in bindings.
1 parent 5418cc8 commit 35e4968

File tree

5 files changed

+246
-0
lines changed

5 files changed

+246
-0
lines changed

bindings/ldk_node.udl

+45
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ interface Node {
8484
sequence<PaymentDetails> list_payments();
8585
sequence<PeerDetails> list_peers();
8686
sequence<ChannelDetails> list_channels();
87+
NetworkGraph network_graph();
8788
[Throws=NodeError]
8889
string sign_message([ByRef]sequence<u8> msg);
8990
boolean verify_signature([ByRef]sequence<u8> msg, [ByRef]string sig, [ByRef]PublicKey pkey);
@@ -167,6 +168,7 @@ enum NodeError {
167168
"InvalidPublicKey",
168169
"InvalidSecretKey",
169170
"InvalidOfferId",
171+
"InvalidNodeId",
170172
"InvalidPaymentId",
171173
"InvalidPaymentHash",
172174
"InvalidPaymentPreimage",
@@ -387,6 +389,46 @@ enum LogLevel {
387389
"Error",
388390
};
389391

392+
interface NetworkGraph {
393+
sequence<u64> list_channels();
394+
ChannelInfo? channel(u64 short_channel_id);
395+
sequence<NodeId> list_nodes();
396+
NodeInfo? node([ByRef]NodeId node_id);
397+
};
398+
399+
dictionary ChannelInfo {
400+
NodeId node_one;
401+
ChannelUpdateInfo? one_to_two;
402+
NodeId node_two;
403+
ChannelUpdateInfo? two_to_one;
404+
u64? capacity_sats;
405+
};
406+
407+
dictionary ChannelUpdateInfo {
408+
u32 last_update;
409+
boolean enabled;
410+
u16 cltv_expiry_delta;
411+
u64 htlc_minimum_msat;
412+
u64 htlc_maximum_msat;
413+
RoutingFees fees;
414+
};
415+
416+
dictionary RoutingFees {
417+
u32 base_msat;
418+
u32 proportional_millionths;
419+
};
420+
421+
dictionary NodeInfo {
422+
sequence<u64> channels;
423+
NodeAnnouncementInfo? announcement_info;
424+
};
425+
426+
dictionary NodeAnnouncementInfo {
427+
u32 last_update;
428+
string alias;
429+
sequence<SocketAddress> addresses;
430+
};
431+
390432
[Custom]
391433
typedef string Txid;
392434

@@ -399,6 +441,9 @@ typedef string SocketAddress;
399441
[Custom]
400442
typedef string PublicKey;
401443

444+
[Custom]
445+
typedef string NodeId;
446+
402447
[Custom]
403448
typedef string Address;
404449

src/error.rs

+3
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ pub enum Error {
5555
InvalidSecretKey,
5656
/// The given offer id is invalid.
5757
InvalidOfferId,
58+
/// The given node id is invalid.
59+
InvalidNodeId,
5860
/// The given payment id is invalid.
5961
InvalidPaymentId,
6062
/// The given payment hash is invalid.
@@ -120,6 +122,7 @@ impl fmt::Display for Error {
120122
Self::InvalidPublicKey => write!(f, "The given public key is invalid."),
121123
Self::InvalidSecretKey => write!(f, "The given secret key is invalid."),
122124
Self::InvalidOfferId => write!(f, "The given offer id is invalid."),
125+
Self::InvalidNodeId => write!(f, "The given node id is invalid."),
123126
Self::InvalidPaymentId => write!(f, "The given payment id is invalid."),
124127
Self::InvalidPaymentHash => write!(f, "The given payment hash is invalid."),
125128
Self::InvalidPaymentPreimage => write!(f, "The given payment preimage is invalid."),

src/graph.rs

+166
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
//! Objects for querying the network graph.
2+
3+
use crate::types::Graph;
4+
5+
use lightning::routing::gossip::NodeId;
6+
7+
#[cfg(feature = "uniffi")]
8+
use lightning::ln::msgs::SocketAddress;
9+
#[cfg(feature = "uniffi")]
10+
use lightning::routing::gossip::RoutingFees;
11+
12+
#[cfg(not(feature = "uniffi"))]
13+
use lightning::routing::gossip::{ChannelInfo, NodeInfo};
14+
15+
use std::sync::Arc;
16+
17+
/// Represents the network as nodes and channels between them.
18+
pub struct NetworkGraph {
19+
inner: Arc<Graph>,
20+
}
21+
22+
impl NetworkGraph {
23+
pub(crate) fn new(inner: Arc<Graph>) -> Self {
24+
Self { inner }
25+
}
26+
27+
/// Returns the list of channels in the graph
28+
pub fn list_channels(&self) -> Vec<u64> {
29+
self.inner.read_only().channels().unordered_keys().map(|c| *c).collect()
30+
}
31+
32+
/// Returns information on a channel with the given id.
33+
pub fn channel(&self, short_channel_id: u64) -> Option<ChannelInfo> {
34+
self.inner.read_only().channels().get(&short_channel_id).cloned().map(|c| c.into())
35+
}
36+
37+
/// Returns the list of nodes in the graph
38+
pub fn list_nodes(&self) -> Vec<NodeId> {
39+
self.inner.read_only().nodes().unordered_keys().map(|n| *n).collect()
40+
}
41+
42+
/// Returns information on a node with the given id.
43+
pub fn node(&self, node_id: &NodeId) -> Option<NodeInfo> {
44+
self.inner.read_only().nodes().get(node_id).cloned().map(|n| n.into())
45+
}
46+
}
47+
48+
/// Details about a channel (both directions).
49+
///
50+
/// Received within a channel announcement.
51+
///
52+
/// This is a simplified version of LDK's `ChannelInfo` for bindings.
53+
#[cfg(feature = "uniffi")]
54+
#[derive(Clone, Debug, PartialEq, Eq)]
55+
pub struct ChannelInfo {
56+
/// Source node of the first direction of a channel
57+
pub node_one: NodeId,
58+
/// Details about the first direction of a channel
59+
pub one_to_two: Option<ChannelUpdateInfo>,
60+
/// Source node of the second direction of a channel
61+
pub node_two: NodeId,
62+
/// Details about the second direction of a channel
63+
pub two_to_one: Option<ChannelUpdateInfo>,
64+
/// The channel capacity as seen on-chain, if chain lookup is available.
65+
pub capacity_sats: Option<u64>,
66+
}
67+
68+
#[cfg(feature = "uniffi")]
69+
impl From<lightning::routing::gossip::ChannelInfo> for ChannelInfo {
70+
fn from(value: lightning::routing::gossip::ChannelInfo) -> Self {
71+
Self {
72+
node_one: value.node_one,
73+
one_to_two: value.one_to_two.map(|u| u.into()),
74+
node_two: value.node_two,
75+
two_to_one: value.two_to_one.map(|u| u.into()),
76+
capacity_sats: value.capacity_sats,
77+
}
78+
}
79+
}
80+
81+
/// Details about one direction of a channel as received within a `ChannelUpdate`.
82+
///
83+
/// This is a simplified version of LDK's `ChannelUpdateInfo` for bindings.
84+
#[cfg(feature = "uniffi")]
85+
#[derive(Clone, Debug, PartialEq, Eq)]
86+
pub struct ChannelUpdateInfo {
87+
/// When the last update to the channel direction was issued.
88+
/// Value is opaque, as set in the announcement.
89+
pub last_update: u32,
90+
/// Whether the channel can be currently used for payments (in this one direction).
91+
pub enabled: bool,
92+
/// The difference in CLTV values that you must have when routing through this channel.
93+
pub cltv_expiry_delta: u16,
94+
/// The minimum value, which must be relayed to the next hop via the channel
95+
pub htlc_minimum_msat: u64,
96+
/// The maximum value which may be relayed to the next hop via the channel.
97+
pub htlc_maximum_msat: u64,
98+
/// Fees charged when the channel is used for routing
99+
pub fees: RoutingFees,
100+
}
101+
102+
#[cfg(feature = "uniffi")]
103+
impl From<lightning::routing::gossip::ChannelUpdateInfo> for ChannelUpdateInfo {
104+
fn from(value: lightning::routing::gossip::ChannelUpdateInfo) -> Self {
105+
Self {
106+
last_update: value.last_update,
107+
enabled: value.enabled,
108+
cltv_expiry_delta: value.cltv_expiry_delta,
109+
htlc_minimum_msat: value.htlc_minimum_msat,
110+
htlc_maximum_msat: value.htlc_maximum_msat,
111+
fees: value.fees,
112+
}
113+
}
114+
}
115+
116+
/// Details about a node in the network, known from the network announcement.
117+
///
118+
/// This is a simplified version of LDK's `NodeInfo` for bindings.
119+
#[cfg(feature = "uniffi")]
120+
#[derive(Clone, Debug, PartialEq, Eq)]
121+
pub struct NodeInfo {
122+
/// All valid channels a node has announced
123+
pub channels: Vec<u64>,
124+
/// More information about a node from node_announcement.
125+
/// Optional because we store a Node entry after learning about it from
126+
/// a channel announcement, but before receiving a node announcement.
127+
pub announcement_info: Option<NodeAnnouncementInfo>,
128+
}
129+
130+
#[cfg(feature = "uniffi")]
131+
impl From<lightning::routing::gossip::NodeInfo> for NodeInfo {
132+
fn from(value: lightning::routing::gossip::NodeInfo) -> Self {
133+
Self {
134+
channels: value.channels,
135+
announcement_info: value.announcement_info.map(|a| a.into()),
136+
}
137+
}
138+
}
139+
140+
/// Information received in the latest node_announcement from this node.
141+
///
142+
/// This is a simplified version of LDK's `NodeAnnouncementInfo` for bindings.
143+
#[cfg(feature = "uniffi")]
144+
#[derive(Clone, Debug, PartialEq, Eq)]
145+
pub struct NodeAnnouncementInfo {
146+
/// When the last known update to the node state was issued.
147+
/// Value is opaque, as set in the announcement.
148+
pub last_update: u32,
149+
/// Moniker assigned to the node.
150+
/// May be invalid or malicious (eg control chars),
151+
/// should not be exposed to the user.
152+
pub alias: String,
153+
/// List of addresses on which this node is reachable
154+
pub addresses: Vec<SocketAddress>,
155+
}
156+
157+
#[cfg(feature = "uniffi")]
158+
impl From<lightning::routing::gossip::NodeAnnouncementInfo> for NodeAnnouncementInfo {
159+
fn from(value: lightning::routing::gossip::NodeAnnouncementInfo) -> Self {
160+
Self {
161+
last_update: value.last_update,
162+
alias: value.alias.to_string(),
163+
addresses: value.addresses().iter().cloned().collect(),
164+
}
165+
}
166+
}

src/lib.rs

+14
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ mod error;
8383
mod event;
8484
mod fee_estimator;
8585
mod gossip;
86+
pub mod graph;
8687
mod hex_utils;
8788
pub mod io;
8889
mod liquidity;
@@ -128,6 +129,7 @@ use config::{
128129
use connection::ConnectionManager;
129130
use event::{EventHandler, EventQueue};
130131
use gossip::GossipSource;
132+
use graph::NetworkGraph;
131133
use liquidity::LiquiditySource;
132134
use payment::store::PaymentStore;
133135
use payment::{Bolt11Payment, Bolt12Payment, OnchainPayment, PaymentDetails, SpontaneousPayment};
@@ -1407,6 +1409,18 @@ impl Node {
14071409
peers
14081410
}
14091411

1412+
/// Returns a handler allowing to query the network graph.
1413+
#[cfg(not(feature = "uniffi"))]
1414+
pub fn network_graph(&self) -> NetworkGraph {
1415+
NetworkGraph::new(Arc::clone(&self.network_graph))
1416+
}
1417+
1418+
/// Returns a handler allowing to query the network graph.
1419+
#[cfg(feature = "uniffi")]
1420+
pub fn network_graph(&self) -> Arc<NetworkGraph> {
1421+
Arc::new(NetworkGraph::new(Arc::clone(&self.network_graph)))
1422+
}
1423+
14101424
/// Creates a digital ECDSA signature of a message with the node's secret key.
14111425
///
14121426
/// A receiver knowing the corresponding `PublicKey` (e.g. the node’s id) and the message

src/uniffi_types.rs

+18
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
pub use crate::graph::{ChannelInfo, ChannelUpdateInfo, NodeAnnouncementInfo, NodeInfo};
12
pub use crate::payment::store::{LSPFeeLimits, PaymentDirection, PaymentKind, PaymentStatus};
23

34
pub use lightning::events::{ClosureReason, PaymentFailureReason};
45
pub use lightning::ln::{ChannelId, PaymentHash, PaymentPreimage, PaymentSecret};
56
pub use lightning::offers::invoice::Bolt12Invoice;
67
pub use lightning::offers::offer::{Offer, OfferId};
78
pub use lightning::offers::refund::Refund;
9+
pub use lightning::routing::gossip::{NodeId, RoutingFees};
810
pub use lightning::util::string::UntrustedString;
911

1012
pub use lightning_invoice::Bolt11Invoice;
@@ -45,6 +47,22 @@ impl UniffiCustomTypeConverter for PublicKey {
4547
}
4648
}
4749

50+
impl UniffiCustomTypeConverter for NodeId {
51+
type Builtin = String;
52+
53+
fn into_custom(val: Self::Builtin) -> uniffi::Result<Self> {
54+
if let Ok(key) = NodeId::from_str(&val) {
55+
return Ok(key);
56+
}
57+
58+
Err(Error::InvalidNodeId.into())
59+
}
60+
61+
fn from_custom(obj: Self) -> Self::Builtin {
62+
obj.to_string()
63+
}
64+
}
65+
4866
impl UniffiCustomTypeConverter for Address {
4967
type Builtin = String;
5068

0 commit comments

Comments
 (0)