Skip to content

Commit

Permalink
add functionality to periodically update routing scores from an exter…
Browse files Browse the repository at this point in the history
…nal http source
  • Loading branch information
joostjager committed Feb 4, 2025
1 parent c64d8c5 commit 7bf68af
Show file tree
Hide file tree
Showing 6 changed files with 205 additions and 12 deletions.
2 changes: 2 additions & 0 deletions bindings/ldk_node.udl
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ interface Builder {
void set_chain_source_bitcoind_rpc(string rpc_host, u16 rpc_port, string rpc_user, string rpc_password);
void set_gossip_source_p2p();
void set_gossip_source_rgs(string rgs_server_url);
void set_pathfinding_scores_source(string url);
void set_liquidity_source_lsps2(SocketAddress address, PublicKey node_id, string? token);
void set_storage_dir_path(string storage_dir_path);
void set_filesystem_logger(string? log_file_path, LogLevel? log_level);
Expand Down Expand Up @@ -273,6 +274,7 @@ dictionary NodeStatus {
u64? latest_onchain_wallet_sync_timestamp;
u64? latest_fee_rate_cache_update_timestamp;
u64? latest_rgs_snapshot_timestamp;
u64? latest_pathfinding_scores_sync_timestamp;
u64? latest_node_announcement_broadcast_timestamp;
u32? latest_channel_monitor_archival_height;
};
Expand Down
55 changes: 47 additions & 8 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use crate::logger::{log_error, log_info, LdkLogger, LogLevel, LogWriter, Logger}
use crate::message_handler::NodeCustomMessageHandler;
use crate::payment::store::PaymentStore;
use crate::peer_store::PeerStore;
use crate::scoring::BackgroundPathfindingScoresSyncer;
use crate::tx_broadcaster::TransactionBroadcaster;
use crate::types::{
ChainMonitor, ChannelManager, DynStore, GossipSync, Graph, KeysManager, MessageRouter,
Expand All @@ -41,7 +42,8 @@ use lightning::ln::peer_handler::{IgnoringMessageHandler, MessageHandler};
use lightning::routing::gossip::NodeAlias;
use lightning::routing::router::DefaultRouter;
use lightning::routing::scoring::{
ProbabilisticScorer, ProbabilisticScoringDecayParameters, ProbabilisticScoringFeeParameters,
CombinedScorer, ProbabilisticScorer, ProbabilisticScoringDecayParameters,
ProbabilisticScoringFeeParameters,
};
use lightning::sign::EntropySource;

Expand Down Expand Up @@ -97,6 +99,11 @@ enum GossipSourceConfig {
RapidGossipSync(String),
}

#[derive(Debug, Clone)]
struct PathfindingScoresSyncConfig {
url: String,
}

#[derive(Debug, Clone)]
struct LiquiditySourceConfig {
// LSPS2 service's (address, node_id, token)
Expand Down Expand Up @@ -229,6 +236,7 @@ pub struct NodeBuilder {
gossip_source_config: Option<GossipSourceConfig>,
liquidity_source_config: Option<LiquiditySourceConfig>,
log_writer_config: Option<LogWriterConfig>,
pathfinding_scores_sync_config: Option<PathfindingScoresSyncConfig>,
}

impl NodeBuilder {
Expand All @@ -245,13 +253,15 @@ impl NodeBuilder {
let gossip_source_config = None;
let liquidity_source_config = None;
let log_writer_config = None;
let scoring_source_config = None;
Self {
config,
entropy_source_config,
chain_data_source_config,
gossip_source_config,
liquidity_source_config,
log_writer_config,
pathfinding_scores_sync_config: scoring_source_config,
}
}

Expand Down Expand Up @@ -322,6 +332,14 @@ impl NodeBuilder {
self
}

/// Configures the [`Node`] instance to source its external scores from the given URL.
///
/// The external scores are merged into the local scoring system to improve routing.
pub fn set_pathfinding_scores_source(&mut self, url: String) -> &mut Self {
self.pathfinding_scores_sync_config = Some(PathfindingScoresSyncConfig { url });
self
}

/// Configures the [`Node`] instance to source its inbound liquidity from the given
/// [LSPS2](https://github.com/BitcoinAndLightningLayerSpecs/lsp/blob/main/LSPS2/README.md)
/// service.
Expand Down Expand Up @@ -540,6 +558,7 @@ impl NodeBuilder {
config,
self.chain_data_source_config.as_ref(),
self.gossip_source_config.as_ref(),
self.pathfinding_scores_sync_config.as_ref(),
self.liquidity_source_config.as_ref(),
seed_bytes,
logger,
Expand All @@ -562,6 +581,7 @@ impl NodeBuilder {
config,
self.chain_data_source_config.as_ref(),
self.gossip_source_config.as_ref(),
self.pathfinding_scores_sync_config.as_ref(),
self.liquidity_source_config.as_ref(),
seed_bytes,
logger,
Expand Down Expand Up @@ -654,6 +674,12 @@ impl ArcedNodeBuilder {
self.inner.write().unwrap().set_gossip_source_rgs(rgs_server_url);
}

/// Configures the [`Node`] instance to source its external scores from the given URL. These scores are used to
/// augment the internal pathfinding scoring system to improve routing.
pub fn set_pathfinding_scores_source(&self, url: String) {
self.inner.write().unwrap().set_pathfinding_scores_source(url);
}

/// Configures the [`Node`] instance to source its inbound liquidity from the given
/// [LSPS2](https://github.com/BitcoinAndLightningLayerSpecs/lsp/blob/main/LSPS2/README.md)
/// service.
Expand Down Expand Up @@ -806,6 +832,7 @@ impl ArcedNodeBuilder {
fn build_with_store_internal(
config: Arc<Config>, chain_data_source_config: Option<&ChainDataSourceConfig>,
gossip_source_config: Option<&GossipSourceConfig>,
pathfinding_scores_sync_config: Option<&PathfindingScoresSyncConfig>,
liquidity_source_config: Option<&LiquiditySourceConfig>, seed_bytes: [u8; 64],
logger: Arc<Logger>, kv_store: Arc<DynStore>,
) -> Result<Node, BuildError> {
Expand Down Expand Up @@ -950,26 +977,24 @@ fn build_with_store_internal(
},
};

let scorer = match io::utils::read_scorer(
let local_scorer = match io::utils::read_scorer(
Arc::clone(&kv_store),
Arc::clone(&network_graph),
Arc::clone(&logger),
) {
Ok(scorer) => Arc::new(Mutex::new(scorer)),
Ok(scorer) => scorer,
Err(e) => {
if e.kind() == std::io::ErrorKind::NotFound {
let params = ProbabilisticScoringDecayParameters::default();
Arc::new(Mutex::new(ProbabilisticScorer::new(
params,
Arc::clone(&network_graph),
Arc::clone(&logger),
)))
ProbabilisticScorer::new(params, Arc::clone(&network_graph), Arc::clone(&logger))
} else {
return Err(BuildError::ReadFailed);
}
},
};

let scorer = Arc::new(Mutex::new(CombinedScorer::new(local_scorer)));

let scoring_fee_params = ProbabilisticScoringFeeParameters::default();
let router = Arc::new(DefaultRouter::new(
Arc::clone(&network_graph),
Expand Down Expand Up @@ -1129,6 +1154,19 @@ fn build_with_store_internal(
},
};

let background_pathfinding_scores_syncer = if let Some(config) = pathfinding_scores_sync_config
{
Some(Arc::new(BackgroundPathfindingScoresSyncer::new(
config.url.clone(),
Arc::clone(&scorer),
Arc::clone(&node_metrics),
Arc::clone(&kv_store),
Arc::clone(&logger),
)))
} else {
None
};

let liquidity_source = liquidity_source_config.as_ref().and_then(|lsc| {
lsc.lsps2_service.as_ref().map(|(address, node_id, token)| {
let lsps2_client_config = Some(LSPS2ClientConfig {});
Expand Down Expand Up @@ -1303,6 +1341,7 @@ fn build_with_store_internal(
keys_manager,
network_graph,
gossip_source,
background_pathfinding_scores_syncer,
liquidity_source,
kv_store,
logger,
Expand Down
6 changes: 6 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ pub(crate) const PEER_RECONNECTION_INTERVAL: Duration = Duration::from_secs(10);
// The time in-between RGS sync attempts.
pub(crate) const RGS_SYNC_INTERVAL: Duration = Duration::from_secs(60 * 60);

// The time in-between external scores sync attempts.
pub(crate) const EXTERNAL_PATHFINDING_SCORES_SYNC_INTERVAL: Duration = Duration::from_secs(60 * 60);

// The time in-between node announcement broadcast attempts.
pub(crate) const NODE_ANN_BCAST_INTERVAL: Duration = Duration::from_secs(60 * 60);

Expand All @@ -78,6 +81,9 @@ pub(crate) const TX_BROADCAST_TIMEOUT_SECS: u64 = 5;
// The timeout after which we abort a RGS sync operation.
pub(crate) const RGS_SYNC_TIMEOUT_SECS: u64 = 5;

// The timeout after which we abort a external scores sync operation.
pub(crate) const EXTERNAL_PATHFINDING_SCORES_SYNC_TIMEOUT_SECS: u64 = 5;

// The length in bytes of our wallets' keys seed.
pub(crate) const WALLET_KEYS_SEED_LEN: usize = 64;

Expand Down
22 changes: 20 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ pub mod logger;
mod message_handler;
pub mod payment;
mod peer_store;
mod scoring;
mod sweep;
mod tx_broadcaster;
mod types;
Expand Down Expand Up @@ -122,8 +123,9 @@ pub use builder::NodeBuilder as Builder;

use chain::ChainSource;
use config::{
default_user_config, may_announce_channel, ChannelConfig, Config, NODE_ANN_BCAST_INTERVAL,
PEER_RECONNECTION_INTERVAL, RGS_SYNC_INTERVAL,
default_user_config, may_announce_channel, ChannelConfig, Config,
EXTERNAL_PATHFINDING_SCORES_SYNC_INTERVAL, EXTERNAL_PATHFINDING_SCORES_SYNC_TIMEOUT_SECS,
NODE_ANN_BCAST_INTERVAL, PEER_RECONNECTION_INTERVAL, RGS_SYNC_INTERVAL,
};
use connection::ConnectionManager;
use event::{EventHandler, EventQueue};
Expand All @@ -137,6 +139,7 @@ use payment::{
UnifiedQrPayment,
};
use peer_store::{PeerInfo, PeerStore};
use scoring::BackgroundPathfindingScoresSyncer;
use types::{
Broadcaster, BumpTransactionEventHandler, ChainMonitor, ChannelManager, DynStore, Graph,
KeysManager, OnionMessenger, PeerManager, Router, Scorer, Sweeper, Wallet,
Expand Down Expand Up @@ -190,6 +193,7 @@ pub struct Node {
keys_manager: Arc<KeysManager>,
network_graph: Arc<Graph>,
gossip_source: Arc<GossipSource>,
background_pathfinding_scores_syncer: Option<Arc<BackgroundPathfindingScoresSyncer>>,
liquidity_source: Option<Arc<LiquiditySource<Arc<Logger>>>>,
kv_store: Arc<DynStore>,
logger: Arc<Logger>,
Expand Down Expand Up @@ -304,6 +308,12 @@ impl Node {
});
}

if let Some(background_pathfinding_scores_syncer) =
self.background_pathfinding_scores_syncer.as_ref()
{
background_pathfinding_scores_syncer.start(&runtime, &self.stop_sender);
}

if let Some(listening_addresses) = &self.config.listening_addresses {
// Setup networking
let peer_manager_connection_handler = Arc::clone(&self.peer_manager);
Expand Down Expand Up @@ -725,6 +735,8 @@ impl Node {
locked_node_metrics.latest_fee_rate_cache_update_timestamp;
let latest_rgs_snapshot_timestamp =
locked_node_metrics.latest_rgs_snapshot_timestamp.map(|val| val as u64);
let latest_pathfinding_scores_sync_timestamp =
locked_node_metrics.latest_pathfinding_scores_sync_timestamp;
let latest_node_announcement_broadcast_timestamp =
locked_node_metrics.latest_node_announcement_broadcast_timestamp;
let latest_channel_monitor_archival_height =
Expand All @@ -738,6 +750,7 @@ impl Node {
latest_onchain_wallet_sync_timestamp,
latest_fee_rate_cache_update_timestamp,
latest_rgs_snapshot_timestamp,
latest_pathfinding_scores_sync_timestamp,
latest_node_announcement_broadcast_timestamp,
latest_channel_monitor_archival_height,
}
Expand Down Expand Up @@ -1547,6 +1560,8 @@ pub struct NodeStatus {
///
/// Will be `None` if RGS isn't configured or the snapshot hasn't been updated yet.
pub latest_rgs_snapshot_timestamp: Option<u64>,
/// The timestamp, in seconds since start of the UNIX epoch, when we last successfully merged external scores.
pub latest_pathfinding_scores_sync_timestamp: Option<u64>,
/// The timestamp, in seconds since start of the UNIX epoch, when we last broadcasted a node
/// announcement.
///
Expand All @@ -1565,6 +1580,7 @@ pub(crate) struct NodeMetrics {
latest_onchain_wallet_sync_timestamp: Option<u64>,
latest_fee_rate_cache_update_timestamp: Option<u64>,
latest_rgs_snapshot_timestamp: Option<u32>,
latest_pathfinding_scores_sync_timestamp: Option<u64>,
latest_node_announcement_broadcast_timestamp: Option<u64>,
latest_channel_monitor_archival_height: Option<u32>,
}
Expand All @@ -1576,6 +1592,7 @@ impl Default for NodeMetrics {
latest_onchain_wallet_sync_timestamp: None,
latest_fee_rate_cache_update_timestamp: None,
latest_rgs_snapshot_timestamp: None,
latest_pathfinding_scores_sync_timestamp: None,
latest_node_announcement_broadcast_timestamp: None,
latest_channel_monitor_archival_height: None,
}
Expand All @@ -1584,6 +1601,7 @@ impl Default for NodeMetrics {

impl_writeable_tlv_based!(NodeMetrics, {
(0, latest_lightning_wallet_sync_timestamp, option),
(1, latest_pathfinding_scores_sync_timestamp, option),
(2, latest_onchain_wallet_sync_timestamp, option),
(4, latest_fee_rate_cache_update_timestamp, option),
(6, latest_rgs_snapshot_timestamp, option),
Expand Down
Loading

0 comments on commit 7bf68af

Please sign in to comment.