Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/ffi/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ impl std::fmt::Display for Offer {
}
}

#[derive(Eq, Hash, PartialEq)]
pub struct HumanReadableName {
pub(crate) inner: LdkHumanReadableName,
}
Expand Down
57 changes: 57 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1631,3 +1631,60 @@ pub(crate) fn total_anchor_channels_reserve_sats(
* anchor_channels_config.per_channel_reserve_sats
})
}

/// Testing utils for DNSSEC proof resolution of offers associated with the given HRN.
pub mod dnssec_testing_utils {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's introduce a new cfg(hrn_tests) gate and move all the testing-related changes behind it, to make sure we don't ever run it in production code.

use std::collections::HashMap;
#[cfg(feature = "uniffi")]
use std::sync::Arc;
use std::sync::{LazyLock, Mutex};

#[cfg(not(feature = "uniffi"))]
type Offer = lightning::offers::offer::Offer;
#[cfg(feature = "uniffi")]
type Offer = Arc<crate::ffi::Offer>;

#[cfg(not(feature = "uniffi"))]
type HumanReadableName = lightning::onion_message::dns_resolution::HumanReadableName;
#[cfg(feature = "uniffi")]
type HumanReadableName = Arc<crate::ffi::HumanReadableName>;

static OFFER_OVERRIDE_MAP: LazyLock<Mutex<HashMap<HumanReadableName, Offer>>> =
LazyLock::new(|| Mutex::new(HashMap::new()));

/// Sets a testing override for DNSSEC proof resolution of offers associated with the given HRN.
pub fn set_testing_dnssec_proof_offer_resolution_override(hrn: &str, offer: Offer) {
let hrn_key = {
#[cfg(not(feature = "uniffi"))]
{
lightning::onion_message::dns_resolution::HumanReadableName::from_encoded(hrn)
.unwrap()
}

#[cfg(feature = "uniffi")]
{
Arc::new(crate::ffi::HumanReadableName::from_encoded(hrn).unwrap())
}
};

OFFER_OVERRIDE_MAP.lock().unwrap().insert(hrn_key, offer);
}

/// Retrieves a testing override for DNSSEC proof resolution of offers associated with the given HRNs.
#[cfg(not(feature = "uniffi"))]
pub fn get_testing_offer_override(hrn: Option<HumanReadableName>) -> Option<Offer> {
OFFER_OVERRIDE_MAP.lock().unwrap().get(&hrn?).cloned()
}

/// Retrieves a testing override for DNSSEC proof resolution of offers associated with the given HRNs.
#[cfg(feature = "uniffi")]
pub fn get_testing_offer_override(hrn: Option<HumanReadableName>) -> Option<Offer> {
let offer = OFFER_OVERRIDE_MAP.lock().unwrap().get(&hrn?).cloned().unwrap();
Some(offer)
}

/// Clears all testing overrides for DNSSEC proof resolution of offers.
pub fn clear_testing_overrides() {
OFFER_OVERRIDE_MAP.lock().unwrap().clear();
}
}
16 changes: 15 additions & 1 deletion src/payment/bolt12.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,21 @@ impl Bolt12Payment {
return Err(Error::NotRunning);
}

let offer = maybe_deref(offer);
let mut current_offer = offer.clone();

if let Some(hrn_ref) = hrn.as_ref() {
current_offer = match crate::dnssec_testing_utils::get_testing_offer_override(Some(
hrn_ref.clone(),
)) {
Some(offer) => {
log_info!(self.logger, "Using test-specific Offer override.");
offer
},
_ => offer.clone(),
};
}

let offer = maybe_deref(&current_offer);
Comment on lines +197 to +211
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be simplified a bit. The current_offer mutable variable and cloning feels unnecessary.

We could do something like:

let final_offer = if let Some(hrn_ref) = &hrn {
    get_testing_offer_override(Some(hrn_ref.clone()))
        .map(|override_offer| {
            log_info!(self.logger, "Using test-specific Offer override.");
            override_offer
        })
        .unwrap_or_else(|| offer.clone())
} else {
    offer.clone()
};

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I'll replace that with -->

let offer = if let Some(hrn_ref) = &hrn {
      crate::dnssec_testing_utils::get_testing_offer_override(Some(hrn_ref.clone()))
          .map(|override_offer| {
                  log_info!(self.logger, "Using test-specific Offer override.");
                  override_offer
          })
          .unwrap_or_else(|| offer.clone())
} else {
      offer.clone()
};

let offer = maybe_deref(&offer);


let mut random_bytes = [0u8; 32];
rand::thread_rng().fill_bytes(&mut random_bytes);
Expand Down
50 changes: 38 additions & 12 deletions src/payment/unified.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use std::vec::IntoIter;

