Skip to content

Commit fa70af5

Browse files
authored
Expose scoring parameters in JS bindings (#37)
The ldk-node revision we now depend on supports configuring ProbabilisticScorer parameters. Pass them through the NAPI layer so JS consumers can tune routing behavior. All fields from ProbabilisticScoringFeeParameters and ProbabilisticScoringDecayParameters are available as optional overrides in MdkNodeOptions. Unset fields keep their LDK defaults. Manual node penalties use a Record<string, number> with hex node IDs as keys.
1 parent 7b4a8a3 commit fa70af5

3 files changed

Lines changed: 98 additions & 2 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ bitcoin-payment-instructions = { version = "0.5.0", default-features = false, fe
1515
"http",
1616
] }
1717
# Branch: https://github.com/moneydevkit/ldk-node/commits/lsp-0.7.0_accept-underpaying-htlcs_with_timing_logs
18-
ldk-node = { default-features = false, git = "https://github.com/moneydevkit/ldk-node.git", rev = "e935695c6ea9b33efee4cf579207ec84b40ce71f" }
18+
ldk-node = { default-features = false, git = "https://github.com/moneydevkit/ldk-node.git", rev = "5baa1f83a13407818b069b1f990157c8761eb982" }
1919
#ldk-node = { path = "../ldk-node" }
2020

2121
napi = { version = "2", features = ["napi4"] }

