Skip to content
Open
8 changes: 8 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,14 @@ jobs:
if: "matrix.platform != 'windows-latest' && matrix.build-uniffi"
run: |
RUSTFLAGS="--cfg no_download --cfg cycle_tests" cargo test --features uniffi
- name: Test with HRN overrides (No UniFFI) on Rust ${{ matrix.toolchain }}
if: "matrix.platform == 'ubuntu-latest' && matrix.toolchain == 'stable'"
run: |
RUSTFLAGS="--cfg no_download" cargo test --features hrn_tests
- name: Test with UniFFI and HRN overrides on Rust ${{ matrix.toolchain }}
if: "matrix.platform != 'windows-latest' && matrix.build-uniffi"
run: |
RUSTFLAGS="--cfg no_download" cargo test --features uniffi,hrn_tests

doc:
name: Documentation
Expand Down
7 changes: 7 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ panic = 'abort' # Abort on panic

[features]
default = []
hrn_tests = []

[dependencies]
#lightning = { version = "0.2.0", features = ["std"] }
Expand All @@ -38,6 +39,7 @@ default = []
#lightning-transaction-sync = { version = "0.2.0", features = ["esplora-async-https", "time", "electrum-rustls-ring"] }
#lightning-liquidity = { version = "0.2.0", features = ["std"] }
#lightning-macros = { version = "0.2.0" }
#lightning-dns-resolver = { version = "0.3.0" }

lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "35ab03fbe0fe0927a9242754a0797553f6f7f099", features = ["std"] }
lightning-types = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "35ab03fbe0fe0927a9242754a0797553f6f7f099" }
Expand All @@ -50,6 +52,7 @@ lightning-block-sync = { git = "https://github.com/lightningdevkit/rust-lightnin
lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "35ab03fbe0fe0927a9242754a0797553f6f7f099", features = ["esplora-async-https", "time", "electrum-rustls-ring"] }
lightning-liquidity = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "35ab03fbe0fe0927a9242754a0797553f6f7f099", features = ["std"] }
lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "35ab03fbe0fe0927a9242754a0797553f6f7f099" }
lightning-dns-resolver = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "35ab03fbe0fe0927a9242754a0797553f6f7f099" }

bdk_chain = { version = "0.23.0", default-features = false, features = ["std"] }
bdk_esplora = { version = "0.22.0", default-features = false, features = ["async-https-rustls", "tokio"]}
Expand Down Expand Up @@ -141,6 +144,7 @@ harness = false
#lightning-transaction-sync = { path = "../rust-lightning/lightning-transaction-sync" }
#lightning-liquidity = { path = "../rust-lightning/lightning-liquidity" }
#lightning-macros = { path = "../rust-lightning/lightning-macros" }
#lightning-dns-resolver = { path = "../rust-lightning/lightning-dns-resolver" }

#lightning = { git = "https://github.com/lightningdevkit/rust-lightning", branch = "main" }
#lightning-types = { git = "https://github.com/lightningdevkit/rust-lightning", branch = "main" }
Expand All @@ -153,6 +157,7 @@ harness = false
#lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", branch = "main" }
#lightning-liquidity = { git = "https://github.com/lightningdevkit/rust-lightning", branch = "main" }
#lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning", branch = "main" }
#lightning-dns-resolver = { git = "https://github.com/lightningdevkit/rust-lightning", branch = "main" }

#lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "21e9a9c0ef80021d0669f2a366f55d08ba8d9b03" }
#lightning-types = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "21e9a9c0ef80021d0669f2a366f55d08ba8d9b03" }
Expand All @@ -165,6 +170,7 @@ harness = false
#lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "21e9a9c0ef80021d0669f2a366f55d08ba8d9b03" }
#lightning-liquidity = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "21e9a9c0ef80021d0669f2a366f55d08ba8d9b03" }
#lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "21e9a9c0ef80021d0669f2a366f55d08ba8d9b03" }
#lightning-dns-resolver = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "21e9a9c0ef80021d0669f2a366f55d08ba8d9b03" }

#vss-client-ng = { path = "../vss-client" }
#vss-client-ng = { git = "https://github.com/lightningdevkit/vss-client", branch = "main" }
Expand All @@ -181,3 +187,4 @@ harness = false
#lightning-transaction-sync = { path = "../rust-lightning/lightning-transaction-sync" }
#lightning-liquidity = { path = "../rust-lightning/lightning-liquidity" }
#lightning-macros = { path = "../rust-lightning/lightning-macros" }
#lightning-dns-resolver = { path = "../rust-lightning/lightning-dns-resolver" }
1 change: 1 addition & 0 deletions benches/payments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ fn payment_benchmark(c: &mut Criterion) {
true,
false,
common::TestStoreType::Sqlite,
false,
);

