diff --git a/Cargo.lock b/Cargo.lock index 581879bb..deae3d2e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -302,7 +302,7 @@ checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf" [[package]] name = "bitcoin-payment-instructions" version = "0.6.0" -source = "git+https://github.com/tnull/bitcoin-payment-instructions?rev=ff09ce9401afa448549a8f101172700bcd14d7bb#ff09ce9401afa448549a8f101172700bcd14d7bb" +source = "git+https://github.com/tnull/bitcoin-payment-instructions?rev=0e430be98c09540624a68a68022ee0551e86d1be#0e430be98c09540624a68a68022ee0551e86d1be" dependencies = [ "bitcoin", "dnssec-prover", @@ -1054,7 +1054,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.1", + "socket2 0.5.10", "tokio", "tower-service", "tracing", @@ -1266,7 +1266,7 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "ldk-node" version = "0.8.0+git" -source = "git+https://github.com/lightningdevkit/ldk-node?rev=f2e44fdd21490ff3fa90445d7a28b19cfd020c3d#f2e44fdd21490ff3fa90445d7a28b19cfd020c3d" +source = "git+https://github.com/lightningdevkit/ldk-node?rev=056447c28221be02c3d39f8c6ae430a67ebbd850#056447c28221be02c3d39f8c6ae430a67ebbd850" dependencies = [ "async-trait", "base64 0.22.1", @@ -1420,7 +1420,7 @@ dependencies = [ [[package]] name = "lightning" version = "0.3.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=3dfcc4cca1866c5e5d4d4eaf3b82e09584e2ce5c#3dfcc4cca1866c5e5d4d4eaf3b82e09584e2ce5c" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" dependencies = [ "bech32", "bitcoin", @@ -1437,7 +1437,7 @@ dependencies = [ [[package]] name = "lightning-background-processor" version = "0.3.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=3dfcc4cca1866c5e5d4d4eaf3b82e09584e2ce5c#3dfcc4cca1866c5e5d4d4eaf3b82e09584e2ce5c" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" dependencies = [ "bitcoin", "bitcoin-io", @@ -1451,7 +1451,7 @@ dependencies = [ [[package]] name = "lightning-block-sync" version = "0.3.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=3dfcc4cca1866c5e5d4d4eaf3b82e09584e2ce5c#3dfcc4cca1866c5e5d4d4eaf3b82e09584e2ce5c" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" dependencies = [ "bitcoin", "bitreq", @@ -1463,7 +1463,7 @@ dependencies = [ [[package]] name = "lightning-dns-resolver" version = "0.3.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=3dfcc4cca1866c5e5d4d4eaf3b82e09584e2ce5c#3dfcc4cca1866c5e5d4d4eaf3b82e09584e2ce5c" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" dependencies = [ "dnssec-prover", "lightning", @@ -1474,7 +1474,7 @@ dependencies = [ [[package]] name = "lightning-invoice" version = "0.35.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=3dfcc4cca1866c5e5d4d4eaf3b82e09584e2ce5c#3dfcc4cca1866c5e5d4d4eaf3b82e09584e2ce5c" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" dependencies = [ "bech32", "bitcoin", @@ -1485,7 +1485,7 @@ dependencies = [ [[package]] name = "lightning-liquidity" version = "0.3.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=3dfcc4cca1866c5e5d4d4eaf3b82e09584e2ce5c#3dfcc4cca1866c5e5d4d4eaf3b82e09584e2ce5c" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" dependencies = [ "bitcoin", "bitreq", @@ -1501,7 +1501,7 @@ dependencies = [ [[package]] name = "lightning-macros" version = "0.2.2+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=3dfcc4cca1866c5e5d4d4eaf3b82e09584e2ce5c#3dfcc4cca1866c5e5d4d4eaf3b82e09584e2ce5c" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" dependencies = [ "proc-macro2", "quote", @@ -1511,7 +1511,7 @@ dependencies = [ [[package]] name = "lightning-net-tokio" version = "0.3.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=3dfcc4cca1866c5e5d4d4eaf3b82e09584e2ce5c#3dfcc4cca1866c5e5d4d4eaf3b82e09584e2ce5c" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" dependencies = [ "bitcoin", "lightning", @@ -1521,7 +1521,7 @@ dependencies = [ [[package]] name = "lightning-persister" version = "0.3.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=3dfcc4cca1866c5e5d4d4eaf3b82e09584e2ce5c#3dfcc4cca1866c5e5d4d4eaf3b82e09584e2ce5c" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" dependencies = [ "bitcoin", "lightning", @@ -1532,7 +1532,7 @@ dependencies = [ [[package]] name = "lightning-rapid-gossip-sync" version = "0.3.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=3dfcc4cca1866c5e5d4d4eaf3b82e09584e2ce5c#3dfcc4cca1866c5e5d4d4eaf3b82e09584e2ce5c" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" dependencies = [ "bitcoin", "bitcoin-io", @@ -1543,7 +1543,7 @@ dependencies = [ [[package]] name = "lightning-transaction-sync" version = "0.3.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=3dfcc4cca1866c5e5d4d4eaf3b82e09584e2ce5c#3dfcc4cca1866c5e5d4d4eaf3b82e09584e2ce5c" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" dependencies = [ "bitcoin", "electrum-client", @@ -1556,7 +1556,7 @@ dependencies = [ [[package]] name = "lightning-types" version = "0.4.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=3dfcc4cca1866c5e5d4d4eaf3b82e09584e2ce5c#3dfcc4cca1866c5e5d4d4eaf3b82e09584e2ce5c" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" dependencies = [ "bitcoin", ] @@ -1715,7 +1715,7 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "possiblyrandom" version = "0.2.0" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=3dfcc4cca1866c5e5d4d4eaf3b82e09584e2ce5c#3dfcc4cca1866c5e5d4d4eaf3b82e09584e2ce5c" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" dependencies = [ "getrandom 0.2.16", ] @@ -1824,7 +1824,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls 0.23.34", - "socket2 0.6.1", + "socket2 0.5.10", "thiserror", "tokio", "tracing", @@ -1861,7 +1861,7 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.1", + "socket2 0.5.10", "tracing", "windows-sys 0.60.2", ] diff --git a/contrib/ldk-server-config.toml b/contrib/ldk-server-config.toml index 32f74c8a..c935cb92 100644 --- a/contrib/ldk-server-config.toml +++ b/contrib/ldk-server-config.toml @@ -9,6 +9,17 @@ alias = "ldk_server" # Lightning node alias #rgs_server_url = "https://rapidsync.lightningdevkit.org/snapshot/v2/" # Optional: RGS URL for rapid gossip sync #async_payments_role = "client" # Optional async payments role: "client" or "server" +# Background probing service (optional) +# CAUTION: Probes send real HTLCs and can lock outbound liquidity until they time out. +#[probing] +#strategy = "high_degree" # "high_degree" or "random_walk" +#top_node_count = 100 # Required for "high_degree" +#max_hops = 5 # Required for "random_walk"; values below 2 are clamped to 2 +#interval_secs = 10 # Time between probe attempts (default: 10 seconds) +#max_locked_msat = 100000000 # Maximum total in-flight probe liquidity (default: 100k sats) +#diversity_penalty_msat = 250 # Optional; useful with "high_degree" +#cooldown_secs = 3600 # Node re-probe cooldown for "high_degree" (default: 1 hour) + # Storage settings [storage.disk] dir_path = "/tmp/ldk-server/" # Path for LDK and BDK data persistence, optional, defaults to ~/Library/Application Support/ldk-server/ on macOS, ~/.ldk-server/ on Linux diff --git a/docs/configuration.md b/docs/configuration.md index 548ab7c2..25b32f64 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -59,6 +59,37 @@ this node to go offline. Set `async_payments_role = "server"` to hold async paym and onion messages for peers. The server role requires an announceable node configuration. Leave the field unset to disable async payments. +### `[probing]` + +Enables LDK Node's background probing service to train the payment scorer with current +channel-liquidity information. Probing is disabled when this section and the corresponding +CLI/environment options are absent. + +Two strategies are available: + +- **`"high_degree"`** - probes toward highly connected public nodes. Set + `top_node_count` to control how many top nodes are cycled through. +- **`"random_walk"`** - constructs random paths through the public graph. Set `max_hops` + to limit path length; values below `2` are clamped to `2`. + +```toml +[probing] +strategy = "high_degree" +top_node_count = 100 +interval_secs = 30 +max_locked_msat = 500000 +diversity_penalty_msat = 250 +cooldown_secs = 3600 +``` + +The optional `interval_secs`, `max_locked_msat`, `diversity_penalty_msat`, and +`cooldown_secs` fields override LDK Node's defaults. `diversity_penalty_msat` and +`cooldown_secs` are useful with `high_degree`; random walks bypass the scorer and do not +track destination cooldowns. + +> **Warning:** Probes send real HTLCs over real channels. A probe can remain in flight and +> lock outbound liquidity until its HTLC times out. Set `max_locked_msat` conservatively. + ### `[storage.disk]` Where persistent data is stored. Defaults to `~/.ldk-server/` on Linux and diff --git a/e2e-tests/Cargo.lock b/e2e-tests/Cargo.lock index 0b864dd0..ad624f1d 100644 --- a/e2e-tests/Cargo.lock +++ b/e2e-tests/Cargo.lock @@ -73,6 +73,29 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "aws-lc-rs" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4342d8937fc7e5dd9b1c60292261c0670c882a2cd1719cfc11b1af41731e32ad" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d9ceb1da931507a12f4fccea479dccd00da1943e1b4ae72d8e502d707361444" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", + "pkg-config", +] + [[package]] name = "base58ck" version = "0.1.0" @@ -126,9 +149,9 @@ dependencies = [ [[package]] name = "bdk_electrum" -version = "0.23.2" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b59a3f7fbe678874fa34354097644a171276e02a49934c13b3d61c54610ddf39" +checksum = "00a9846105bf6e751adbb6946b000ff919ce24a15a941cbfe68485549b552a9c" dependencies = [ "bdk_core", "electrum-client", @@ -148,9 +171,9 @@ dependencies = [ [[package]] name = "bdk_wallet" -version = "2.3.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03f1e31ccc562f600981f747d2262b84428cbff52c9c9cdf14d15fb15bd2286" +checksum = "1284fb23acc3e3022673712b55f4d5ce7e38aadc2c49bbef830dc3935f0a3289" dependencies = [ "bdk_chain", "bip39", @@ -227,7 +250,7 @@ checksum = "2dee39a0ee5b4095224a0cfc6bf4cc1baf0f9624b96b367e53b66d974e51d953" [[package]] name = "bitcoin-payment-instructions" version = "0.6.0" -source = "git+https://github.com/jkczyz/bitcoin-payment-instructions?rev=a7b32d5fded9bb45f73bf82e6d7187adf705171c#a7b32d5fded9bb45f73bf82e6d7187adf705171c" +source = "git+https://github.com/tnull/bitcoin-payment-instructions?rev=0e430be98c09540624a68a68022ee0551e86d1be#0e430be98c09540624a68a68022ee0551e86d1be" dependencies = [ "bitcoin", "dnssec-prover", @@ -332,6 +355,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7a4d3ec6524d28a329fc53654bbadc9bdd7b0431f5d65f1a56ffb28a1ee5283" dependencies = [ "find-msvc-tools", + "jobserver", + "libc", "shlex", ] @@ -353,6 +378,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b4b0fc281743d80256607bd65e8beedc42cb0787ea119c85b81b4c0eab85e5f" +[[package]] +name = "chacha20-poly1305" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bad28386ab70dd960294556a76aaab6da2994fec47d650fd725b3012f062052a" + [[package]] name = "chrono" version = "0.4.44" @@ -365,6 +396,15 @@ dependencies = [ "windows-link", ] +[[package]] +name = "cmake" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" +dependencies = [ + "cc", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -461,6 +501,12 @@ dependencies = [ "tokio", ] +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + [[package]] name = "e2e-tests" version = "0.1.0" @@ -483,9 +529,9 @@ checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "electrum-client" -version = "0.24.1" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5059f13888a90486e7268bbce59b175f5f76b1c55e5b9c568ceaa42d2b8507c" +checksum = "1970c5d7bd9de6d4041cbfc3e46faa3e85fe7efcea2b0eb06750d3ae1ef577b7" dependencies = [ "bitcoin", "byteorder", @@ -610,6 +656,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "futures" version = "0.3.32" @@ -992,7 +1044,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.3", + "socket2 0.5.10", "tokio", "tower-service", "tracing", @@ -1174,6 +1226,16 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +[[package]] +name = "jobserver" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" +dependencies = [ + "getrandom 0.4.2", + "libc", +] + [[package]] name = "js-sys" version = "0.3.94" @@ -1207,7 +1269,7 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "ldk-node" version = "0.8.0+git" -source = "git+https://github.com/lightningdevkit/ldk-node?rev=c754e2fe85c70741b5e370334cd16856c615265e#c754e2fe85c70741b5e370334cd16856c615265e" +source = "git+https://github.com/lightningdevkit/ldk-node?rev=056447c28221be02c3d39f8c6ae430a67ebbd850#056447c28221be02c3d39f8c6ae430a67ebbd850" dependencies = [ "async-trait", "base64 0.22.1", @@ -1228,6 +1290,7 @@ dependencies = [ "lightning", "lightning-background-processor", "lightning-block-sync", + "lightning-dns-resolver", "lightning-invoice", "lightning-liquidity", "lightning-macros", @@ -1319,10 +1382,11 @@ dependencies = [ [[package]] name = "lightning" version = "0.3.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=38a62c32454d3eac22578144c479dbf9a6d9bff6#38a62c32454d3eac22578144c479dbf9a6d9bff6" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" dependencies = [ "bech32", "bitcoin", + "chacha20-poly1305 0.2.0", "dnssec-prover", "hashbrown 0.13.2", "libm", @@ -1335,7 +1399,7 @@ dependencies = [ [[package]] name = "lightning-background-processor" version = "0.3.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=38a62c32454d3eac22578144c479dbf9a6d9bff6#38a62c32454d3eac22578144c479dbf9a6d9bff6" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" dependencies = [ "bitcoin", "bitcoin-io", @@ -1349,7 +1413,7 @@ dependencies = [ [[package]] name = "lightning-block-sync" version = "0.3.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=38a62c32454d3eac22578144c479dbf9a6d9bff6#38a62c32454d3eac22578144c479dbf9a6d9bff6" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" dependencies = [ "bitcoin", "bitreq", @@ -1358,10 +1422,21 @@ dependencies = [ "tokio", ] +[[package]] +name = "lightning-dns-resolver" +version = "0.3.0+git" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" +dependencies = [ + "dnssec-prover", + "lightning", + "lightning-types", + "tokio", +] + [[package]] name = "lightning-invoice" version = "0.35.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=38a62c32454d3eac22578144c479dbf9a6d9bff6#38a62c32454d3eac22578144c479dbf9a6d9bff6" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" dependencies = [ "bech32", "bitcoin", @@ -1372,9 +1447,10 @@ dependencies = [ [[package]] name = "lightning-liquidity" version = "0.3.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=38a62c32454d3eac22578144c479dbf9a6d9bff6#38a62c32454d3eac22578144c479dbf9a6d9bff6" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" dependencies = [ "bitcoin", + "bitreq", "chrono", "lightning", "lightning-invoice", @@ -1387,7 +1463,7 @@ dependencies = [ [[package]] name = "lightning-macros" version = "0.2.2+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=38a62c32454d3eac22578144c479dbf9a6d9bff6#38a62c32454d3eac22578144c479dbf9a6d9bff6" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" dependencies = [ "proc-macro2", "quote", @@ -1397,7 +1473,7 @@ dependencies = [ [[package]] name = "lightning-net-tokio" version = "0.3.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=38a62c32454d3eac22578144c479dbf9a6d9bff6#38a62c32454d3eac22578144c479dbf9a6d9bff6" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" dependencies = [ "bitcoin", "lightning", @@ -1407,7 +1483,7 @@ dependencies = [ [[package]] name = "lightning-persister" version = "0.3.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=38a62c32454d3eac22578144c479dbf9a6d9bff6#38a62c32454d3eac22578144c479dbf9a6d9bff6" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" dependencies = [ "bitcoin", "lightning", @@ -1418,7 +1494,7 @@ dependencies = [ [[package]] name = "lightning-rapid-gossip-sync" version = "0.3.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=38a62c32454d3eac22578144c479dbf9a6d9bff6#38a62c32454d3eac22578144c479dbf9a6d9bff6" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" dependencies = [ "bitcoin", "bitcoin-io", @@ -1429,7 +1505,7 @@ dependencies = [ [[package]] name = "lightning-transaction-sync" version = "0.3.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=38a62c32454d3eac22578144c479dbf9a6d9bff6#38a62c32454d3eac22578144c479dbf9a6d9bff6" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" dependencies = [ "bitcoin", "electrum-client", @@ -1442,7 +1518,7 @@ dependencies = [ [[package]] name = "lightning-types" version = "0.4.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=38a62c32454d3eac22578144c479dbf9a6d9bff6#38a62c32454d3eac22578144c479dbf9a6d9bff6" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" dependencies = [ "bitcoin", ] @@ -1598,7 +1674,7 @@ checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" [[package]] name = "possiblyrandom" version = "0.2.0" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=38a62c32454d3eac22578144c479dbf9a6d9bff6#38a62c32454d3eac22578144c479dbf9a6d9bff6" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" dependencies = [ "getrandom 0.2.17", ] @@ -1717,7 +1793,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls 0.23.37", - "socket2 0.6.3", + "socket2 0.5.10", "thiserror", "tokio", "tracing", @@ -1754,7 +1830,7 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.3", + "socket2 0.5.10", "tracing", "windows-sys 0.60.2", ] @@ -2034,6 +2110,7 @@ version = "0.23.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" dependencies = [ + "aws-lc-rs", "log", "once_cell", "ring", @@ -2078,6 +2155,7 @@ version = "0.103.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef" dependencies = [ + "aws-lc-rs", "ring", "rustls-pki-types", "untrusted", @@ -2569,16 +2647,16 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "vss-client-ng" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6334cb4940aba86a2e2aa9dde7c722a2510f55815422088a2a2ac24f46579e6a" +checksum = "49dae7224f498977c17c2ac85d5377d6a2b7290102add15070db257b86a519d6" dependencies = [ "async-trait", "base64 0.22.1", "bitcoin", "bitcoin_hashes", "bitreq", - "chacha20-poly1305", + "chacha20-poly1305 0.1.2", "log", "prost", "prost-build", diff --git a/e2e-tests/Cargo.toml b/e2e-tests/Cargo.toml index 786d075e..461ba244 100644 --- a/e2e-tests/Cargo.toml +++ b/e2e-tests/Cargo.toml @@ -21,4 +21,4 @@ ldk-server-client = { path = "../ldk-server-client" } ldk-server-grpc = { path = "../ldk-server-grpc", features = ["serde"] } serde_json = "1.0" hex-conservative = { version = "0.2", features = ["std"] } -ldk-node = { git = "https://github.com/lightningdevkit/ldk-node", rev = "c754e2fe85c70741b5e370334cd16856c615265e" } +ldk-node = { git = "https://github.com/lightningdevkit/ldk-node", rev = "056447c28221be02c3d39f8c6ae430a67ebbd850" } diff --git a/e2e-tests/tests/e2e.rs b/e2e-tests/tests/e2e.rs index 423838f1..6f801e91 100644 --- a/e2e-tests/tests/e2e.rs +++ b/e2e-tests/tests/e2e.rs @@ -1200,12 +1200,12 @@ async fn test_forwarded_payment_event() { let mut builder_c = ldk_node::Builder::from_config(config_c); let (rpc_host, rpc_port, rpc_user, rpc_password) = bitcoind.rpc_details(); - builder_c.set_chain_source_bitcoind_rpc(rpc_host, rpc_port, rpc_user, rpc_password); + builder_c.set_chain_source_bitcoind_rpc(rpc_host, rpc_port, rpc_user, rpc_password, None); // Set B as LSPS2 LSP for C let b_node_id = ldk_node::bitcoin::secp256k1::PublicKey::from_str(server_b.node_id()).unwrap(); let b_addr = SocketAddress::from_str(&format!("127.0.0.1:{}", server_b.p2p_port)).unwrap(); - builder_c.set_liquidity_source_lsps2(b_node_id, b_addr, None); + builder_c.add_liquidity_source(b_node_id, b_addr, None, true); let mnemonic_c = ldk_node::entropy::generate_entropy_mnemonic(None); let node_entropy_c = ldk_node::entropy::NodeEntropy::from_bip39_mnemonic(mnemonic_c, None); diff --git a/ldk-server/Cargo.toml b/ldk-server/Cargo.toml index d27e4943..2a9783d0 100644 --- a/ldk-server/Cargo.toml +++ b/ldk-server/Cargo.toml @@ -13,7 +13,7 @@ keywords = ["bitcoin", "lightning", "ldk", "server"] categories = ["cryptography::cryptocurrencies"] [dependencies] -ldk-node = { git = "https://github.com/lightningdevkit/ldk-node", rev = "f2e44fdd21490ff3fa90445d7a28b19cfd020c3d" } +ldk-node = { git = "https://github.com/lightningdevkit/ldk-node", rev = "056447c28221be02c3d39f8c6ae430a67ebbd850" } serde = { version = "1.0.203", default-features = false, features = ["derive"] } hyper = { version = "1", default-features = false, features = ["server", "http2"] } http-body-util = { version = "0.1", default-features = false } diff --git a/ldk-server/src/api/error.rs b/ldk-server/src/api/error.rs index ad1c152f..eef727f2 100644 --- a/ldk-server/src/api/error.rs +++ b/ldk-server/src/api/error.rs @@ -129,6 +129,7 @@ impl From for LdkServerError { | NodeError::LnurlAuthFailed | NodeError::LnurlAuthTimeout | NodeError::InvalidLnurl + | NodeError::ChainSourceNotSupported | NodeError::TxSyncTimeout => (error.to_string(), LdkServerErrorCode::InternalServerError), }; LdkServerError::new(error_code, message) diff --git a/ldk-server/src/main.rs b/ldk-server/src/main.rs index 123c35fb..cc8e0471 100644 --- a/ldk-server/src/main.rs +++ b/ldk-server/src/main.rs @@ -207,6 +207,10 @@ fn main() { builder.set_gossip_source_rgs(rgs_server_url); } + if let Some(probing_config) = config_file.probing_config { + builder.set_probing_config(probing_config); + } + if let Err(e) = builder.set_async_payments_role(config_file.async_payments_role) { error!("Failed to configure async payments role: {e}"); std::process::exit(-1); @@ -443,8 +447,8 @@ fn main() { reason, } => { info!( - "CHANNEL_CLOSED: {} from counterparty {:?}", - channel_id, counterparty_node_id.map(|p| p.to_string()), + "CHANNEL_CLOSED: {} from counterparty {}", + channel_id, counterparty_node_id, ); let channel_id_hex = channel_id.0.to_lower_hex_string(); @@ -456,8 +460,7 @@ fn main() { event_envelope::Event::ChannelStateChanged(events::ChannelStateChanged { channel_id: channel_id_hex, user_channel_id: user_channel_id.0.to_string(), - counterparty_node_id: counterparty_node_id - .map(|node_id| node_id.to_string()), + counterparty_node_id: Some(counterparty_node_id.to_string()), state: if is_open_failure { events::ChannelState::OpenFailed.into() } else { diff --git a/ldk-server/src/util/config.rs b/ldk-server/src/util/config.rs index fc2ee45b..6b148771 100644 --- a/ldk-server/src/util/config.rs +++ b/ldk-server/src/util/config.rs @@ -10,6 +10,7 @@ use std::net::SocketAddr; use std::path::PathBuf; use std::str::FromStr; +use std::time::Duration; use std::{fs, io}; use clap::Parser; @@ -19,6 +20,7 @@ use ldk_node::config::{AsyncPaymentsRole, HRNResolverConfig, HumanReadableNamesC use ldk_node::lightning::ln::msgs::SocketAddress; use ldk_node::lightning::routing::gossip::NodeAlias; use ldk_node::liquidity::LSPS2ServiceConfig; +use ldk_node::probing::{ProbingConfig, ProbingConfigBuilder}; use log::LevelFilter; use serde::{Deserialize, Serialize}; @@ -64,6 +66,7 @@ pub struct Config { pub log_max_files: usize, pub log_to_file: bool, pub pathfinding_scores_source_url: Option, + pub probing_config: Option, pub async_payments_role: Option, pub metrics_enabled: bool, pub poll_metrics_interval: Option, @@ -137,6 +140,7 @@ struct ConfigBuilder { log_max_files: Option, log_to_file: Option, pathfinding_scores_source_url: Option, + probing: Option, async_payments_role: Option, metrics_enabled: Option, poll_metrics_interval: Option, @@ -214,6 +218,10 @@ impl ConfigBuilder { self.metrics_password = metrics.password.or(self.metrics_password.clone()); } + if let Some(probing) = toml.probing { + self.probing = Some(probing); + } + if let Some(tor) = toml.tor { self.tor_proxy_address = Some(tor.proxy_address) } @@ -276,6 +284,36 @@ impl ConfigBuilder { self.async_payments_role = Some(async_payments_role.clone()); } + if args.has_probing_options() { + let probing = self.probing.get_or_insert_with(ProbingTomlConfig::default); + if let Some(probing_strategy) = &args.probing_strategy { + probing.strategy = Some(probing_strategy.clone()); + match probing_strategy.trim().to_ascii_lowercase().as_str() { + "high_degree" | "high-degree" => probing.max_hops = None, + "random_walk" | "random-walk" => probing.top_node_count = None, + _ => {}, + } + } + if let Some(top_node_count) = args.probing_top_node_count { + probing.top_node_count = Some(top_node_count); + } + if let Some(max_hops) = args.probing_max_hops { + probing.max_hops = Some(max_hops); + } + if let Some(interval_secs) = args.probing_interval_secs { + probing.interval_secs = Some(interval_secs); + } + if let Some(max_locked_msat) = args.probing_max_locked_msat { + probing.max_locked_msat = Some(max_locked_msat); + } + if let Some(diversity_penalty_msat) = args.probing_diversity_penalty_msat { + probing.diversity_penalty_msat = Some(diversity_penalty_msat); + } + if let Some(cooldown_secs) = args.probing_cooldown_secs { + probing.cooldown_secs = Some(cooldown_secs); + } + } + if args.metrics_enabled { self.metrics_enabled = Some(true); } @@ -484,6 +522,8 @@ impl ConfigBuilder { let pathfinding_scores_source_url = self.pathfinding_scores_source_url; + let probing_config = build_probing_config(self.probing)?; + let async_payments_role = self.async_payments_role.as_deref().map(parse_async_payments_role).transpose()?; @@ -544,6 +584,7 @@ impl ConfigBuilder { log_max_files, log_to_file, pathfinding_scores_source_url, + probing_config, async_payments_role, metrics_enabled, poll_metrics_interval, @@ -568,6 +609,7 @@ pub struct TomlConfig { log: Option, tls: Option, metrics: Option, + probing: Option, tor: Option, hrn: Option, } @@ -645,6 +687,18 @@ struct MetricsTomlConfig { password: Option, } +#[derive(Default, Deserialize, Serialize)] +#[serde(deny_unknown_fields)] +struct ProbingTomlConfig { + strategy: Option, + top_node_count: Option, + max_hops: Option, + interval_secs: Option, + max_locked_msat: Option, + diversity_penalty_msat: Option, + cooldown_secs: Option, +} + #[derive(Deserialize, Serialize)] #[serde(deny_unknown_fields)] struct TomlTorConfig { @@ -757,6 +811,76 @@ fn parse_async_payments_role(role: &str) -> io::Result { } } +fn build_probing_config(config: Option) -> io::Result> { + let Some(config) = config else { + return Ok(None); + }; + let ProbingTomlConfig { + strategy, + top_node_count, + max_hops, + interval_secs, + max_locked_msat, + diversity_penalty_msat, + cooldown_secs, + } = config; + + let strategy = strategy.ok_or_else(|| missing_field_err("probing.strategy"))?; + let mut builder = match strategy.trim().to_ascii_lowercase().as_str() { + "high_degree" | "high-degree" => { + if max_hops.is_some() { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "`probing.max_hops` only applies to the `random_walk` strategy", + )); + } + let top_node_count = + top_node_count.ok_or_else(|| missing_field_err("probing.top_node_count"))?; + if top_node_count == 0 { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "`probing.top_node_count` must be greater than 0", + )); + } + ProbingConfigBuilder::high_degree(top_node_count) + }, + "random_walk" | "random-walk" => { + if top_node_count.is_some() { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "`probing.top_node_count` only applies to the `high_degree` strategy", + )); + } + let max_hops = max_hops.ok_or_else(|| missing_field_err("probing.max_hops"))?; + ProbingConfigBuilder::random_walk(max_hops) + }, + other => { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + format!( + "Invalid probing strategy '{}' configured; expected 'high_degree' or 'random_walk'", + other + ), + )) + }, + }; + + if let Some(interval_secs) = interval_secs { + builder.interval(Duration::from_secs(interval_secs)); + } + if let Some(max_locked_msat) = max_locked_msat { + builder.max_locked_msat(max_locked_msat); + } + if let Some(diversity_penalty_msat) = diversity_penalty_msat { + builder.diversity_penalty_msat(diversity_penalty_msat); + } + if let Some(cooldown_secs) = cooldown_secs { + builder.cooldown(Duration::from_secs(cooldown_secs)); + } + + Ok(Some(builder.build())) +} + #[derive(Deserialize, Serialize)] #[serde(deny_unknown_fields)] struct LiquidityConfig { @@ -971,6 +1095,55 @@ pub struct ArgsConfig { )] node_async_payments_role: Option, + #[arg( + long, + env = "LDK_SERVER_PROBING_STRATEGY", + help = "Enable background probing with `high_degree` or `random_walk`." + )] + probing_strategy: Option, + + #[arg( + long, + env = "LDK_SERVER_PROBING_TOP_NODE_COUNT", + help = "Number of highly connected nodes to cycle through with the `high_degree` probing strategy." + )] + probing_top_node_count: Option, + + #[arg( + long, + env = "LDK_SERVER_PROBING_MAX_HOPS", + help = "Maximum path length for the `random_walk` probing strategy." + )] + probing_max_hops: Option, + + #[arg( + long, + env = "LDK_SERVER_PROBING_INTERVAL_SECS", + help = "Interval between background probe attempts in seconds." + )] + probing_interval_secs: Option, + + #[arg( + long, + env = "LDK_SERVER_PROBING_MAX_LOCKED_MSAT", + help = "Maximum total millisatoshis that background probes may lock in flight." + )] + probing_max_locked_msat: Option, + + #[arg( + long, + env = "LDK_SERVER_PROBING_DIVERSITY_PENALTY_MSAT", + help = "Scoring penalty for recently probed channels. Useful with `high_degree`." + )] + probing_diversity_penalty_msat: Option, + + #[arg( + long, + env = "LDK_SERVER_PROBING_COOLDOWN_SECS", + help = "Time before a node can be probed again. Applies to `high_degree`." + )] + probing_cooldown_secs: Option, + #[arg( long, env = "LDK_SERVER_METRICS_ENABLED", @@ -1008,6 +1181,18 @@ pub struct ArgsConfig { tor_proxy_address: Option, } +impl ArgsConfig { + fn has_probing_options(&self) -> bool { + self.probing_strategy.is_some() + || self.probing_top_node_count.is_some() + || self.probing_max_hops.is_some() + || self.probing_interval_secs.is_some() + || self.probing_max_locked_msat.is_some() + || self.probing_diversity_penalty_msat.is_some() + || self.probing_cooldown_secs.is_some() + } +} + pub fn load_config(args: &ArgsConfig) -> io::Result { let mut builder = ConfigBuilder::default(); @@ -1148,6 +1333,13 @@ mod tests { node_alias: Some(String::from("LDK Server CLI")), pathfinding_scores_source_url: Some(String::from("https://example.com/")), node_async_payments_role: Some(String::from("server")), + probing_strategy: None, + probing_top_node_count: None, + probing_max_hops: None, + probing_interval_secs: None, + probing_max_locked_msat: None, + probing_diversity_penalty_msat: None, + probing_cooldown_secs: None, metrics_enabled: false, poll_metrics_interval: None, metrics_username: None, @@ -1176,6 +1368,13 @@ mod tests { storage_dir_path: None, pathfinding_scores_source_url: None, node_async_payments_role: None, + probing_strategy: None, + probing_top_node_count: None, + probing_max_hops: None, + probing_interval_secs: None, + probing_max_locked_msat: None, + probing_diversity_penalty_msat: None, + probing_cooldown_secs: None, metrics_enabled: false, poll_metrics_interval: None, metrics_username: None, @@ -1281,6 +1480,7 @@ mod tests { log_max_files: 5, log_to_file: true, pathfinding_scores_source_url: None, + probing_config: None, async_payments_role: Some(AsyncPaymentsRole::Client), metrics_enabled: false, poll_metrics_interval: None, @@ -1682,6 +1882,7 @@ mod tests { log_level: LevelFilter::Trace, log_file_path: Some("/var/log/ldk-server.log".to_string()), pathfinding_scores_source_url: Some("https://example.com/".to_string()), + probing_config: None, async_payments_role: Some(AsyncPaymentsRole::Server), metrics_enabled: false, poll_metrics_interval: None, @@ -1801,6 +2002,7 @@ mod tests { log_level: LevelFilter::Trace, log_file_path: Some("/var/log/ldk-server.log".to_string()), pathfinding_scores_source_url: Some("https://example.com/".to_string()), + probing_config: None, async_payments_role: Some(AsyncPaymentsRole::Server), metrics_enabled: false, poll_metrics_interval: None, @@ -2087,6 +2289,162 @@ mod tests { assert!(parse_dns_server_address("invalid@address").is_err()); } + #[test] + fn test_probing_config_from_file() { + let storage_path = std::env::temp_dir(); + let config_file_name = "test_probing_config.toml"; + let mut args_config = empty_args_config(); + args_config.config_file = + Some(storage_path.join(config_file_name).to_string_lossy().to_string()); + + let high_degree_config = format!( + r#" + [node] + network = "regtest" + + [bitcoind] + rpc_address = "127.0.0.1:8332" + rpc_user = "user" + rpc_password = "password" + + [probing] + strategy = "high_degree" + top_node_count = 100 + interval_secs = 30 + max_locked_msat = 500000 + diversity_penalty_msat = 250 + cooldown_secs = 1800 + {}"#, + lsps2_service_config_for_feature() + ); + fs::write(storage_path.join(config_file_name), high_degree_config).unwrap(); + assert!(load_config(&args_config).unwrap().probing_config.is_some()); + + let random_walk_config = format!( + r#" + [node] + network = "regtest" + + [bitcoind] + rpc_address = "127.0.0.1:8332" + rpc_user = "user" + rpc_password = "password" + + [probing] + strategy = "random_walk" + max_hops = 5 + {}"#, + lsps2_service_config_for_feature() + ); + fs::write(storage_path.join(config_file_name), random_walk_config).unwrap(); + assert!(load_config(&args_config).unwrap().probing_config.is_some()); + } + + #[test] + fn test_probing_config_validation() { + let high_degree = ProbingTomlConfig { + strategy: Some("high_degree".to_string()), + top_node_count: Some(100), + interval_secs: Some(30), + max_locked_msat: Some(500_000), + diversity_penalty_msat: Some(250), + cooldown_secs: Some(1800), + ..ProbingTomlConfig::default() + }; + assert!(build_probing_config(Some(high_degree)).unwrap().is_some()); + + let random_walk = ProbingTomlConfig { + strategy: Some("random_walk".to_string()), + max_hops: Some(5), + ..ProbingTomlConfig::default() + }; + assert!(build_probing_config(Some(random_walk)).unwrap().is_some()); + + let missing_strategy = build_probing_config(Some(ProbingTomlConfig { + top_node_count: Some(100), + ..ProbingTomlConfig::default() + })) + .unwrap_err(); + assert!(missing_strategy.to_string().contains("probing.strategy")); + + let missing_strategy_option = build_probing_config(Some(ProbingTomlConfig { + strategy: Some("high_degree".to_string()), + ..ProbingTomlConfig::default() + })) + .unwrap_err(); + assert!(missing_strategy_option.to_string().contains("probing.top_node_count")); + + let mismatched_option = build_probing_config(Some(ProbingTomlConfig { + strategy: Some("random_walk".to_string()), + top_node_count: Some(100), + max_hops: Some(5), + ..ProbingTomlConfig::default() + })) + .unwrap_err(); + assert!(mismatched_option.to_string().contains("probing.top_node_count")); + + let invalid_strategy = build_probing_config(Some(ProbingTomlConfig { + strategy: Some("invalid".to_string()), + ..ProbingTomlConfig::default() + })) + .unwrap_err(); + assert!(invalid_strategy.to_string().contains("Invalid probing strategy")); + } + + #[test] + fn test_accepts_probing_args() { + let args_config = ArgsConfig::try_parse_from([ + "ldk-server", + "--probing-strategy", + "high_degree", + "--probing-top-node-count", + "100", + "--probing-interval-secs", + "30", + "--probing-max-locked-msat", + "500000", + "--probing-diversity-penalty-msat", + "250", + "--probing-cooldown-secs", + "1800", + ]) + .unwrap(); + + assert_eq!(args_config.probing_strategy.as_deref(), Some("high_degree")); + assert_eq!(args_config.probing_top_node_count, Some(100)); + assert_eq!(args_config.probing_interval_secs, Some(30)); + assert_eq!(args_config.probing_max_locked_msat, Some(500_000)); + assert_eq!(args_config.probing_diversity_penalty_msat, Some(250)); + assert_eq!(args_config.probing_cooldown_secs, Some(1800)); + } + + #[test] + fn test_probing_args_override_strategy_specific_file_options() { + let mut builder = ConfigBuilder { + probing: Some(ProbingTomlConfig { + strategy: Some("high_degree".to_string()), + top_node_count: Some(100), + ..ProbingTomlConfig::default() + }), + ..ConfigBuilder::default() + }; + let args_config = ArgsConfig::try_parse_from([ + "ldk-server", + "--probing-strategy", + "random_walk", + "--probing-max-hops", + "5", + ]) + .unwrap(); + + builder.merge_args(&args_config); + + let probing = builder.probing.unwrap(); + assert_eq!(probing.strategy.as_deref(), Some("random_walk")); + assert_eq!(probing.top_node_count, None); + assert_eq!(probing.max_hops, Some(5)); + } + #[test] fn test_rejects_node_entropy_config() { let storage_path = std::env::temp_dir();