index.d.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,21 @@ export declare function generateMnemonic(): string
1515
* Runs in ~1ms vs ~4s for full node construction.
1616
*/
1717
export declare function deriveNodeId(mnemonicStr: string, networkStr: string): string
18+
export interface ScoringParamOverrides {
19+
basePenaltyMsat?: number
20+
basePenaltyAmountMultiplierMsat?: number
21+
liquidityPenaltyMultiplierMsat?: number
22+
liquidityPenaltyAmountMultiplierMsat?: number
23+
historicalLiquidityPenaltyMultiplierMsat?: number
24+
historicalLiquidityPenaltyAmountMultiplierMsat?: number
25+
antiProbingPenaltyMsat?: number
26+
consideredImpossiblePenaltyMsat?: number
27+
linearSuccessProbability?: boolean
28+
probingDiversityPenaltyMsat?: number
29+
liquidityOffsetHalfLifeSecs?: number
30+
historicalNoUpdatesHalfLifeSecs?: number
31+
manualNodePenalties?: Record<string, number>
32+
}
1833
export interface MdkNodeOptions {
1934
network: string
2035
mdkApiKey: string
@@ -24,6 +39,7 @@ export interface MdkNodeOptions {
2439
mnemonic: string
2540
lspNodeId: string
2641
lspAddress: string
42+
scoringParamOverrides?: ScoringParamOverrides
2743
}
2844
export interface PaymentMetadata {
2945
bolt11: string

src/lib.rs

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,13 @@ use napi::{
2828
threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode},
2929
};
3030

31+
use ldk_node::lightning::routing::gossip::NodeId;
32+
use ldk_node::lightning::routing::scoring::{
33+
ProbabilisticScoringDecayParameters, ProbabilisticScoringFeeParameters,
34+
};
3135
use ldk_node::logger::{LogLevel, LogRecord, LogWriter};
3236
use ldk_node::{
33-
Builder, Event, Node,
37+
Builder, Event, Node, ProbabilisticScoringParameters,
3438
bip39::Mnemonic,
3539
bitcoin::{
3640
Network,
@@ -263,6 +267,24 @@ fn derive_vss_identifier(mnemonic: &Mnemonic) -> String {
263267
sha256::Hash::hash(mnemonic_phrase.as_bytes()).to_string()
264268
}
265269

270+
#[napi(object)]
271+
#[derive(Default)]
272+
pub struct ScoringParamOverrides {
273+
pub base_penalty_msat: Option<i64>,
274+
pub base_penalty_amount_multiplier_msat: Option<i64>,
275+
pub liquidity_penalty_multiplier_msat: Option<i64>,
276+
pub liquidity_penalty_amount_multiplier_msat: Option<i64>,
277+
pub historical_liquidity_penalty_multiplier_msat: Option<i64>,
278+
pub historical_liquidity_penalty_amount_multiplier_msat: Option<i64>,
279+
pub anti_probing_penalty_msat: Option<i64>,
280+
pub considered_impossible_penalty_msat: Option<i64>,
281+
pub linear_success_probability: Option<bool>,
282+
pub probing_diversity_penalty_msat: Option<i64>,
283+
pub liquidity_offset_half_life_secs: Option<i64>,
284+
pub historical_no_updates_half_life_secs: Option<i64>,
285+
pub manual_node_penalties: Option<HashMap<String, i64>>,
286+
}
287+
266288
#[napi(object)]
267289
pub struct MdkNodeOptions {
268290
pub network: String,
@@ -273,6 +295,7 @@ pub struct MdkNodeOptions {
273295
pub mnemonic: String,
274296
pub lsp_node_id: String,
275297
pub lsp_address: String,
298+
pub scoring_param_overrides: Option<ScoringParamOverrides>,
276299
}
277300

278301
#[napi(object)]
@@ -381,6 +404,63 @@ impl MdkNode {
381404
builder.set_custom_logger(logger);
382405
builder.set_liquidity_source_lsps4(lsp_node_id, lsp_address);
383406

407+
if let Some(scoring) = options.scoring_param_overrides {
408+
let mut fee_params = ProbabilisticScoringFeeParameters::default();
409+
if let Some(v) = scoring.base_penalty_msat {
410+
fee_params.base_penalty_msat = v as u64;
411+
}
412+
if let Some(v) = scoring.base_penalty_amount_multiplier_msat {
413+
fee_params.base_penalty_amount_multiplier_msat = v as u64;
414+
}
415+
if let Some(v) = scoring.liquidity_penalty_multiplier_msat {
416+
fee_params.liquidity_penalty_multiplier_msat = v as u64;
417+
}
418+
if let Some(v) = scoring.liquidity_penalty_amount_multiplier_msat {
419+
fee_params.liquidity_penalty_amount_multiplier_msat = v as u64;
420+
}
421+
if let Some(v) = scoring.historical_liquidity_penalty_multiplier_msat {
422+
fee_params.historical_liquidity_penalty_multiplier_msat = v as u64;
423+
}
424+
if let Some(v) = scoring.historical_liquidity_penalty_amount_multiplier_msat {
425+
fee_params.historical_liquidity_penalty_amount_multiplier_msat = v as u64;
426+
}
427+
if let Some(v) = scoring.anti_probing_penalty_msat {
428+
fee_params.anti_probing_penalty_msat = v as u64;
429+
}
430+
if let Some(v) = scoring.considered_impossible_penalty_msat {
431+
fee_params.considered_impossible_penalty_msat = v as u64;
432+
}
433+
if let Some(v) = scoring.linear_success_probability {
434+
fee_params.linear_success_probability = v;
435+
}
436+
if let Some(v) = scoring.probing_diversity_penalty_msat {
437+
fee_params.probing_diversity_penalty_msat = v as u64;
438+
}
439+
if let Some(penalties) = scoring.manual_node_penalties {
440+
for (node_id_str, penalty) in penalties {
441+
let node_id = NodeId::from_str(&node_id_str).map_err(|e| {
442+
napi::Error::from_reason(format!("Invalid node_id {}: {}", node_id_str, e))
443+
})?;
444+
fee_params
445+
.manual_node_penalties
446+
.insert(node_id, penalty as u64);
447+
}
448+
}
449+
450+
let mut decay_params = ProbabilisticScoringDecayParameters::default();
451+
if let Some(v) = scoring.liquidity_offset_half_life_secs {
452+
decay_params.liquidity_offset_half_life = Duration::from_secs(v as u64);
453+
}
454+
if let Some(v) = scoring.historical_no_updates_half_life_secs {
455+
decay_params.historical_no_updates_half_life = Duration::from_secs(v as u64);
456+
}
457+
458+
builder.set_scoring_params(ProbabilisticScoringParameters {
459+
fee_params,
460+
decay_params,
461+
});
462+
}
463+
384464
let vss_headers = HashMap::from([(
385465
"Authorization".to_string(),
386466
format!("Bearer {}", options.mdk_api_key),

0 commit comments

Comments
 (0)