Conversation
Signed-off-by: dzdidi <dzdidi@protonmail.com>
There was a problem hiding this comment.
Pull request overview
Adds DNS-based peer discovery (BOLT-0010) so the node can bootstrap outbound connections automatically from well-known DNS seeds instead of relying solely on manual peer configuration.
Changes:
- Introduces a new
dns_bootstrapmodule to query SRV records, decode bech32 pubkeys from hostnames, and resolve A/AAAA records into connectable peers. - Wires optional
dns_bootstrapconfiguration throughconfig.json→NodeConfig→LdkUserInfoand starts a periodic bootstrap task instart_ldk. - Adds
hickory-resolverdependency to perform async DNS lookups.
Reviewed changes
Copilot reviewed 9 out of 10 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| src/main.rs | Starts periodic DNS bootstrap task to discover/connect peers. |
| src/dns_bootstrap/bootstrapper.rs | Core bootstrap logic: SRV query → pubkey decode → host resolve → peer list. |
| src/dns_bootstrap/query.rs | DNS resolver wrapper for SRV + IP lookups with timeouts. |
| src/dns_bootstrap/pubkey.rs | Decodes node pubkeys from bech32 DNS labels; includes unit tests. |
| src/dns_bootstrap/config.rs | Defines DnsBootstrapConfig + default seed list. |
| src/dns_bootstrap/mod.rs | Module exports and error type definitions. |
| src/config.rs | Adds dns_bootstrap to NodeConfig and config help output. |
| src/cli.rs | Extends LdkUserInfo to carry dns_bootstrap config. |
| Cargo.toml | Adds hickory-resolver dependency. |
| Cargo.lock | Lockfile updates for new DNS dependency graph. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| println!( | ||
| "DNS bootstrap enabled: {} seeds, target {} peers, interval {}s", | ||
| bootstrapper.config().seeds.len(), | ||
| num_peers, | ||
| interval_secs, | ||
| ); | ||
|
|
||
| tokio::spawn(async move { | ||
| // Initial delay to let networking and gossip start. | ||
| tokio::time::sleep(Duration::from_secs(5)).await; | ||
|
|
||
| let mut interval = | ||
| tokio::time::interval(Duration::from_secs(interval_secs)); | ||
| loop { | ||
| interval.tick().await; | ||
|
|
||
| if stop_bootstrap.load(Ordering::Acquire) { | ||
| return; | ||
| } | ||
|
|
||
| // Build ignore set from currently connected peers. | ||
| let ignore: std::collections::HashSet< | ||
| lightning::routing::gossip::NodeId, | ||
| > = bootstrap_pm | ||
| .list_peers() | ||
| .iter() | ||
| .map(|p| { | ||
| lightning::routing::gossip::NodeId::from_pubkey( | ||
| &p.counterparty_node_id, | ||
| ) | ||
| }) | ||
| .collect(); | ||
|
|
||
| match bootstrapper.sample_node_addrs(num_peers, &ignore).await { | ||
| Ok(peers) => { | ||
| println!( | ||
| "[dns_bootstrap] Discovered {} peers", | ||
| peers.len() | ||
| ); | ||
| for peer in peers { | ||
| let _ = cli::do_connect_peer( | ||
| peer.pubkey, | ||
| peer.addr, | ||
| Arc::clone(&bootstrap_pm), | ||
| ) | ||
| .await; | ||
| } | ||
| }, | ||
| Err(e) => { | ||
| eprintln!("[dns_bootstrap] Bootstrap failed: {}", e); | ||
| }, | ||
| } | ||
| } | ||
| }); |
There was a problem hiding this comment.
interval_secs comes from config and can be set to 0, but tokio::time::interval(Duration::from_secs(0)) panics. Add validation (e.g., reject 0 with a clear error) before spawning the task, or clamp to a minimum of 1s so misconfiguration can’t crash the bootstrap task.
| println!( | |
| "DNS bootstrap enabled: {} seeds, target {} peers, interval {}s", | |
| bootstrapper.config().seeds.len(), | |
| num_peers, | |
| interval_secs, | |
| ); | |
| tokio::spawn(async move { | |
| // Initial delay to let networking and gossip start. | |
| tokio::time::sleep(Duration::from_secs(5)).await; | |
| let mut interval = | |
| tokio::time::interval(Duration::from_secs(interval_secs)); | |
| loop { | |
| interval.tick().await; | |
| if stop_bootstrap.load(Ordering::Acquire) { | |
| return; | |
| } | |
| // Build ignore set from currently connected peers. | |
| let ignore: std::collections::HashSet< | |
| lightning::routing::gossip::NodeId, | |
| > = bootstrap_pm | |
| .list_peers() | |
| .iter() | |
| .map(|p| { | |
| lightning::routing::gossip::NodeId::from_pubkey( | |
| &p.counterparty_node_id, | |
| ) | |
| }) | |
| .collect(); | |
| match bootstrapper.sample_node_addrs(num_peers, &ignore).await { | |
| Ok(peers) => { | |
| println!( | |
| "[dns_bootstrap] Discovered {} peers", | |
| peers.len() | |
| ); | |
| for peer in peers { | |
| let _ = cli::do_connect_peer( | |
| peer.pubkey, | |
| peer.addr, | |
| Arc::clone(&bootstrap_pm), | |
| ) | |
| .await; | |
| } | |
| }, | |
| Err(e) => { | |
| eprintln!("[dns_bootstrap] Bootstrap failed: {}", e); | |
| }, | |
| } | |
| } | |
| }); | |
| if interval_secs == 0 { | |
| eprintln!( | |
| "[dns_bootstrap] Invalid configuration: interval_secs must be greater than 0; DNS bootstrap disabled." | |
| ); | |
| } else { | |
| println!( | |
| "DNS bootstrap enabled: {} seeds, target {} peers, interval {}s", | |
| bootstrapper.config().seeds.len(), | |
| num_peers, | |
| interval_secs, | |
| ); | |
| tokio::spawn(async move { | |
| // Initial delay to let networking and gossip start. | |
| tokio::time::sleep(Duration::from_secs(5)).await; | |
| let mut interval = | |
| tokio::time::interval(Duration::from_secs(interval_secs)); | |
| loop { | |
| interval.tick().await; | |
| if stop_bootstrap.load(Ordering::Acquire) { | |
| return; | |
| } | |
| // Build ignore set from currently connected peers. | |
| let ignore: std::collections::HashSet< | |
| lightning::routing::gossip::NodeId, | |
| > = bootstrap_pm | |
| .list_peers() | |
| .iter() | |
| .map(|p| { | |
| lightning::routing::gossip::NodeId::from_pubkey( | |
| &p.counterparty_node_id, | |
| ) | |
| }) | |
| .collect(); | |
| match bootstrapper.sample_node_addrs(num_peers, &ignore).await { | |
| Ok(peers) => { | |
| println!( | |
| "[dns_bootstrap] Discovered {} peers", | |
| peers.len() | |
| ); | |
| for peer in peers { | |
| let _ = cli::do_connect_peer( | |
| peer.pubkey, | |
| peer.addr, | |
| Arc::clone(&bootstrap_pm), | |
| ) | |
| .await; | |
| } | |
| }, | |
| Err(e) => { | |
| eprintln!("[dns_bootstrap] Bootstrap failed: {}", e); | |
| }, | |
| } | |
| } | |
| }); | |
| } |
Signed-off-by: dzdidi <dzdidi@protonmail.com>
5de4407 to
5b96479
Compare
Signed-off-by: dzdidi <dzdidi@protonmail.com>
Signed-off-by: dzdidi <dzdidi@protonmail.com>
5b96479 to
5b135c3
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 15 out of 16 changed files in this pull request and generated 16 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Signed-off-by: dzdidi <dzdidi@protonmail.com>
There was a problem hiding this comment.
PR looks good 🙌🏻
Reiterating the niceness we could've achieved with a PR which formats according to your rules, so then all additional PRs cover actual changes and make reviewing much easier due to not having to "vouch" for a lot of changes that are hard to understand and keep all in head at once time, for the final PR approval 🙃
Ultimately, GitHub's "hide whitespaces" did help a lot with reviewing and keeping track of what's necessary to read as they're legit changes; but there's still some changes seen, like for example when formatting of arguments was changed to list on same line instead of a line for each arg.
IDK the rust ecosystem so good, is Clippy the standard for these formatting enforcement concerns?!
| rpc_client | ||
| .call_method("signrawtransactionwithwallet", &[tx_hex_json]) | ||
| .await | ||
| .unwrap() | ||
| rpc_client.call_method("signrawtransactionwithwallet", &[tx_hex_json]).await.unwrap() |
There was a problem hiding this comment.
nit: would be nice pick a formatting tool and setup a rule so AI always runs the formatter before commits, so that formatting changes are "right from the start" and don't change arbitrarily with each PR, unless actual code changes warrant new formatting 👍🏻
|
@ovitrif , I usually do both |
DNS bootstrap instead of manual
Blocked by / Based on #6
Adds DNS-based peer discovery (BOLT-10) so the node can bootstrap outbound connections automatically from well-known DNS seeds instead of relying solely on manual peer configuration.
Changes: