Skip to content

Commit 58acc12

Browse files
committed
add functionality to periodically update routing scores from an external http source
1 parent b388ee1 commit 58acc12

File tree

5 files changed

+135
-34
lines changed

5 files changed

+135
-34
lines changed

Cargo.toml

+23-23
Original file line numberDiff line numberDiff line change
@@ -28,27 +28,27 @@ panic = 'abort' # Abort on panic
2828
default = []
2929

3030
[dependencies]
31-
lightning = { version = "0.1.0", features = ["std"] }
32-
lightning-types = { version = "0.2.0" }
33-
lightning-invoice = { version = "0.33.0", features = ["std"] }
34-
lightning-net-tokio = { version = "0.1.0" }
35-
lightning-persister = { version = "0.1.0" }
36-
lightning-background-processor = { version = "0.1.0", features = ["futures"] }
37-
lightning-rapid-gossip-sync = { version = "0.1.0" }
38-
lightning-block-sync = { version = "0.1.0", features = ["rpc-client", "tokio"] }
39-
lightning-transaction-sync = { version = "0.1.0", features = ["esplora-async-https", "time"] }
40-
lightning-liquidity = { version = "0.1.0", features = ["std"] }
41-
42-
#lightning = { git = "https://github.com/lightningdevkit/rust-lightning", branch = "main", features = ["std"] }
43-
#lightning-types = { git = "https://github.com/lightningdevkit/rust-lightning", branch = "main" }
44-
#lightning-invoice = { git = "https://github.com/lightningdevkit/rust-lightning", branch = "main", features = ["std"] }
45-
#lightning-net-tokio = { git = "https://github.com/lightningdevkit/rust-lightning", branch = "main" }
46-
#lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", branch = "main" }
47-
#lightning-background-processor = { git = "https://github.com/lightningdevkit/rust-lightning", branch = "main", features = ["futures"] }
48-
#lightning-rapid-gossip-sync = { git = "https://github.com/lightningdevkit/rust-lightning", branch = "main" }
49-
#lightning-block-sync = { git = "https://github.com/lightningdevkit/rust-lightning", branch = "main", features = ["rpc-client", "tokio"] }
50-
#lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", branch = "main", features = ["esplora-async-https", "time"] }
51-
#lightning-liquidity = { git = "https://github.com/lightningdevkit/rust-lightning", branch = "main" }
31+
#lightning = { version = "0.1.0", features = ["std"] }
32+
#lightning-types = { version = "0.2.0" }
33+
#lightning-invoice = { version = "0.33.0", features = ["std"] }
34+
#lightning-net-tokio = { version = "0.1.0" }
35+
#lightning-persister = { version = "0.1.0" }
36+
#lightning-background-processor = { version = "0.1.0", features = ["futures"] }
37+
#lightning-rapid-gossip-sync = { version = "0.1.0" }
38+
#lightning-block-sync = { version = "0.1.0", features = ["rpc-client", "tokio"] }
39+
#lightning-transaction-sync = { version = "0.1.0", features = ["esplora-async-https", "time"] }
40+
#lightning-liquidity = { version = "0.1.0", features = ["std"] }
41+
42+
lightning = { git = "https://github.com/joostjager/rust-lightning", branch = "merge-scores", features = ["std"] }
43+
lightning-types = { git = "https://github.com/joostjager/rust-lightning", branch = "merge-scores" }
44+
lightning-invoice = { git = "https://github.com/joostjager/rust-lightning", branch = "merge-scores", features = ["std"] }
45+
lightning-net-tokio = { git = "https://github.com/joostjager/rust-lightning", branch = "merge-scores" }
46+
lightning-persister = { git = "https://github.com/joostjager/rust-lightning", branch = "merge-scores" }
47+
lightning-background-processor = { git = "https://github.com/joostjager/rust-lightning", branch = "merge-scores", features = ["futures"] }
48+
lightning-rapid-gossip-sync = { git = "https://github.com/joostjager/rust-lightning", branch = "merge-scores" }
49+
lightning-block-sync = { git = "https://github.com/joostjager/rust-lightning", branch = "merge-scores", features = ["rpc-client", "tokio"] }
50+
lightning-transaction-sync = { git = "https://github.com/joostjager/rust-lightning", branch = "merge-scores", features = ["esplora-async-https", "time"] }
51+
lightning-liquidity = { git = "https://github.com/joostjager/rust-lightning", branch = "merge-scores" }
5252

5353
#lightning = { path = "../rust-lightning/lightning", features = ["std"] }
5454
#lightning-types = { path = "../rust-lightning/lightning-types" }
@@ -88,8 +88,8 @@ prost = { version = "0.11.6", default-features = false}
8888
winapi = { version = "0.3", features = ["winbase"] }
8989

9090
[dev-dependencies]
91-
lightning = { version = "0.1.0", features = ["std", "_test_utils"] }
92-
#lightning = { git = "https://github.com/lightningdevkit/rust-lightning", branch="main", features = ["std", "_test_utils"] }
91+
#lightning = { version = "0.1.0", features = ["std", "_test_utils"] }
92+
lightning = { git = "https://github.com/joostjager/rust-lightning", branch="merge-scores", features = ["std", "_test_utils"] }
9393
#lightning = { path = "../rust-lightning/lightning", features = ["std", "_test_utils"] }
9494
electrum-client = { version = "0.21.0", default-features = true }
9595
bitcoincore-rpc = { version = "0.19.0", default-features = false }

src/builder.rs

+13-8
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ use lightning::ln::peer_handler::{IgnoringMessageHandler, MessageHandler};
3838
use lightning::routing::gossip::NodeAlias;
3939
use lightning::routing::router::DefaultRouter;
4040
use lightning::routing::scoring::{
41-
ProbabilisticScorer, ProbabilisticScoringDecayParameters, ProbabilisticScoringFeeParameters,
41+
CombinedScorer, ProbabilisticScorer, ProbabilisticScoringDecayParameters,
42+
ProbabilisticScoringFeeParameters,
4243
};
4344
use lightning::sign::EntropySource;
4445

@@ -273,6 +274,12 @@ impl NodeBuilder {
273274
self
274275
}
275276

277+
/// Configures the [`Node`] instance to source its external scores from the given URL.
278+
pub fn set_external_scores_source(&mut self, url: String) -> &mut Self {
279+
self.config.external_scores_url = Some(url);
280+
self
281+
}
282+
276283
/// Configures the [`Node`] instance to source its inbound liquidity from the given
277284
/// [LSPS2](https://github.com/BitcoinAndLightningLayerSpecs/lsp/blob/main/LSPS2/README.md)
278285
/// service.
@@ -877,26 +884,24 @@ fn build_with_store_internal(
877884
},
878885
};
879886

