Skip to content

Commit 44596dc

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 7932aac commit 44596dc

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
@@ -81,6 +81,7 @@ interface Node {
8181
sequence<PaymentDetails> list_payments();
8282
sequence<PeerDetails> list_peers();
8383
sequence<ChannelDetails> list_channels();
84+
NetworkGraph network_graph();
8485
[Throws=NodeError]
8586
string sign_message([ByRef]sequence<u8> msg);
8687
boolean verify_signature([ByRef]sequence<u8> msg, [ByRef]string sig, [ByRef]PublicKey pkey);
@@ -148,6 +149,7 @@ enum NodeError {
148149
"InvalidSocketAddress",
149150
"InvalidPublicKey",
150151
"InvalidSecretKey",
152+
"InvalidNodeId",
151153
"InvalidPaymentId",
152154
"InvalidPaymentHash",
153155
"InvalidPaymentPreimage",
@@ -369,6 +371,46 @@ enum LogLevel {
369371
"Error",
370372
};
371373

374+
interface NetworkGraph {
375+
sequence<u64> list_channels();
376+
ChannelInfo? channel(u64 short_channel_id);
377+
sequence<NodeId> list_nodes();
378+
NodeInfo? node([ByRef]NodeId node_id);
379+
};
380+
381+
dictionary ChannelInfo {
382+
NodeId node_one;
383+
ChannelUpdateInfo? one_to_two;
384+
NodeId node_two;
385+
ChannelUpdateInfo? two_to_one;
386+
u64? capacity_sats;
387+
};
388+
389+
dictionary ChannelUpdateInfo {
390+
u32 last_update;
391+
boolean enabled;
392+
u16 cltv_expiry_delta;
393+
u64 htlc_minimum_msat;
394+
u64 htlc_maximum_msat;
395+
RoutingFees fees;
396+
};
397+
398+
dictionary RoutingFees {
399+
u32 base_msat;
400+
u32 proportional_millionths;
401+
};
402+
403+
dictionary NodeInfo {
404+
sequence<u64> channels;
405+
NodeAnnouncementInfo? announcement_info;
406+
};
407+
408+
dictionary NodeAnnouncementInfo {
409+
u32 last_update;
410+
string alias;
411+
sequence<SocketAddress> addresses;
412+
};
413+
372414
[Custom]
373415
typedef string Txid;
374416

@@ -381,6 +423,9 @@ typedef string SocketAddress;
381423
[Custom]
382424
typedef string PublicKey;
383425

426+
[Custom]
427+
typedef string NodeId;
428+
384429
[Custom]
385430
typedef string Address;
386431

src/error.rs

+3
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ pub enum Error {
5353
InvalidPublicKey,
5454
/// The given secret key is invalid.
5555
InvalidSecretKey,
56+
/// The given node id is invalid.
57+
InvalidNodeId,
5658
/// The given payment id is invalid.
5759
InvalidPaymentId,
5860
/// The given payment hash is invalid.
@@ -113,6 +115,7 @@ impl fmt::Display for Error {
113115
Self::InvalidSocketAddress => write!(f, "The given network address is invalid."),
114116
Self::InvalidPublicKey => write!(f, "The given public key is invalid."),
115117
Self::InvalidSecretKey => write!(f, "The given secret key is invalid."),
118+
Self::InvalidNodeId => write!(f, "The given node id is invalid."),
116119
Self::InvalidPaymentId => write!(f, "The given payment id is invalid."),
117120
Self::InvalidPaymentHash => write!(f, "The given payment hash is invalid."),
118121
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, OnchainPayment, PaymentDetails, SpontaneousPayment};
@@ -1474,6 +1476,18 @@ impl Node {
14741476
peers
14751477
}
14761478

1479+
/// Returns a handler allowing to query the network graph.
1480+
#[cfg(not(feature = "uniffi"))]
1481+
pub fn network_graph(&self) -> NetworkGraph {
1482+
NetworkGraph::new(Arc::clone(&self.network_graph))
1483+
}
1484+
1485+
/// Returns a handler allowing to query the network graph.
1486+
#[cfg(feature = "uniffi")]
1487+
pub fn network_graph(&self) -> Arc<NetworkGraph> {
1488+
Arc::new(NetworkGraph::new(Arc::clone(&self.network_graph)))
1489+
}
1490+
14771491
/// Creates a digital ECDSA signature of a message with the node's secret key.
14781492
///
14791493
/// 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,7 +1,9 @@
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};
6+
pub use lightning::routing::gossip::{NodeId, RoutingFees};
57
pub use lightning::util::string::UntrustedString;
68

79
pub use lightning_invoice::Bolt11Invoice;
@@ -41,6 +43,22 @@ impl UniffiCustomTypeConverter for PublicKey {
4143
}
4244
}
4345

46+
impl UniffiCustomTypeConverter for NodeId {
47+
type Builtin = String;
48+
49+
fn into_custom(val: Self::Builtin) -> uniffi::Result<Self> {
50+
if let Ok(key) = NodeId::from_str(&val) {
51+
return Ok(key);
52+
}
53+
54+
Err(Error::InvalidNodeId.into())
55+
}
56+
57+
fn from_custom(obj: Self) -> Self::Builtin {
58+
obj.to_string()
59+
}
60+
}
61+
4462
impl UniffiCustomTypeConverter for Address {
4563
type Builtin = String;
4664

0 commit comments

Comments
 (0)