Skip to content

Commit 350c3a9

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 c21cefd commit 350c3a9

File tree

5 files changed

+246
-0
lines changed

5 files changed

+246
-0
lines changed

bindings/ldk_node.udl

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ interface Node {
7676
sequence<PaymentDetails> list_payments();
7777
sequence<PeerDetails> list_peers();
7878
sequence<ChannelDetails> list_channels();
79+
NetworkGraph network_graph();
7980
[Throws=NodeError]
8081
string sign_message([ByRef]sequence<u8> msg);
8182
boolean verify_signature([ByRef]sequence<u8> msg, [ByRef]string sig, [ByRef]PublicKey pkey);
@@ -159,6 +160,7 @@ enum NodeError {
159160
"InvalidPublicKey",
160161
"InvalidSecretKey",
161162
"InvalidOfferId",
163+
"InvalidNodeId",
162164
"InvalidPaymentId",
163165
"InvalidPaymentHash",
164166
"InvalidPaymentPreimage",
@@ -378,6 +380,46 @@ enum LogLevel {
378380
"Error",
379381
};
380382

383+
interface NetworkGraph {
384+
sequence<u64> list_channels();
385+
ChannelInfo? channel(u64 short_channel_id);
386+
sequence<NodeId> list_nodes();
387+
NodeInfo? node([ByRef]NodeId node_id);
388+
};
389+
390+
dictionary ChannelInfo {
391+
NodeId node_one;
392+
ChannelUpdateInfo? one_to_two;
393+
NodeId node_two;
394+
ChannelUpdateInfo? two_to_one;
395+
u64? capacity_sats;
396+
};
397+
398+
dictionary ChannelUpdateInfo {
399+
u32 last_update;
400+
boolean enabled;
401+
u16 cltv_expiry_delta;
402+
u64 htlc_minimum_msat;
403+
u64 htlc_maximum_msat;
404+
RoutingFees fees;
405+
};
406+
407+
dictionary RoutingFees {
408+
u32 base_msat;
409+
u32 proportional_millionths;
410+
};
411+
412+
dictionary NodeInfo {
413+
sequence<u64> channels;
414+
NodeAnnouncementInfo? announcement_info;
415+
};
416+
417+
dictionary NodeAnnouncementInfo {
418+
u32 last_update;
419+
string alias;
420+
sequence<SocketAddress> addresses;
421+
};
422+
381423
[Custom]
382424
typedef string Txid;
383425

@@ -390,6 +432,9 @@ typedef string SocketAddress;
390432
[Custom]
391433
typedef string PublicKey;
392434

435+
[Custom]
436+
typedef string NodeId;
437+
393438
[Custom]
394439
typedef string Address;
395440

src/error.rs

Lines changed: 3 additions & 0 deletions
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

Lines changed: 166 additions & 0 deletions
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

Lines changed: 14 additions & 0 deletions
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};
@@ -1299,6 +1301,18 @@ impl Node {
12991301
peers
13001302
}
13011303

1304+
/// Returns a handler allowing to query the network graph.
1305+
#[cfg(not(feature = "uniffi"))]
1306+
pub fn network_graph(&self) -> NetworkGraph {
1307+
NetworkGraph::new(Arc::clone(&self.network_graph))
1308+
}
1309+
1310+
/// Returns a handler allowing to query the network graph.
1311+
#[cfg(feature = "uniffi")]
1312+
pub fn network_graph(&self) -> Arc<NetworkGraph> {
1313+
Arc::new(NetworkGraph::new(Arc::clone(&self.network_graph)))
1314+
}
1315+
13021316
/// Creates a digital ECDSA signature of a message with the node's secret key.
13031317
///
13041318
/// A receiver knowing the corresponding `PublicKey` (e.g. the node’s id) and the message

src/uniffi_types.rs

Lines changed: 18 additions & 0 deletions
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)