let runtime =
Expand Down
14 changes: 14 additions & 0 deletions bindings/ldk_node.udl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ dictionary Config {
u64 probing_liquidity_limit_multiplier;
AnchorChannelsConfig? anchor_channels_config;
RouteParametersConfig? route_parameters;
HumanReadableNamesConfig? hrn_config;
};

dictionary AnchorChannelsConfig {
Expand Down Expand Up @@ -361,6 +362,7 @@ enum NodeError {
"InvalidBlindedPaths",
"AsyncPaymentServicesDisabled",
"HrnParsingFailed",
"HrnResolverNotConfigured",
};

dictionary NodeStatus {
Expand Down Expand Up @@ -395,6 +397,7 @@ enum BuildError {
"LoggerSetupFailed",
"NetworkMismatch",
"AsyncPaymentsConfigMismatch",
"DNSResolverSetupFailed",
};

[Trait]
Expand Down Expand Up @@ -514,6 +517,17 @@ dictionary RouteParametersConfig {
u8 max_channel_saturation_power_of_half;
};

[Enum]
interface HRNResolverConfig {
Blip32Onion();
LocalDns(string dns_server_address);
};

dictionary HumanReadableNamesConfig {
HRNResolverConfig client_resolution_config;
boolean disable_hrn_resolution_service;
};

dictionary CustomTlvRecord {
u64 type_num;
sequence<u8> value;
Expand Down
100 changes: 85 additions & 15 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use std::collections::HashMap;
use std::convert::TryInto;
use std::default::Default;
use std::path::PathBuf;
use std::sync::{Arc, Mutex, Once, RwLock};
use std::sync::{Arc, Mutex, Once, RwLock, Weak};
use std::time::SystemTime;
use std::{fmt, fs};

Expand All @@ -19,12 +19,14 @@ use bitcoin::bip32::{ChildNumber, Xpriv};
use bitcoin::key::Secp256k1;
use bitcoin::secp256k1::PublicKey;
use bitcoin::{BlockHash, Network};
use bitcoin_payment_instructions::dns_resolver::DNSHrnResolver;
use bitcoin_payment_instructions::onion_message_resolver::LDKOnionMessageDNSSECHrnResolver;
use lightning::chain::{chainmonitor, BestBlock};
use lightning::ln::channelmanager::{self, ChainParameters, ChannelManagerReadArgs};
use lightning::ln::msgs::{RoutingMessageHandler, SocketAddress};
use lightning::ln::peer_handler::{IgnoringMessageHandler, MessageHandler};
use lightning::log_trace;
use lightning::onion_message::dns_resolution::DNSResolverMessageHandler;
use lightning::routing::gossip::NodeAlias;
use lightning::routing::router::DefaultRouter;
use lightning::routing::scoring::{
Expand All @@ -39,13 +41,14 @@ use lightning::util::persist::{
};
use lightning::util::ser::ReadableArgs;
use lightning::util::sweep::OutputSweeper;
use lightning_dns_resolver::OMDomainResolver;
use lightning_persister::fs_store::FilesystemStore;
use vss_client::headers::VssHeaderProvider;

use crate::chain::ChainSource;
use crate::config::{
default_user_config, may_announce_channel, AnnounceError, AsyncPaymentsRole,
BitcoindRestClientConfig, Config, ElectrumSyncConfig, EsploraSyncConfig,
BitcoindRestClientConfig, Config, ElectrumSyncConfig, EsploraSyncConfig, HRNResolverConfig,
DEFAULT_ESPLORA_SERVER_URL, DEFAULT_LOG_FILENAME, DEFAULT_LOG_LEVEL,
};
use crate::connection::ConnectionManager;
Expand Down Expand Up @@ -76,8 +79,8 @@ use crate::runtime::{Runtime, RuntimeSpawner};
use crate::tx_broadcaster::TransactionBroadcaster;
use crate::types::{
AsyncPersister, ChainMonitor, ChannelManager, DynStore, DynStoreWrapper, GossipSync, Graph,
KeysManager, MessageRouter, OnionMessenger, PaymentStore, PeerManager, PendingPaymentStore,
Persister, SyncAndAsyncKVStore,
HRNResolver, KeysManager, MessageRouter, OnionMessenger, PaymentStore, PeerManager,
PendingPaymentStore, Persister, SyncAndAsyncKVStore,
};
use crate::wallet::persist::KVStoreWalletPersister;
use crate::wallet::Wallet;
Expand Down Expand Up @@ -189,6 +192,8 @@ pub enum BuildError {
NetworkMismatch,
/// The role of the node in an asynchronous payments context is not compatible with the current configuration.
AsyncPaymentsConfigMismatch,
/// An attempt to setup a DNS Resolver failed.
DNSResolverSetupFailed,
}

impl fmt::Display for BuildError {
Expand Down Expand Up @@ -221,6 +226,9 @@ impl fmt::Display for BuildError {
"The async payments role is not compatible with the current configuration."
)
},
Self::DNSResolverSetupFailed => {
write!(f, "An attempt to setup a DNS resolver has failed.")
},
}
}
}
Expand Down Expand Up @@ -1517,7 +1525,70 @@ fn build_with_store_internal(
})?;
}

let hrn_resolver = Arc::new(LDKOnionMessageDNSSECHrnResolver::new(Arc::clone(&network_graph)));
let peer_manager_hook: Arc<Mutex<Option<Weak<PeerManager>>>> = Arc::new(Mutex::new(None));
let mut hrn_resolver_out = None;

let om_resolver = match &config.hrn_config {
None => Arc::new(IgnoringMessageHandler {}),
Some(hrn_config) => {
let client_resolver: Arc<dyn DNSResolverMessageHandler + Send + Sync> =
match &hrn_config.client_resolution_config {
HRNResolverConfig::Blip32Onion => {
let hrn_res = Arc::new(LDKOnionMessageDNSSECHrnResolver::new(Arc::clone(
&network_graph,
)));
hrn_resolver_out = Some(Arc::new(HRNResolver::Onion(Arc::clone(&hrn_res))));

let pm_hook_clone = Arc::clone(&peer_manager_hook);
hrn_res.register_post_queue_action(Box::new(move || {
if let Ok(guard) = pm_hook_clone.lock() {
if let Some(weak_pm) = &*guard {
if let Some(pm) = weak_pm.upgrade() {
pm.process_events();
}
}
}
}));

hrn_res
},
HRNResolverConfig::LocalDns { dns_server_address } => {
let addr = dns_server_address
.parse()
.map_err(|_| BuildError::DNSResolverSetupFailed)?;
let hrn_res = Arc::new(DNSHrnResolver(addr));
hrn_resolver_out = Some(Arc::new(HRNResolver::Local(hrn_res)));
Arc::new(OMDomainResolver::ignoring_incoming_proofs(addr))
},
};

let should_act_as_service = if hrn_config.disable_hrn_resolution_service {
false
} else {
may_announce_channel(&config).is_ok()
};

if should_act_as_service {
if let HRNResolverConfig::LocalDns { dns_server_address } =
&hrn_config.client_resolution_config
{
let service_dns_addr = dns_server_address
.parse()
.map_err(|_| BuildError::DNSResolverSetupFailed)?;
Arc::new(OMDomainResolver::with_runtime(
service_dns_addr,
Some(client_resolver),
Some(runtime.handle().clone()),
))
} else {
log_error!(logger, "To act as an HRN resolution service, the DNS resolver must be configured to use a DNS server.");
return Err(BuildError::DNSResolverSetupFailed);
}
} else {
client_resolver
}
},
};

// Initialize the PeerManager
let onion_messenger: Arc<OnionMessenger> =
Expand All @@ -1530,7 +1601,7 @@ fn build_with_store_internal(
message_router,
Arc::clone(&channel_manager),
Arc::clone(&channel_manager),
Arc::clone(&hrn_resolver),
Arc::clone(&om_resolver),
IgnoringMessageHandler {},
))
} else {
Expand All @@ -1542,7 +1613,7 @@ fn build_with_store_internal(
message_router,
Arc::clone(&channel_manager),
Arc::clone(&channel_manager),
Arc::clone(&hrn_resolver),
Arc::clone(&om_resolver),
IgnoringMessageHandler {},
))
};
Expand Down Expand Up @@ -1663,7 +1734,7 @@ fn build_with_store_internal(
BuildError::InvalidSystemTime
})?;