880-
let scorer = match io::utils::read_scorer(
887+
let local_scorer = match io::utils::read_scorer(
881888
Arc::clone(&kv_store),
882889
Arc::clone(&network_graph),
883890
Arc::clone(&logger),
884891
) {
885-
Ok(scorer) => Arc::new(Mutex::new(scorer)),
892+
Ok(scorer) => scorer,
886893
Err(e) => {
887894
if e.kind() == std::io::ErrorKind::NotFound {
888895
let params = ProbabilisticScoringDecayParameters::default();
889-
Arc::new(Mutex::new(ProbabilisticScorer::new(
890-
params,
891-
Arc::clone(&network_graph),
892-
Arc::clone(&logger),
893-
)))
896+
ProbabilisticScorer::new(params, Arc::clone(&network_graph), Arc::clone(&logger))
894897
} else {
895898
return Err(BuildError::ReadFailed);
896899
}
897900
},
898901
};
899902

903+
let scorer = Arc::new(Mutex::new(CombinedScorer::new(local_scorer)));
904+
900905
let scoring_fee_params = ProbabilisticScoringFeeParameters::default();
901906
let router = Arc::new(DefaultRouter::new(
902907
Arc::clone(&network_graph),

src/config.rs

+10
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ pub(crate) const PEER_RECONNECTION_INTERVAL: Duration = Duration::from_secs(10);
5050
// The time in-between RGS sync attempts.
5151
pub(crate) const RGS_SYNC_INTERVAL: Duration = Duration::from_secs(60 * 60);
5252

53+
// The time in-between external scores sync attempts.
54+
pub(crate) const EXTERNAL_SCORES_SYNC_INTERVAL: Duration = Duration::from_secs(60 * 60);
55+
5356
// The time in-between node announcement broadcast attempts.
5457
pub(crate) const NODE_ANN_BCAST_INTERVAL: Duration = Duration::from_secs(60 * 60);
5558

@@ -71,6 +74,9 @@ pub(crate) const TX_BROADCAST_TIMEOUT_SECS: u64 = 5;
7174
// The timeout after which we abort a RGS sync operation.
7275
pub(crate) const RGS_SYNC_TIMEOUT_SECS: u64 = 5;
7376

77+
// The timeout after which we abort a external scores sync operation.
78+
pub(crate) const EXTERNAL_SCORES_SYNC_TIMEOUT_SECS: u64 = 5;
79+
7480
// The length in bytes of our wallets' keys seed.
7581
pub(crate) const WALLET_KEYS_SEED_LEN: usize = 64;
7682

@@ -162,6 +168,9 @@ pub struct Config {
162168
/// **Note:** If unset, default parameters will be used, and you will be able to override the
163169
/// parameters on a per-payment basis in the corresponding method calls.
164170
pub sending_parameters: Option<SendingParameters>,
171+
172+
/// Optional URL to periodically fetch external scores from.
173+
pub external_scores_url: Option<String>,
165174
}
166175

167176
impl Default for Config {
@@ -177,6 +186,7 @@ impl Default for Config {
177186
anchor_channels_config: Some(AnchorChannelsConfig::default()),
178187
sending_parameters: None,
179188
node_alias: None,
189+
external_scores_url: None,
180190
}
181191
}
182192
}

src/lib.rs

+86-1
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ mod wallet;
9999
pub use bip39;
100100
pub use bitcoin;
101101
pub use lightning;
102+
use lightning::routing::scoring::{ChannelLiquidities, CombinedScorer};
103+
use lightning::util::ser::Readable;
102104
pub use lightning_invoice;
103105
pub use lightning_types;
104106
pub use vss_client;
@@ -122,7 +124,8 @@ pub use builder::NodeBuilder as Builder;
122124

123125
use chain::ChainSource;
124126
use config::{
125-
default_user_config, may_announce_channel, ChannelConfig, Config, NODE_ANN_BCAST_INTERVAL,
127+
default_user_config, may_announce_channel, ChannelConfig, Config,
128+
EXTERNAL_SCORES_SYNC_INTERVAL, EXTERNAL_SCORES_SYNC_TIMEOUT_SECS, NODE_ANN_BCAST_INTERVAL,
126129
PEER_RECONNECTION_INTERVAL, RGS_SYNC_INTERVAL,
127130
};
128131
use connection::ConnectionManager;
@@ -162,6 +165,7 @@ use bitcoin::secp256k1::PublicKey;
162165
use rand::Rng;
163166

164167
use std::default::Default;
168+
use std::io::Cursor;
165169
use std::net::ToSocketAddrs;
166170
use std::sync::atomic::{AtomicBool, Ordering};
167171
use std::sync::{Arc, Mutex, RwLock};
@@ -306,6 +310,38 @@ impl Node {
306310
});
307311
}
308312

