1pub use crate::peer_parent::OverlayCommitSource;
6use crate::{
7 DialLimit, Error,
8 engine::Engine,
9 model::RecordPackage,
10 peer_codec::Codec,
11 peer_gossip::{Dial, Dials, Shared, is_loopback, validate_announce, validate_peers},
12 peer_inventory::{lookup, validate_inventory},
13 peer_limits::{LimitDenied, Limits},
14 peer_parent::{Parent, validate_parent},
15 peer_protocol::{GossipPeer, InventoryPage, Kind, PROTOCOL, Reply, Request, Response, VERSION},
16 peer_rate::{Bucket, Count, peer_rate},
17 peerbook::PeerAddress,
18 retrieval::RecordSource,
19};
20use bitcoin::{BlockHash, Txid};
21use libp2p::{
22 Multiaddr, PeerId, StreamProtocol, Swarm, SwarmBuilder,
23 futures::StreamExt,
24 identify,
25 identity::Keypair,
26 multiaddr::Protocol,
27 noise,
28 request_response::{self, Message, OutboundRequestId, ResponseChannel},
29 swarm::{ConnectionId, DialError, NetworkBehaviour, SwarmEvent},
30 tcp, yamux,
31};
32use std::{
33 collections::{HashMap, HashSet},
34 num::{NonZeroU8, NonZeroUsize},
35 sync::{
36 Arc,
37 atomic::{AtomicBool, AtomicU64, Ordering},
38 mpsc,
39 },
40 thread::JoinHandle,
41 time::{Duration, Instant},
42};
43use tokio::sync::mpsc as async_mpsc;
44use urma_chain::observation::Chain;
45
46const REQUEST_TIMEOUT: Duration = Duration::from_secs(8);
47pub(crate) const CALL_TIMEOUT: Duration = Duration::from_secs(10);
48const MAX_PENDING: usize = 8;
49const IDENTIFY_PROTOCOL: &str = "/urma/node/4";
50
51#[derive(NetworkBehaviour)]
52pub(crate) struct Behaviour {
53 limits: Limits,
55 records: request_response::Behaviour<Codec>,
56 identify: identify::Behaviour,
57}
58
59pub struct PeerNetwork {
63 peer: PeerId,
64 listen: Option<Multiaddr>,
65 pub(crate) commands: async_mpsc::Sender<Command>,
66 pub(crate) stop: Arc<AtomicBool>,
67 connected: Arc<AtomicU64>,
68 pub(crate) shared: Arc<Shared>,
69 worker: Option<JoinHandle<Result<(), Error>>>,
70}
71
72#[derive(Clone)]
76pub struct PeerSource {
77 address: PeerAddress,
78 commands: async_mpsc::Sender<Command>,
79 stop: Arc<AtomicBool>,
80 shared: Arc<Shared>,
81}
82
83pub(crate) enum Command {
84 Fetch(Fetch),
85 Disconnect(PeerId),
87 Dial(Dial),
88}
89
90pub(crate) struct Fetch {
92 peer: PeerId,
93 addresses: Vec<Multiaddr>,
94 kind: Kind,
95 deadline: Instant,
96 answer: Answer,
97}
98
99pub(crate) struct Pending {
100 peer: PeerId,
101 pub(crate) kind: Kind,
102 deadline: Instant,
103 answer: Answer,
104}
105
106pub(crate) enum Answer {
108 Record(mpsc::SyncSender<Result<Option<RecordPackage>, Error>>),
109 Parent(mpsc::SyncSender<Result<Parent, Error>>),
110 Inventory(mpsc::SyncSender<Result<Option<InventoryPage>, Error>>),
111 Peers(mpsc::SyncSender<Result<Vec<GossipPeer>, Error>>),
112}
113
114impl Answer {
115 fn fail(&self, error: Error) {
116 match self {
117 Answer::Record(sender) => {
118 let _ = sender.try_send(Err(error));
119 }
120 Answer::Parent(sender) => {
121 let _ = sender.try_send(Err(error));
122 }
123 Answer::Inventory(sender) => {
124 let _ = sender.try_send(Err(error));
125 }
126 Answer::Peers(sender) => {
127 let _ = sender.try_send(Err(error));
128 }
129 }
130 }
131
132 fn deliver(&self, genesis: BlockHash, peer: PeerId, pending: &Pending, response: Response) {
133 match self {
134 Answer::Record(sender) => {
135 let _ = sender.try_send(validate_response(genesis, peer, pending, response));
136 }
137 Answer::Parent(sender) => {
138 let _ = sender.try_send(validate_parent(genesis, peer, pending, response));
139 }
140 Answer::Inventory(sender) => {
141 let _ = sender.try_send(validate_inventory(genesis, peer, pending, response));
142 }
143 Answer::Peers(sender) => {
144 let _ = sender.try_send(validate_peers(genesis, peer, pending, response));
145 }
146 }
147 }
148}
149
150type Startup = mpsc::SyncSender<Result<Option<Multiaddr>, Error>>;
151struct ServeJob {
152 deadline: Instant,
153 request: Request,
154 channel: ResponseChannel<Response>,
155}
156struct Served {
157 response: Response,
158 channel: ResponseChannel<Response>,
159 bytes: usize,
160}
161
162impl PeerNetwork {
163 pub fn start(
168 engine: Arc<Engine>,
169 identity: Keypair,
170 listen: Option<Multiaddr>,
171 announce: Vec<Multiaddr>,
172 ) -> Result<Self, Error> {
173 if let Some(address) = &listen {
174 validate_listener(address)?;
175 }
176 engine.status()?;
177 let peer = identity.public().to_peer_id();
178 let announce = announce
179 .iter()
180 .map(|address| validate_announce(address, peer))
181 .collect::<Result<Vec<_>, _>>()?;
182 let (commands, receiver) = async_mpsc::channel(MAX_PENDING);
183 let (startup, ready) = mpsc::sync_channel(1);
184 let stop = Arc::new(AtomicBool::new(false));
185 let connected = Arc::new(AtomicU64::new(0));
186 let shared = Arc::new(Shared::new(peer));
187 let control = Control {
188 commands: receiver,
189 stop: stop.clone(),
190 connected: connected.clone(),
191 startup,
192 shared: shared.clone(),
193 announce,
194 };
195 let worker = std::thread::Builder::new()
196 .name("urma-peer".into())
197 .spawn(move || {
198 let runtime = tokio::runtime::Builder::new_current_thread()
199 .enable_all()
200 .build()?;
201 runtime.block_on(run(engine, identity, listen, control))
202 })?;
203 let actual = match ready.recv_timeout(Duration::from_secs(5)) {
204 Ok(Ok(address)) => address,
205 result => {
206 stop.store(true, Ordering::Release);
207 let joined = worker.join();
208 return Err(match result {
209 Ok(Err(error)) => error,
210 _ => match joined {
211 Ok(Err(error)) => error,
212 _ => Error::Busy("peer listener startup failed or expired".into()),
213 },
214 });
215 }
216 };
217 Ok(Self {
218 peer,
219 listen: actual,
220 commands,
221 stop,
222 connected,
223 shared,
224 worker: Some(worker),
225 })
226 }
227
228 pub fn peer_id(&self) -> PeerId {
229 self.peer
230 }
231 pub fn connected_peers(&self) -> u64 {
233 self.connected.load(Ordering::Relaxed)
234 }
235 pub fn listen_address(&self) -> Option<Multiaddr> {
236 self.listen.clone()
237 }
238
239 pub fn source(&self, address: PeerAddress) -> PeerSource {
240 PeerSource {
241 address,
242 commands: self.commands.clone(),
243 stop: self.stop.clone(),
244 shared: Arc::clone(&self.shared),
245 }
246 }
247
248 pub fn commit_source(&self, chain: Chain, peers: Vec<PeerAddress>) -> OverlayCommitSource {
249 OverlayCommitSource::new(
250 chain,
251 peers
252 .into_iter()
253 .map(|address| self.source(address))
254 .collect(),
255 )
256 }
257
258 pub fn shutdown(mut self) -> Result<(), Error> {
259 self.stop.store(true, Ordering::Release);
260 match self.worker.take().expect("network owner has worker").join() {
261 Ok(result) => result,
262 Err(_) => Err(Error::Invalid("peer worker panicked".into())),
263 }
264 }
265}
266
267impl Drop for PeerNetwork {
268 fn drop(&mut self) {
269 self.stop.store(true, Ordering::Release);
270 if let Some(worker) = self.worker.take() {
271 let _ = worker.join();
272 }
273 }
274}
275
276pub(crate) fn request<T>(
279 commands: &async_mpsc::Sender<Command>,
280 stop: &AtomicBool,
281 (peer, addresses): (PeerId, Vec<Multiaddr>),
282 kind: Kind,
283 limit: Instant,
284 answer: fn(mpsc::SyncSender<Result<T, Error>>) -> Answer,
285) -> Result<T, Error> {
286 if stop.load(Ordering::Acquire) {
287 return Err(Error::Busy("peer network stopped".into()));
288 }
289 let deadline = limit.min(Instant::now() + CALL_TIMEOUT);
290 let (sender, receiver) = mpsc::sync_channel(1);
291 commands
292 .try_send(Command::Fetch(Fetch {
293 peer,
294 addresses,
295 kind,
296 deadline,
297 answer: answer(sender),
298 }))
299 .map_err(|_| Error::Busy("peer command queue unavailable".into()))?;
300 receiver
301 .recv_timeout(deadline.saturating_duration_since(Instant::now()))
302 .map_err(|_| Error::Busy("peer acquisition deadline or shutdown".into()))?
303}
304
305impl PeerSource {
306 pub(crate) fn request<T>(
307 &self,
308 kind: Kind,
309 limit: Instant,
310 answer: fn(mpsc::SyncSender<Result<T, Error>>) -> Answer,
311 ) -> Result<T, Error> {
312 let target = (self.address.peer_id(), vec![self.address.address().clone()]);
313 request(&self.commands, &self.stop, target, kind, limit, answer)
314 }
315
316 pub(crate) fn parent(&self, txid: Txid, deadline: Instant) -> Result<Parent, Error> {
317 self.request(Kind::Parent { txid }, deadline, Answer::Parent)
318 }
319
320 pub(crate) fn connected(&self) -> bool {
322 self.shared.is_connected(self.address.peer_id())
323 }
324
325 pub(crate) fn disconnect(&self) {
327 let _ = self
328 .commands
329 .try_send(Command::Disconnect(self.address.peer_id()));
330 }
331}
332
333impl RecordSource for PeerSource {
334 fn fetch(&self, txid: Txid) -> Result<Option<RecordPackage>, Error> {
335 let kind = Kind::Record { txid };
336 self.request(kind, Instant::now() + CALL_TIMEOUT, Answer::Record)
337 }
338}
339
340fn validate_listener(address: &Multiaddr) -> Result<(), Error> {
341 let mut parts = address.iter();
342 let ip = matches!(
343 parts.next(),
344 Some(Protocol::Ip4(_)) | Some(Protocol::Ip6(_))
345 );
346 if !ip || !matches!(parts.next(), Some(Protocol::Tcp(_))) || parts.next().is_some() {
347 return Err(Error::Invalid(
348 "peer listener requires only IP and TCP".into(),
349 ));
350 }
351 Ok(())
352}
353
354fn swarm(identity: Keypair) -> Result<Swarm<Behaviour>, Error> {
355 let records = request_response::Behaviour::with_codec(
356 Codec,
357 [(
358 StreamProtocol::new(PROTOCOL),
359 request_response::ProtocolSupport::Full,
360 )],
361 request_response::Config::default()
362 .with_request_timeout(REQUEST_TIMEOUT)
363 .with_max_concurrent_streams(2),
364 );
365 let identify = identify::Behaviour::new(
366 identify::Config::new(IDENTIFY_PROTOCOL.into(), identity.public())
367 .with_agent_version(format!("urma-node/{}", env!("CARGO_PKG_VERSION"))),
368 );
369 let limits = Limits::default();
370 Ok(SwarmBuilder::with_existing_identity(identity)
371 .with_tokio()
372 .with_tcp(
373 tcp::Config::default().nodelay(true),
374 noise::Config::new,
375 || {
376 let mut config = yamux::Config::default();
377 config.set_max_num_streams(4);
379 config.set_max_buffer_size(256 * 1024);
380 config.set_receive_window_size(256 * 1024);
381 config
382 },
383 )
384 .map_err(|e| Error::Invalid(format!("peer transport: {e}")))?
385 .with_dns()
386 .map_err(|e| Error::Invalid(format!("peer DNS resolver: {e}")))?
387 .with_behaviour(|_| Behaviour {
388 limits,
389 records,
390 identify,
391 })
392 .map_err(|e| Error::Invalid(format!("peer behaviour: {e}")))?
393 .with_swarm_config(|config| {
394 config
395 .with_notify_handler_buffer_size(NonZeroUsize::new(2).unwrap())
396 .with_per_connection_event_buffer_size(2)
397 .with_max_negotiating_inbound_streams(2)
398 .with_dial_concurrency_factor(NonZeroU8::new(1).unwrap())
399 .with_idle_connection_timeout(Duration::from_secs(20))
400 })
401 .with_connection_timeout(Duration::from_secs(5))
402 .build())
403}
404
405struct Control {
406 commands: async_mpsc::Receiver<Command>,
407 stop: Arc<AtomicBool>,
408 connected: Arc<AtomicU64>,
409 startup: Startup,
410 shared: Arc<Shared>,
411 announce: Vec<Multiaddr>,
412}
413
414#[derive(Default)]
416struct Remotes(HashMap<ConnectionId, (Multiaddr, bool)>);
417
418impl Remotes {
419 fn loopback(&self, connection: ConnectionId) -> bool {
420 self.0
421 .get(&connection)
422 .is_some_and(|(address, _)| is_loopback(address))
423 }
424
425 fn outbound(&self, connection: ConnectionId) -> bool {
426 self.0
427 .get(&connection)
428 .is_some_and(|(_, outbound)| *outbound)
429 }
430}
431
432async fn run(
433 engine: Arc<Engine>,
434 identity: Keypair,
435 listen: Option<Multiaddr>,
436 control: Control,
437) -> Result<(), Error> {
438 let Control {
439 mut commands,
440 stop,
441 connected,
442 startup,
443 shared,
444 announce,
445 } = control;
446 let genesis = engine.status()?.chain_genesis;
447 let mut swarm = swarm(identity)?;
448 for address in announce {
449 swarm.add_external_address(address);
450 }
451 let mut ready = Some(startup);
452 let listening = listen.is_some();
453 if let Some(address) = listen {
454 swarm
455 .listen_on(address)
456 .map_err(|e| Error::Invalid(format!("peer listen: {e}")))?;
457 }
458 let (jobs, input) = mpsc::sync_channel::<ServeJob>(2);
459 let (served, mut responses) = async_mpsc::channel::<Served>(2);
460 let serving_stop = stop.clone();
461 let serving = std::thread::Builder::new()
462 .name("urma-peer-read".into())
463 .spawn(move || serve(&engine, genesis, &serving_stop, input, served))?;
464 if !listening {
465 let _ = ready.take().unwrap().send(Ok(None));
466 }
467 let mut pending = HashMap::<OutboundRequestId, Pending>::new();
468 let mut inbound = HashSet::new();
469 let mut remotes = Remotes::default();
470 let mut dials = Dials::default();
471 let mut rates = HashMap::<PeerId, Bucket>::new();
472 let mut requests = Bucket::new(16, 16);
473 let mut bytes = Bucket::new(64 * 1024 * 1024, 4 * 1024 * 1024);
474 let mut tick = tokio::time::interval(Duration::from_millis(50));
475 let started = Instant::now();
476 let result = loop {
477 if stop.load(Ordering::Acquire) {
478 break Ok(());
479 }
480 if ready.is_some() && started.elapsed() > Duration::from_secs(4) {
481 break Err(Error::Busy("peer listener startup deadline".into()));
482 }
483 tokio::select! {
484 _ = tick.tick() => {
485 for value in pending.values_mut() {
488 if Instant::now() >= value.deadline {
489 value.answer.fail(Error::Busy("peer acquisition deadline".into()));
490 let _ = swarm.disconnect_peer_id(value.peer);
491 }
492 }
493 dials.expire();
494 }
495 command = commands.recv() => {
496 let command = match command {
497 Some(Command::Fetch(fetch)) => fetch,
498 Some(Command::Disconnect(peer)) => {
500 if !pending.values().any(|p| p.peer == peer) { let _ = swarm.disconnect_peer_id(peer); }
501 continue;
502 }
503 Some(Command::Dial(dial)) => { dials.start(&mut swarm, dial); continue; }
504 None => break Ok(()),
505 };
506 if Instant::now() >= command.deadline || pending.len() >= MAX_PENDING
507 || pending.values().any(|p| p.peer == command.peer) {
508 command.answer.fail(Error::Busy("peer acquisition capacity or deadline".into()));
509 continue;
510 }
511 let request = Request { version: VERSION, chain_genesis: genesis, kind: command.kind };
512 let id = swarm.behaviour_mut().records.send_request_with_addresses(
513 &command.peer, request, command.addresses);
514 pending.insert(id, Pending { peer: command.peer, kind: command.kind, deadline: command.deadline, answer: command.answer });
515 }
516 response = responses.recv() => {
517 let Some(mut response) = response else { break Err(Error::Invalid("peer archive reader stopped".into())); };
518 if response.channel.is_open() {
519 if !bytes.take(u64::try_from(response.bytes.saturating_sub(256)).unwrap_or(u64::MAX)) { response.response.result = Reply::Busy; }
520 let _ = swarm.behaviour_mut().records.send_response(response.channel, response.response);
521 }
522 }
523 event = swarm.select_next_some() => match event {
524 SwarmEvent::NewListenAddr { mut address, .. } => {
525 address.push(Protocol::P2p(*swarm.local_peer_id()));
526 if let Some(ready) = ready.take() { let _ = ready.send(Ok(Some(address))); }
527 }
528 SwarmEvent::ListenerError { error, .. } if ready.is_some() => break Err(Error::Io(error)),
529 SwarmEvent::ConnectionEstablished { peer_id, connection_id, endpoint, num_established, .. } => {
530 remotes.0.insert(connection_id, (endpoint.get_remote_address().clone(), endpoint.is_dialer()));
531 dials.established(connection_id, peer_id);
532 if num_established.get() == 1 {
533 connected.fetch_add(1, Ordering::Relaxed);
534 shared.set_connected(peer_id, true);
535 }
536 }
537 SwarmEvent::ConnectionClosed { peer_id, connection_id, num_established, .. } => {
538 remotes.0.remove(&connection_id);
539 if num_established == 0 {
540 connected.fetch_sub(1, Ordering::Relaxed);
541 shared.set_connected(peer_id, false);
542 }
543 }
544 SwarmEvent::OutgoingConnectionError { connection_id, peer_id, error } => {
545 if let (Some(peer), DialError::Denied { cause }) = (peer_id, &error) {
547 if let Some(LimitDenied(limit)) = cause.downcast_ref::<LimitDenied>() { release_denied(&mut pending, peer, *limit); }
548 }
549 dials.failed(connection_id, &error);
550 }
551 SwarmEvent::Behaviour(BehaviourEvent::Identify(identify::Event::Received { connection_id, peer_id, info })) => {
552 if remotes.outbound(connection_id) {
554 if let Some(confirmed) = shared.observe(info.observed_addr, peer_id) { swarm.add_external_address(confirmed); }
555 }
556 shared.identified(peer_id, info.listen_addrs);
557 }
558 SwarmEvent::Behaviour(BehaviourEvent::Records(event)) => match event {
559 request_response::Event::Message { peer, connection_id, message: Message::Request { request_id, request, channel } } => {
560 let over_rate = !requests.take(1) || !peer_rate(&mut rates, peer);
564 if !bytes.take(256) {
565 continue;
566 }
567 let refusal = if over_rate { Some(Reply::Busy) }
568 else if request.version != VERSION { Some(Reply::UnsupportedVersion) }
569 else if request.chain_genesis != genesis { Some(Reply::WrongNetwork) }
570 else if let Kind::Peers { limit } = request.kind {
571 Some(shared.serve(peer, remotes.loopback(connection_id), limit))
573 }
574 else if inbound.len() >= 2 || inbound.iter().any(|(p, _)| *p == peer)
575 { Some(Reply::Busy) }
576 else { None };
577 if let Some(result) = refusal {
578 let _ = swarm.behaviour_mut().records.send_response(channel, Response { version: VERSION, chain_genesis: genesis, kind: request.kind, result });
579 } else {
580 match jobs.try_send(ServeJob { deadline: Instant::now() + REQUEST_TIMEOUT, request, channel }) {
581 Ok(()) => { inbound.insert((peer, request_id)); }
582 Err(_) => { }
583 }
584 }
585 }
586 request_response::Event::Message { peer, message: Message::Response { request_id, response }, .. } => {
587 if let Some(value) = pending.remove(&request_id) {
588 value.answer.deliver(genesis, peer, &value, response);
589 }
590 }
591 request_response::Event::OutboundFailure { request_id, error, .. } => {
592 if let Some(value) = pending.remove(&request_id) {
593 value.answer.fail(Error::Transport(error.to_string()));
594 }
595 }
596 request_response::Event::InboundFailure { peer, request_id, .. }
597 | request_response::Event::ResponseSent { peer, request_id, .. } => { inbound.remove(&(peer, request_id)); }
598 },
599 _ => (),
600 }
601 }
602 };
603 stop.store(true, Ordering::Release);
604 for (_, value) in pending {
605 value
606 .answer
607 .fail(Error::Busy("peer network stopped".into()));
608 }
609 dials.close();
610 drop(swarm);
613 drop(jobs);
614 drop(responses);
615 if serving.join().is_err() {
616 return Err(Error::Invalid("peer archive reader panicked".into()));
617 }
618 result
619}
620
621fn serve(
623 engine: &Engine,
624 genesis: BlockHash,
625 stop: &AtomicBool,
626 input: mpsc::Receiver<ServeJob>,
627 served: async_mpsc::Sender<Served>,
628) {
629 while let Ok(job) = input.recv() {
630 if stop.load(Ordering::Acquire) {
631 break;
632 }
633 if !job.channel.is_open() || Instant::now() >= job.deadline {
634 continue;
635 }
636 let result = lookup(engine, &job.request);
637 if stop.load(Ordering::Acquire) || !job.channel.is_open() || Instant::now() >= job.deadline
638 {
639 continue;
640 }
641 let mut response = Response {
642 version: VERSION,
643 chain_genesis: genesis,
644 kind: job.request.kind,
645 result,
646 };
647 let mut count = Count(0);
648 if serde_json::to_writer(&mut count, &response).is_err() {
649 response.result = Reply::TooLarge;
650 count.0 = 256;
651 }
652 let reply = Served {
653 channel: job.channel,
654 response,
655 bytes: count.0,
656 };
657 if served.blocking_send(reply).is_err() {
658 break;
659 }
660 }
661}
662
663fn release_denied(
666 pending: &mut HashMap<OutboundRequestId, Pending>,
667 peer: PeerId,
668 limit: DialLimit,
669) {
670 pending.retain(|_, value| {
671 if value.peer != peer {
672 return true;
673 }
674 value.answer.fail(Error::DialDenied(limit));
675 false
676 });
677}
678
679pub(crate) fn bind_response(
680 genesis: BlockHash,
681 peer: PeerId,
682 pending: &Pending,
683 response: &Response,
684) -> Result<(), Error> {
685 if Instant::now() >= pending.deadline {
686 return Err(Error::Busy("peer acquisition deadline".into()));
687 }
688 if peer != pending.peer
689 || response.version != VERSION
690 || response.chain_genesis != genesis
691 || response.kind != pending.kind
692 {
693 return Err(Error::Invalid(
694 "peer response identity, network or version mismatch".into(),
695 ));
696 }
697 Ok(())
698}
699
700fn validate_response(
701 genesis: BlockHash,
702 peer: PeerId,
703 pending: &Pending,
704 response: Response,
705) -> Result<Option<RecordPackage>, Error> {
706 bind_response(genesis, peer, pending, &response)?;
707 match response.result {
708 Reply::Record(package) if package.chain_genesis == genesis => Ok(Some(*package)),
709 Reply::Missing => Ok(None),
710 Reply::Busy => Err(Error::PeerBusy),
711 _ => Err(Error::Invalid(
712 "peer refused record or returned wrong network".into(),
713 )),
714 }
715}
716
717#[cfg(test)]
718mod tests {
719 use super::*;
720 use bitcoin::hashes::Hash;
721
722 #[test]
723 fn public_response_metadata_is_bound_but_is_not_record_verification() {
724 let genesis = BlockHash::all_zeros();
725 let txid = Txid::all_zeros();
726 let peer = Keypair::generate_ed25519().public().to_peer_id();
727 let other = Keypair::generate_ed25519().public().to_peer_id();
728 let (answer, _receiver) = mpsc::sync_channel(1);
729 let pending = Pending {
730 peer,
731 kind: Kind::Record { txid },
732 deadline: Instant::now() + CALL_TIMEOUT,
733 answer: Answer::Record(answer),
734 };
735 let response = Response {
736 version: VERSION,
737 chain_genesis: genesis,
738 kind: Kind::Record { txid },
739 result: Reply::Missing,
740 };
741 assert!(
742 validate_response(genesis, peer, &pending, response.clone())
743 .unwrap()
744 .is_none()
745 );
746 assert!(validate_response(genesis, other, &pending, response.clone()).is_err());
747 let mut bad = response.clone();
748 bad.version += 1;
749 assert!(validate_response(genesis, peer, &pending, bad).is_err());
750 let mut bad = response.clone();
751 bad.chain_genesis = BlockHash::from_byte_array([1; 32]);
752 assert!(validate_response(genesis, peer, &pending, bad).is_err());
753 let mut bad = response.clone();
754 bad.kind = Kind::Record {
755 txid: Txid::from_byte_array([1; 32]),
756 };
757 assert!(validate_response(genesis, peer, &pending, bad).is_err());
758 let expired = Pending {
759 deadline: Instant::now(),
760 ..pending
761 };
762 assert!(matches!(
763 validate_response(genesis, peer, &expired, response),
764 Err(Error::Busy(_))
765 ));
766 }
767
768 #[test]
769 fn listener_accepts_only_explicit_ip_tcp_without_implicit_resolution() {
770 for value in [
771 "/ip4/127.0.0.1/tcp/0",
772 "/ip6/::1/tcp/0",
773 "/ip4/0.0.0.0/tcp/28332",
774 ] {
775 validate_listener(&value.parse().unwrap()).unwrap();
776 }
777 for value in [
778 "/dns4/localhost/tcp/1",
779 "/ip4/127.0.0.1/udp/1",
780 "/ip4/127.0.0.1/tcp/1/ws",
781 ] {
782 assert!(validate_listener(&value.parse().unwrap()).is_err());
783 }
784 }
785}