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