313+
if let Some(url) = &self.config.external_scores_url {
314+
log_trace!(self.logger, "External scores background syncing from {} enabled", url);
315+
316+
let external_scores_sync_logger = Arc::clone(&self.logger);
317+
let external_scores_scorer = Arc::clone(&self.scorer);
318+
let mut stop_sync = self.stop_sender.subscribe();
319+
let url = url.clone();
320+
321+
runtime.spawn(async move {
322+
let mut interval = tokio::time::interval(EXTERNAL_SCORES_SYNC_INTERVAL);
323+
loop {
324+
tokio::select! {
325+
_ = stop_sync.changed() => {
326+
log_trace!(
327+
external_scores_sync_logger,
328+
"Stopping background syncing external scores.",
329+
);
330+
return;
331+
}
332+
_ = interval.tick() => {
333+
log_trace!(
334+
external_scores_sync_logger,
335+
"Background sync of external scores started.",
336+
);
337+
338+
sync_external_scores(&external_scores_sync_logger, &external_scores_scorer, &url).await;
339+
}
340+
}
341+
}
342+
});
343+
}
344+
309345
if let Some(listening_addresses) = &self.config.listening_addresses {
310346
// Setup networking
311347
let peer_manager_connection_handler = Arc::clone(&self.peer_manager);
@@ -1612,3 +1648,52 @@ pub(crate) fn total_anchor_channels_reserve_sats(
16121648
* anchor_channels_config.per_channel_reserve_sats
16131649
})
16141650
}
1651+
1652+
async fn sync_external_scores(
1653+
logger: &FilesystemLogger, scorer: &Mutex<CombinedScorer<Arc<Graph>, Arc<FilesystemLogger>>>,
1654+
url: &String,
1655+
) -> () {
1656+
let response = tokio::time::timeout(
1657+
Duration::from_secs(EXTERNAL_SCORES_SYNC_TIMEOUT_SECS),
1658+
reqwest::get(url),
1659+
)
1660+
.await;
1661+
1662+
match response {
1663+
Ok(Ok(response)) => {
1664+
let body = response.bytes().await;
1665+
match body {
1666+
Err(e) => {
1667+
log_trace!(
1668+
logger,
1669+
"Failed to read external scores update from http source: {}",
1670+
e
1671+
);
1672+
return;
1673+
},
1674+
Ok(body) => {
1675+
let mut reader = Cursor::new(body);
1676+
match ChannelLiquidities::read(&mut reader) {
1677+
Ok(liquidities) => {
1678+
let duration_since_epoch = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
1679+
scorer.lock().unwrap().merge(liquidities, duration_since_epoch);
1680+
log_trace!(logger, "External scores merged successfully",);
1681+
},
1682+
Err(e) => {
1683+
log_trace!(logger, "Failed to parse external scores update: {}", e);
1684+
return;
1685+
},
1686+
}
1687+
},
1688+
}
1689+
},
1690+
Err(e) => {
1691+
log_trace!(logger, "Retrieving external scores timed out: {}", e);
1692+
return;
1693+
},
1694+
Ok(Err(e)) => {
1695+
log_trace!(logger, "Failed to retrieve external scores update: {}", e);
1696+
return;
1697+
},
1698+
}
1699+
}

src/types.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ use lightning::ln::peer_handler::IgnoringMessageHandler;
2121
use lightning::ln::types::ChannelId;
2222
use lightning::routing::gossip;
2323
use lightning::routing::router::DefaultRouter;
24-
use lightning::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringFeeParameters};
24+
use lightning::routing::scoring::CombinedScorer;
25+
use lightning::routing::scoring::ProbabilisticScoringFeeParameters;
2526
use lightning::sign::InMemorySigner;
2627
use lightning::util::persist::KVStore;
2728
use lightning::util::ser::{Readable, Writeable, Writer};
@@ -91,7 +92,7 @@ pub(crate) type Router = DefaultRouter<
9192
ProbabilisticScoringFeeParameters,
9293
Scorer,
9394
>;
94-
pub(crate) type Scorer = ProbabilisticScorer<Arc<Graph>, Arc<FilesystemLogger>>;
95+
pub(crate) type Scorer = CombinedScorer<Arc<Graph>, Arc<FilesystemLogger>>;
9596

9697
pub(crate) type Graph = gossip::NetworkGraph<Arc<FilesystemLogger>>;
9798

0 commit comments

Comments
 (0)