let peer_manager = Arc::new(PeerManager::new(
let peer_manager: Arc<PeerManager> = Arc::new(PeerManager::new(
msg_handler,
cur_time.as_secs().try_into().map_err(|e| {
log_error!(logger, "Failed to get current time: {}", e);
Expand All @@ -1674,12 +1745,11 @@ fn build_with_store_internal(
Arc::clone(&keys_manager),
));

let peer_manager_clone = Arc::downgrade(&peer_manager);
hrn_resolver.register_post_queue_action(Box::new(move || {
if let Some(upgraded_pointer) = peer_manager_clone.upgrade() {
upgraded_pointer.process_events();
}
}));
if let Ok(mut guard) = peer_manager_hook.lock() {
*guard = Some(Arc::downgrade(&peer_manager));
} else {
return Err(BuildError::DNSResolverSetupFailed);
}

liquidity_source.as_ref().map(|l| l.set_peer_manager(Arc::downgrade(&peer_manager)));

Expand Down Expand Up @@ -1786,7 +1856,7 @@ fn build_with_store_internal(
node_metrics,
om_mailbox,
async_payments_role,
hrn_resolver,
hrn_resolver: hrn_resolver_out,
#[cfg(cycle_tests)]
_leak_checker,
})
Expand Down
42 changes: 41 additions & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,8 @@ pub(crate) const HRN_RESOLUTION_TIMEOUT_SECS: u64 = 5;
/// | `probing_liquidity_limit_multiplier` | 3 |
/// | `log_level` | Debug |
/// | `anchor_channels_config` | Some(..) |
/// | `route_parameters` | None |
/// | `route_parameters` | None |
/// | `hrn_config` | Some(..) |
///
/// See [`AnchorChannelsConfig`] and [`RouteParametersConfig`] for more information regarding their
/// respective default values.
Expand Down Expand Up @@ -192,6 +193,10 @@ pub struct Config {
/// **Note:** If unset, default parameters will be used, and you will be able to override the
/// parameters on a per-payment basis in the corresponding method calls.
pub route_parameters: Option<RouteParametersConfig>,
/// Configuration options for Human-Readable Names ([BIP 353]).
///
/// [BIP 353]: https://github.com/bitcoin/bips/blob/master/bip-0353.mediawiki
pub hrn_config: Option<HumanReadableNamesConfig>,
}

impl Default for Config {
Expand All @@ -206,6 +211,41 @@ impl Default for Config {
anchor_channels_config: Some(AnchorChannelsConfig::default()),
route_parameters: None,
node_alias: None,
hrn_config: Some(HumanReadableNamesConfig::default()),
}
}
}

/// Configuration options for how our node resolves Human-Readable Names (HRNs) when acting as a client.
#[derive(Debug, Clone)]
pub enum HRNResolverConfig {
/// Use bLIP-32 to ask other nodes to resolve names for us.
Blip32Onion,
/// Resolve names locally using a specific DNS server.
LocalDns {
/// The IP and port of the DNS server (e.g., "8.8.8.8:53").
dns_server_address: String,
},
}

/// Configuration options for Human-Readable Names ([BIP 353]).
///
/// [BIP 353]: https://github.com/bitcoin/bips/blob/master/bip-0353.mediawiki
#[derive(Debug, Clone)]
pub struct HumanReadableNamesConfig {
/// This sets how our node resolves names when we want to send a payment.
pub client_resolution_config: HRNResolverConfig,
/// if set, this allows others to use our node for HRN resolutions.
pub disable_hrn_resolution_service: bool,
}

impl Default for HumanReadableNamesConfig {
fn default() -> Self {
HumanReadableNamesConfig {
client_resolution_config: HRNResolverConfig::LocalDns {
dns_server_address: "8.8.8.8:53".to_string(),
},
disable_hrn_resolution_service: false,
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ pub enum Error {
AsyncPaymentServicesDisabled,
/// Parsing a Human-Readable Name has failed.
HrnParsingFailed,
/// A HRN resolver was not configured
HrnResolverNotConfigured,
}

impl fmt::Display for Error {
Expand Down Expand Up @@ -213,6 +215,9 @@ impl fmt::Display for Error {
Self::HrnParsingFailed => {
write!(f, "Failed to parse a human-readable name.")
},
Self::HrnResolverNotConfigured => {
write!(f, "A HRN resolver was not configured.")
},
}
}
}
Expand Down
Loading
Loading