bitcoin_payment_instructions/
onion_message_resolver.rs1use std::boxed::Box;
5use std::collections::HashMap;
6use std::future::Future;
7use std::ops::Deref;
8use std::pin::Pin;
9use std::sync::atomic::{AtomicUsize, Ordering};
10use std::sync::{Arc, Mutex, RwLock};
11use std::task::{Context, Poll, Waker};
12use std::vec::Vec;
13
14use lightning::blinded_path::message::{DNSResolverContext, MessageContext};
15use lightning::ln::channelmanager::PaymentId;
16use lightning::onion_message::dns_resolution::{
17 DNSResolverMessage, DNSResolverMessageHandler, DNSSECProof, DNSSECQuery, OMNameResolver,
18};
19use lightning::onion_message::messenger::{
20 Destination, MessageSendInstructions, Responder, ResponseInstruction,
21};
22use lightning::routing::gossip::NetworkGraph;
23use lightning::sign::EntropySource;
24use lightning::util::logger::Logger;
25
26use crate::hrn_resolution::{
27 HrnResolution, HrnResolutionFuture, HrnResolver, HumanReadableName, LNURLResolutionFuture,
28};
29use crate::Amount;
30
31struct OsRng;
32impl EntropySource for OsRng {
33 fn get_secure_random_bytes(&self) -> [u8; 32] {
34 let mut res = [0; 32];
35 getrandom::fill(&mut res).expect("Fetching system randomness should always succeed");
36 res
37 }
38}
39
40struct ChannelState {
41 waker: Option<Waker>,
42 result: Option<HrnResolution>,
43}
44
45struct ChannelSend(Arc<Mutex<ChannelState>>);
46
47impl ChannelSend {
48 fn complete(self, result: HrnResolution) {
49 let mut state = self.0.lock().unwrap();
50 state.result = Some(result);
51 if let Some(waker) = state.waker.take() {
52 waker.wake();
53 }
54 }
55
56 fn receiver_alive(&self) -> bool {
57 Arc::strong_count(&self.0) > 1
58 }
59}
60
61struct ChannelRecv(Arc<Mutex<ChannelState>>);
62
63impl Future for ChannelRecv {
64 type Output = HrnResolution;
65 fn poll(self: Pin<&mut Self>, context: &mut Context) -> Poll<HrnResolution> {
66 let mut state = self.0.lock().unwrap();
67 if let Some(res) = state.result.take() {
68 debug_assert!(state.waker.is_none());
69 Poll::Ready(res)
70 } else {
71 state.waker = Some(context.waker().clone());
72 Poll::Pending
73 }
74 }
75}
76
77fn channel() -> (ChannelSend, ChannelRecv) {
78 let state = Arc::new(Mutex::new(ChannelState { waker: None, result: None }));
79 (ChannelSend(Arc::clone(&state)), ChannelRecv(state))
80}
81
82pub struct LDKOnionMessageDNSSECHrnResolver<N: Deref<Target = NetworkGraph<L>>, L: Deref>
98where
99 L::Target: Logger,
100{
101 network_graph: N,
102 resolver: OMNameResolver,
103 next_id: AtomicUsize,
104 pending_resolutions: Mutex<HashMap<HumanReadableName, Vec<(PaymentId, ChannelSend)>>>,
105 message_queue: Mutex<Vec<(DNSResolverMessage, MessageSendInstructions)>>,
106 pm_event_poker: RwLock<Option<Box<dyn Fn() + Send + Sync>>>,
107}
108
109impl<N: Deref<Target = NetworkGraph<L>>, L: Deref> LDKOnionMessageDNSSECHrnResolver<N, L>
110where
111 L::Target: Logger,
112{
113 pub fn new(network_graph: N) -> Self {
117 Self {
118 network_graph,
119 next_id: AtomicUsize::new(0),
120 resolver: OMNameResolver::new(0, 0),
122 pending_resolutions: Mutex::new(HashMap::new()),
123 message_queue: Mutex::new(Vec::new()),
124 pm_event_poker: RwLock::new(None),
125 }
126 }
127
128 pub fn register_post_queue_action(&self, callback: Box<dyn Fn() + Send + Sync>) {
133 *self.pm_event_poker.write().unwrap() = Some(callback);
134 }
135
136 fn init_resolve_hrn<'a>(
137 &'a self, hrn: &HumanReadableName,
138 ) -> Result<ChannelRecv, &'static str> {
139 #[cfg(feature = "std")]
140 {
141 use std::time::SystemTime;
142 let clock_err =
143 "DNSSEC validation relies on having a correct system clock. It is currently set before 1970.";
144 let now =
145 SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).map_err(|_| clock_err)?;
146 self.resolver.new_best_block((now.as_secs() / 60) as u32, now.as_secs() as u32);
148 }
149
150 let mut dns_resolvers = Vec::new();
151 for (node_id, node) in self.network_graph.read_only().nodes().unordered_iter() {
152 if let Some(info) = &node.announcement_info {
153 let supports_dns = info.features().supports_dns_resolution();
159 let supports_om = info.features().supports_onion_messages();
160 if supports_dns && supports_om {
161 if let Ok(pubkey) = node_id.as_pubkey() {
162 dns_resolvers.push(Destination::Node(pubkey));
163 }
164 }
165 }
166 if dns_resolvers.len() > 5 {
167 break;
168 }
169 }
170 if dns_resolvers.is_empty() {
171 return Err(
172 "Failed to find any DNS resolving nodes, check your network graph is synced",
173 );
174 }
175
176 let counter = self.next_id.fetch_add(1, Ordering::Relaxed) as u64;
177 let mut payment_id = [0; 32];
178 payment_id[..8].copy_from_slice(&counter.to_ne_bytes());
179 let payment_id = PaymentId(payment_id);
180
181 let err = "The provided HRN did not fit in a DNS request";
182 let (query, dns_context) =
185 self.resolver.resolve_name(payment_id, *hrn, &OsRng).map_err(|_| err)?;
186 let context = MessageContext::DNSResolver(dns_context);
187
188 let (send, recv) = channel();
189 {
190 let mut pending_resolutions = self.pending_resolutions.lock().unwrap();
191 let senders = pending_resolutions.entry(*hrn).or_insert_with(Vec::new);
192 senders.push((payment_id, send));
193
194 pending_resolutions.retain(|_name, resolutions| {
197 resolutions.retain(|(_payment_id, resolution)| {
198 let has_receiver = resolution.receiver_alive();
199 if !has_receiver {
200 }
203 has_receiver
204 });
205 !resolutions.is_empty()
206 });
207 }
208
209 {
210 let mut queue = self.message_queue.lock().unwrap();
211 for destination in dns_resolvers {
212 let instructions = MessageSendInstructions::WithReplyPath {
213 destination,
214 context: context.clone(),
215 };
216 queue.push((DNSResolverMessage::DNSSECQuery(query.clone()), instructions));
217 }
218 }
219
220 let callback = self.pm_event_poker.read().unwrap();
221 if let Some(callback) = &*callback {
222 callback();
223 }
224
225 Ok(recv)
226 }
227}
228
229impl<N: Deref<Target = NetworkGraph<L>>, L: Deref> DNSResolverMessageHandler
230 for LDKOnionMessageDNSSECHrnResolver<N, L>
231where
232 L::Target: Logger,
233{
234 fn handle_dnssec_query(
235 &self, _: DNSSECQuery, _: Option<Responder>,
236 ) -> Option<(DNSResolverMessage, ResponseInstruction)> {
237 None
238 }
239
240 fn handle_dnssec_proof(&self, msg: DNSSECProof, context: DNSResolverContext) {
241 let results = self.resolver.handle_dnssec_proof_for_uri(msg.clone(), context);
242 if let Some((resolved, res)) = results {
243 let mut pending_resolutions = self.pending_resolutions.lock().unwrap();
244 for (name, _payment_id) in resolved {
245 if let Some(requests) = pending_resolutions.remove(&name) {
246 for (_id, send) in requests {
247 send.complete(HrnResolution::DNSSEC {
248 proof: Some(msg.proof.clone()),
249 result: res.clone(),
250 });
251 }
252 }
253 }
254 }
255 }
256
257 fn release_pending_messages(&self) -> Vec<(DNSResolverMessage, MessageSendInstructions)> {
258 std::mem::take(&mut self.message_queue.lock().unwrap())
259 }
260}
261
262impl<N: Deref<Target = NetworkGraph<L>> + Sync, L: Deref> HrnResolver
263 for LDKOnionMessageDNSSECHrnResolver<N, L>
264where
265 L::Target: Logger,
266{
267 fn resolve_hrn<'a>(&'a self, hrn: &'a HumanReadableName) -> HrnResolutionFuture<'a> {
268 match self.init_resolve_hrn(hrn) {
269 Err(e) => Box::pin(async move { Err(e) }),
270 Ok(recv) => Box::pin(async move { Ok(recv.await) }),
271 }
272 }
273
274 fn resolve_lnurl<'a>(&'a self, _url: &'a str) -> HrnResolutionFuture<'a> {
275 let err = "DNS resolver does not support LNURL resolution";
276 Box::pin(async move { Err(err) })
277 }
278
279 fn resolve_lnurl_to_invoice<'a>(
280 &'a self, _: String, _: Amount, _: [u8; 32],
281 ) -> LNURLResolutionFuture<'a> {
282 let err = "resolve_lnurl_to_invoice shouldn't be called when we don't resolve LNURL";
283 debug_assert!(false, "{err}");
284 Box::pin(async move { Err(err) })
285 }
286}
287
288#[cfg(test)]
289mod tests {
290 use super::*;
291 use crate::*;
292
293 use std::net::ToSocketAddrs;
294
295 use bitcoin::hex::FromHex;
296 use bitcoin::secp256k1::PublicKey;
297
298 use lightning::blinded_path::NodeIdLookUp;
299 use lightning::ln::peer_handler::{
300 ErroringMessageHandler, IgnoringMessageHandler, MessageHandler, PeerManager,
301 };
302 use lightning::onion_message::messenger::{DefaultMessageRouter, OnionMessenger};
303 use lightning::routing::gossip::{NodeId, P2PGossipSync};
304 use lightning::routing::utxo::UtxoLookup;
305 use lightning::sign::KeysManager;
306 use lightning::util::logger::Record;
307
308 struct TestLogger;
309 impl Logger for TestLogger {
310 fn log(&self, r: Record) {
311 eprintln!("{}", r.args);
312 }
313 }
314
315 struct NoPeers;
316 impl NodeIdLookUp for NoPeers {
317 fn next_node_id(&self, _scid: u64) -> Option<PublicKey> {
318 None
319 }
320 }
321
322 #[tokio::test]
323 async fn test_dns_om_hrn_resolver() {
324 let graph = Arc::new(NetworkGraph::new(Network::Bitcoin, &TestLogger));
325 let resolver = Arc::new(LDKOnionMessageDNSSECHrnResolver::new(Arc::clone(&graph)));
326 let signer = Arc::new(KeysManager::new(&OsRng.get_secure_random_bytes(), 0, 0, true));
327 let message_router = Arc::new(DefaultMessageRouter::new(Arc::clone(&graph), &OsRng));
328 let messenger = Arc::new(OnionMessenger::new(
329 &OsRng,
330 Arc::clone(&signer),
331 &TestLogger,
332 &NoPeers,
333 message_router,
334 &IgnoringMessageHandler {},
335 &IgnoringMessageHandler {},
336 Arc::clone(&resolver),
337 &IgnoringMessageHandler {},
338 ));
339 let no_utxos = None::<&(dyn UtxoLookup + Sync + Send)>;
340 let handlers = MessageHandler {
341 chan_handler: Arc::new(ErroringMessageHandler::new()),
342 route_handler: Arc::new(P2PGossipSync::new(Arc::clone(&graph), no_utxos, &TestLogger)),
343 onion_message_handler: Arc::clone(&messenger),
344 custom_message_handler: &IgnoringMessageHandler {},
345 send_only_message_handler: &IgnoringMessageHandler {},
346 };
347 let rand = OsRng.get_secure_random_bytes();
348 let peer_manager =
349 Arc::new(PeerManager::new(handlers, 0, &rand, &TestLogger, Arc::clone(&signer)));
350
351 let their_id_hex = "03db10aa09ff04d3568b0621750794063df401e6853c79a21a83e1a3f3b5bfb0c8";
353 let their_id = PublicKey::from_slice(&Vec::<u8>::from_hex(their_id_hex).unwrap()).unwrap();
354 let addr = "ldk-ln-node.bitcoin.ninja:9735".to_socket_addrs().unwrap().next().unwrap();
355 let connect_pm = Arc::clone(&peer_manager);
356 tokio::spawn(async move {
357 loop {
358 lightning_net_tokio::connect_outbound(Arc::clone(&connect_pm), their_id, addr)
359 .await
360 .unwrap()
361 .await;
362 }
363 });
364
365 let pm_reference = Arc::clone(&peer_manager);
366 tokio::spawn(async move {
367 pm_reference.process_events();
368 tokio::time::sleep(Duration::from_micros(10)).await;
369 });
370
371 let their_node_id = NodeId::from_pubkey(&their_id);
372 loop {
373 {
374 let graph = graph.read_only();
375 let have_announcement =
376 graph.nodes().get(&their_node_id).map(|node| node.announcement_info.is_some());
377 if have_announcement.unwrap_or(false) {
378 break;
379 }
380 }
381 tokio::time::sleep(Duration::from_millis(5)).await;
382 peer_manager.process_events();
383 }
384
385 let instructions = PaymentInstructions::parse(
386 "send.some@satsto.me",
387 bitcoin::Network::Bitcoin,
388 &*resolver,
389 true,
390 )
391 .await
392 .unwrap();
393
394 let resolved = if let PaymentInstructions::ConfigurableAmount(instr) = instructions {
395 assert_eq!(instr.min_amt(), None);
396 assert_eq!(instr.max_amt(), None);
397
398 assert_eq!(instr.pop_callback(), None);
399 assert!(instr.bip_353_dnssec_proof().is_some());
400
401 let hrn = instr.human_readable_name().as_ref().unwrap();
402 assert_eq!(hrn.user(), "send.some");
403 assert_eq!(hrn.domain(), "satsto.me");
404
405 instr.set_amount(Amount::from_sats(100_000).unwrap(), &*resolver).await.unwrap()
406 } else {
407 panic!();
408 };
409
410 assert_eq!(resolved.pop_callback(), None);
411 assert!(resolved.bip_353_dnssec_proof().is_some());
412
413 let hrn = resolved.human_readable_name().as_ref().unwrap();
414 assert_eq!(hrn.user(), "send.some");
415 assert_eq!(hrn.domain(), "satsto.me");
416
417 for method in resolved.methods() {
418 match method {
419 PaymentMethod::LightningBolt11(_) => {
420 panic!("Should only have static payment instructions");
421 },
422 PaymentMethod::LightningBolt12(_) => {},
423 PaymentMethod::OnChain { .. } => {},
424 }
425 }
426 }
427}