use lightning::ln::channelmanager::PaymentId;
use lightning::offers::offer::Offer;
use lightning::onion_message::dns_resolution::HumanReadableName;
use lightning::onion_message::dns_resolution::HumanReadableName as LdkHumanReadableName;
use lightning_invoice::{Bolt11Invoice, Bolt11InvoiceDescription, Description};

use bip21::de::ParamKind;
Expand All @@ -40,6 +40,11 @@ use tokio::time::timeout;

type Uri<'a> = bip21::Uri<'a, NetworkChecked, Extras>;

#[cfg(not(feature = "uniffi"))]
type HumanReadableName = LdkHumanReadableName;
#[cfg(feature = "uniffi")]
type HumanReadableName = Arc<crate::ffi::HumanReadableName>;

#[derive(Debug, Clone)]
struct Extras {
bolt11_invoice: Option<Bolt11Invoice>,
Expand Down Expand Up @@ -162,18 +167,39 @@ impl UnifiedPayment {
Error::HrnResolverNotConfigured
})?;

println!("Parsing instructions...");
const TIMEOUT_DURATION: Duration = Duration::from_secs(30);

let instructions =
PaymentInstructions::parse(uri_str, self.config.network, resolver.as_ref(), false)
.await
.map_err(|e| {
log_error!(self.logger, "Failed to parse payment instructions: {:?}", e);
println!("Failed to parse payment instructions: {:?}", e);
Error::UriParameterParsingFailed
})?;
let target_network;

if let Ok(hrn) = LdkHumanReadableName::from_encoded(uri_str) {
let hrn: HumanReadableName = maybe_wrap(hrn.clone());

println!("Sending...");
target_network =
match crate::dnssec_testing_utils::get_testing_offer_override(Some(hrn.clone())) {
Some(_) => bitcoin::Network::Bitcoin,
_ => self.config.network,
};
} else {
target_network = self.config.network;
};

let instructions = timeout(
TIMEOUT_DURATION,
PaymentInstructions::parse(uri_str, target_network, resolver.as_ref(), false),
)
.await
.map_err(|_| {
log_error!(
self.logger,
"Payment instruction parsing timed out after {:?}",
TIMEOUT_DURATION
);
Error::TimeoutOccurred
})?
.map_err(|e| {
log_error!(self.logger, "Failed to parse payment instructions: {:?}", e);
Error::UriParameterParsingFailed
})?;

let resolved = match instructions {
PaymentInstructions::ConfigurableAmount(instr) => {
Expand Down Expand Up @@ -208,7 +234,7 @@ impl UnifiedPayment {
{
let offer = maybe_wrap(offer.clone());

let payment_result = if let Ok(hrn) = HumanReadableName::from_encoded(uri_str) {
let payment_result = if let Ok(hrn) = LdkHumanReadableName::from_encoded(uri_str) {
let hrn = maybe_wrap(hrn.clone());
self.bolt12_payment.send_using_amount(&offer, amount_msat.unwrap_or(0), None, None, Some(hrn))
} else if let Some(amount_msat) = amount_msat {
Expand Down
13 changes: 11 additions & 2 deletions tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ use bitcoin::{
use electrsd::corepc_node::{Client as BitcoindClient, Node as BitcoinD};
use electrsd::{corepc_node, ElectrsD};
use electrum_client::ElectrumApi;
use ldk_node::config::{AsyncPaymentsRole, Config, ElectrumSyncConfig, EsploraSyncConfig};
use ldk_node::config::{
AsyncPaymentsRole, Config, ElectrumSyncConfig, EsploraSyncConfig, HumanReadableNamesConfig,
};
use ldk_node::io::sqlite_store::SqliteStore;
use ldk_node::payment::{PaymentDirection, PaymentKind, PaymentStatus};
use ldk_node::{
Expand Down Expand Up @@ -281,14 +283,21 @@ pub(crate) use setup_builder;

pub(crate) fn setup_two_nodes(
chain_source: &TestChainSource, allow_0conf: bool, anchor_channels: bool,
anchors_trusted_no_reserve: bool,
anchors_trusted_no_reserve: bool, second_node_is_hrn_resolver: bool,
) -> (TestNode, TestNode) {
println!("== Node A ==");
let config_a = random_config(anchor_channels);
let node_a = setup_node(chain_source, config_a, None);

println!("\n== Node B ==");
let mut config_b = random_config(anchor_channels);
if second_node_is_hrn_resolver {
config_b.node_config.hrn_config = Some(HumanReadableNamesConfig {
default_dns_resolvers: Vec::new(),
is_hrn_resolver: true,
dns_server_address: "8.8.8.8:53".to_string(),
});
}
if allow_0conf {
config_b.node_config.trusted_peers_0conf.push(node_a.node_id());
}
Expand Down
Loading
Loading