Skip to content

Allow choosing the BIP 353 HRN resolver — every HRN send currently goes through dns.google #105

Description

@hash-money

The situation

parse_payment_instructions hardcodes the DoH resolver, so every BIP 353 send resolves the payee's name through Google:

// orange-sdk/src/lib.rs:1224 (master)
PaymentInstructions::parse(instructions, self.inner.network, &HTTPHrnResolver::new(), true)
// orange-sdk/src/lib.rs:1364 (master) — same in the ConfigurableAmount path
conf.clone().set_amount(instructions.amount, &HTTPHrnResolver::new()).await;

HTTPHrnResolver's endpoint is a private const in bitcoin-payment-instructions:

const DOH_ENDPOINT: &'static str = "https://dns.google/dns-query?dns=";

HTTPHrnResolver::with_client() accepts a custom reqwest::Client, but that only swaps TLS/proxy settings — the URL is still dns.google. There is no WalletConfig field, no builder, and no public API taking an HrnResolver, on master or on our pin.

Scope — deliberately narrow

To be precise about what this is and isn't:

  • Security is fine. The DNSSEC proof is built and validated locally (dnssec_utils::resolve_proof), so the resolver cannot forge an answer. This is not a spoofing report.
  • It is only BIP 353 sends. resolve_hrn fires only when HumanReadableName::from_encoded() succeeds (bitcoin-payment-instructions/src/lib.rs:923), so plain BOLT11/BOLT12/on-chain destinations never touch DNS. No blanket per-payment leak.

What remains is that for every user@domain payment, one specific third party learns this IP is about to pay this person. For a Bitcoin wallet that is the payee-identity metadata we would least like to hand to one company, and users can't opt out.

Why we can't just work around it

HRN resolution happens inside your parse_payment_instructions, so the only downstream option is forking orange-sdk — a maintained fork of the custody engine, purely to change one constructor. That's the kind of permanent tech debt we try not to take on.

Suggested change

The capability already exists in the dependency — bitcoin-payment-instructions ships DNSHrnResolver(SocketAddr) (any resolver, including self-hosted) and LDKOnionMessageDNSSECHrnResolver (resolution over onion messages, the privacy-preserving path) alongside HTTPHrnResolver. Only the choice is missing.

WalletConfig already has precedent for exactly this shape — scorer_url: Option<String> and rgs_url: Option<String> are optional endpoints for external services. An hrn_resolver / dns_resolver field in the same spirit, defaulting to today's HTTPHrnResolver so nothing changes for existing users, would cover it.

Happy to send the PR if you'd like it — it's threading a resolver through two call sites plus a config field, and we'd test it on our staging setup (A12 + signet) before proposing it.

Priority

Low and not urgent — we have no BIP 353 traffic in production yet. Filing it now because it's cheap to fix before anyone depends on the current behaviour, and much more awkward afterwards.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions