Skip to main content

rns_net/
driver.rs

1//! Driver loop: receives events, drives the TransportEngine, dispatches actions.
2
3use std::collections::HashMap;
4
5use rns_core::packet::RawPacket;
6use rns_core::transport::types::{InterfaceId, TransportAction, TransportConfig};
7use rns_core::transport::TransportEngine;
8use rns_crypto::{OsRng, Rng};
9
10use crate::event::{
11    BlackholeInfo, Event, EventReceiver, InterfaceStatsResponse,
12    LocalDestinationEntry, NextHopResponse, PathTableEntry, QueryRequest, QueryResponse,
13    RateTableEntry, SingleInterfaceStat,
14};
15use crate::ifac;
16use crate::interface::{InterfaceEntry, InterfaceStats};
17use crate::link_manager::{LinkManager, LinkManagerAction};
18use crate::time;
19
20/// Infer the interface type string from a dynamic interface's name.
21/// Dynamic interfaces (TCP server clients, backbone peers, auto peers, local server clients)
22/// include their type in the name prefix set at construction.
23fn infer_interface_type(name: &str) -> String {
24    if name.starts_with("TCPServerInterface") {
25        "TCPServerClientInterface".to_string()
26    } else if name.starts_with("BackboneInterface") {
27        "BackboneInterface".to_string()
28    } else if name.starts_with("LocalInterface") {
29        "LocalServerClientInterface".to_string()
30    } else {
31        // AutoInterface peers use "{group_name}:{peer_addr}" format where
32        // group_name is the config section name (typically "AutoInterface" or similar).
33        "AutoInterface".to_string()
34    }
35}
36
37/// Callbacks for events the driver produces.
38///
39/// All identifiers use typed wrappers (`DestHash`, `IdentityHash`, `LinkId`, `PacketHash`)
40/// for compile-time safety.
41pub trait Callbacks: Send {
42    fn on_announce(
43        &mut self,
44        announced: crate::destination::AnnouncedIdentity,
45    );
46
47    fn on_path_updated(&mut self, dest_hash: rns_core::types::DestHash, hops: u8);
48
49    fn on_local_delivery(&mut self, dest_hash: rns_core::types::DestHash, raw: Vec<u8>, packet_hash: rns_core::types::PacketHash);
50
51    /// Called when an interface comes online.
52    fn on_interface_up(&mut self, _id: InterfaceId) {}
53
54    /// Called when an interface goes offline.
55    fn on_interface_down(&mut self, _id: InterfaceId) {}
56
57    /// Called when a link is fully established.
58    fn on_link_established(&mut self, _link_id: rns_core::types::LinkId, _rtt: f64, _is_initiator: bool) {}
59
60    /// Called when a link is closed.
61    fn on_link_closed(&mut self, _link_id: rns_core::types::LinkId, _reason: Option<rns_core::link::TeardownReason>) {}
62
63    /// Called when a remote peer identifies on a link.
64    fn on_remote_identified(&mut self, _link_id: rns_core::types::LinkId, _identity_hash: rns_core::types::IdentityHash, _public_key: [u8; 64]) {}
65
66    /// Called when a resource transfer delivers data.
67    fn on_resource_received(&mut self, _link_id: rns_core::types::LinkId, _data: Vec<u8>, _metadata: Option<Vec<u8>>) {}
68
69    /// Called when a resource transfer completes (sender-side proof validated).
70    fn on_resource_completed(&mut self, _link_id: rns_core::types::LinkId) {}
71
72    /// Called when a resource transfer fails.
73    fn on_resource_failed(&mut self, _link_id: rns_core::types::LinkId, _error: String) {}
74
75    /// Called with resource transfer progress updates.
76    fn on_resource_progress(&mut self, _link_id: rns_core::types::LinkId, _received: usize, _total: usize) {}
77
78    /// Called to ask whether to accept an incoming resource (for AcceptApp strategy).
79    /// Return true to accept, false to reject.
80    fn on_resource_accept_query(&mut self, _link_id: rns_core::types::LinkId, _resource_hash: Vec<u8>, _transfer_size: u64, _has_metadata: bool) -> bool {
81        false
82    }
83
84    /// Called when a channel message is received on a link.
85    fn on_channel_message(&mut self, _link_id: rns_core::types::LinkId, _msgtype: u16, _payload: Vec<u8>) {}
86
87    /// Called when generic link data is received.
88    fn on_link_data(&mut self, _link_id: rns_core::types::LinkId, _context: u8, _data: Vec<u8>) {}
89
90    /// Called when a response is received on a link.
91    fn on_response(&mut self, _link_id: rns_core::types::LinkId, _request_id: [u8; 16], _data: Vec<u8>) {}
92
93    /// Called when a delivery proof is received for a packet we sent.
94    /// `rtt` is the round-trip time in seconds.
95    fn on_proof(&mut self, _dest_hash: rns_core::types::DestHash, _packet_hash: rns_core::types::PacketHash, _rtt: f64) {}
96
97    /// Called for ProveApp strategy: should we prove this incoming packet?
98    /// Return true to generate and send a proof, false to skip.
99    fn on_proof_requested(&mut self, _dest_hash: rns_core::types::DestHash, _packet_hash: rns_core::types::PacketHash) -> bool {
100        true
101    }
102}
103
104/// The driver loop. Owns the engine and all interface entries.
105pub struct Driver {
106    pub(crate) engine: TransportEngine,
107    pub(crate) interfaces: HashMap<InterfaceId, InterfaceEntry>,
108    pub(crate) rng: OsRng,
109    pub(crate) rx: EventReceiver,
110    pub(crate) callbacks: Box<dyn Callbacks>,
111    pub(crate) started: f64,
112    pub(crate) announce_cache: Option<crate::announce_cache::AnnounceCache>,
113    /// Destination hash for rnstransport.tunnel.synthesize (PLAIN).
114    pub(crate) tunnel_synth_dest: [u8; 16],
115    /// Transport identity (optional, needed for tunnel synthesis).
116    pub(crate) transport_identity: Option<rns_crypto::identity::Identity>,
117    /// Link manager: handles link lifecycle, request/response.
118    pub(crate) link_manager: LinkManager,
119    /// Management configuration for ACL checks.
120    pub(crate) management_config: crate::management::ManagementConfig,
121    /// Last time management announces were emitted.
122    pub(crate) last_management_announce: f64,
123    /// Whether initial management announce has been sent (delayed 5s after start).
124    pub(crate) initial_announce_sent: bool,
125    /// Cache of known announced identities, keyed by destination hash.
126    pub(crate) known_destinations: HashMap<[u8; 16], crate::destination::AnnouncedIdentity>,
127    /// Destination hash for rnstransport.path.request (PLAIN).
128    pub(crate) path_request_dest: [u8; 16],
129    /// Proof strategies per destination hash.
130    /// Maps dest_hash → (strategy, optional signing identity for generating proofs).
131    pub(crate) proof_strategies: HashMap<[u8; 16], (rns_core::types::ProofStrategy, Option<rns_crypto::identity::Identity>)>,
132    /// Tracked sent packets for proof matching: packet_hash → (dest_hash, sent_time).
133    pub(crate) sent_packets: HashMap<[u8; 32], ([u8; 16], f64)>,
134    /// Locally registered destinations: hash → dest_type.
135    pub(crate) local_destinations: HashMap<[u8; 16], u8>,
136}
137
138impl Driver {
139    /// Create a new driver.
140    pub fn new(
141        config: TransportConfig,
142        rx: EventReceiver,
143        callbacks: Box<dyn Callbacks>,
144    ) -> Self {
145        let tunnel_synth_dest = rns_core::destination::destination_hash(
146            "rnstransport",
147            &["tunnel", "synthesize"],
148            None,
149        );
150        let path_request_dest = rns_core::destination::destination_hash(
151            "rnstransport",
152            &["path", "request"],
153            None,
154        );
155        let mut engine = TransportEngine::new(config);
156        engine.register_destination(tunnel_synth_dest, rns_core::constants::DESTINATION_PLAIN);
157        // Register path request destination so inbound path requests are delivered locally
158        engine.register_destination(path_request_dest, rns_core::constants::DESTINATION_PLAIN);
159        let mut local_destinations = HashMap::new();
160        local_destinations.insert(tunnel_synth_dest, rns_core::constants::DESTINATION_PLAIN);
161        local_destinations.insert(path_request_dest, rns_core::constants::DESTINATION_PLAIN);
162        Driver {
163            engine,
164            interfaces: HashMap::new(),
165            rng: OsRng,
166            rx,
167            callbacks,
168            started: time::now(),
169            announce_cache: None,
170            tunnel_synth_dest,
171            transport_identity: None,
172            link_manager: LinkManager::new(),
173            management_config: Default::default(),
174            last_management_announce: 0.0,
175            initial_announce_sent: false,
176            known_destinations: HashMap::new(),
177            path_request_dest,
178            proof_strategies: HashMap::new(),
179            sent_packets: HashMap::new(),
180            local_destinations,
181        }
182    }
183
184    /// Run the event loop. Blocks until Shutdown or all senders are dropped.
185    pub fn run(&mut self) {
186        loop {
187            let event = match self.rx.recv() {
188                Ok(e) => e,
189                Err(_) => break, // all senders dropped
190            };
191
192            match event {
193                Event::Frame { interface_id, data } => {
194                    // Update rx stats
195                    if let Some(entry) = self.interfaces.get_mut(&interface_id) {
196                        entry.stats.rxb += data.len() as u64;
197                        entry.stats.rx_packets += 1;
198                    }
199
200                    // IFAC inbound processing
201                    let packet = if let Some(entry) = self.interfaces.get(&interface_id) {
202                        if let Some(ref ifac_state) = entry.ifac {
203                            // Interface has IFAC enabled — unmask
204                            match ifac::unmask_inbound(&data, ifac_state) {
205                                Some(unmasked) => unmasked,
206                                None => {
207                                    log::debug!("[{}] IFAC rejected packet", interface_id.0);
208                                    continue;
209                                }
210                            }
211                        } else {
212                            // No IFAC — drop if IFAC flag is set
213                            if data.len() > 2 && data[0] & 0x80 == 0x80 {
214                                log::debug!("[{}] dropping packet with IFAC flag on non-IFAC interface", interface_id.0);
215                                continue;
216                            }
217                            data
218                        }
219                    } else {
220                        data
221                    };
222
223                    let actions = self.engine.handle_inbound(
224                        &packet,
225                        interface_id,
226                        time::now(),
227                        &mut self.rng,
228                    );
229                    self.dispatch_all(actions);
230                }
231                Event::Tick => {
232                    let now = time::now();
233                    let actions = self.engine.tick(now, &mut self.rng);
234                    self.dispatch_all(actions);
235                    // Tick link manager (keepalive, stale, timeout)
236                    let link_actions = self.link_manager.tick(&mut self.rng);
237                    self.dispatch_link_actions(link_actions);
238                    // Emit management announces
239                    self.tick_management_announces(now);
240                    // Cull expired sent packet tracking entries (no proof received within 60s)
241                    self.sent_packets.retain(|_, (_, sent_time)| now - *sent_time < 60.0);
242                }
243                Event::InterfaceUp(id, new_writer, info) => {
244                    let wants_tunnel;
245                    if let Some(info) = info {
246                        // New dynamic interface (e.g., TCP server client connection)
247                        log::info!("[{}] dynamic interface registered", id.0);
248                        wants_tunnel = info.wants_tunnel;
249                        let iface_type = infer_interface_type(&info.name);
250                        self.engine.register_interface(info.clone());
251                        if let Some(writer) = new_writer {
252                            self.interfaces.insert(
253                                id,
254                                InterfaceEntry {
255                                    id,
256                                    info,
257                                    writer,
258                                    online: true,
259                                    dynamic: true,
260                                    ifac: None,
261                                    stats: InterfaceStats {
262                                        started: time::now(),
263                                        ..Default::default()
264                                    },
265                                    interface_type: iface_type,
266                                },
267                            );
268                        }
269                        self.callbacks.on_interface_up(id);
270                    } else if let Some(entry) = self.interfaces.get_mut(&id) {
271                        // Existing interface reconnected
272                        log::info!("[{}] interface online", id.0);
273                        wants_tunnel = entry.info.wants_tunnel;
274                        entry.online = true;
275                        if let Some(writer) = new_writer {
276                            log::info!("[{}] writer refreshed after reconnect", id.0);
277                            entry.writer = writer;
278                        }
279                        self.callbacks.on_interface_up(id);
280                    } else {
281                        wants_tunnel = false;
282                    }
283
284                    // Trigger tunnel synthesis if the interface wants it
285                    if wants_tunnel {
286                        self.synthesize_tunnel_for_interface(id);
287                    }
288                }
289                Event::InterfaceDown(id) => {
290                    // Void tunnel if interface had one
291                    if let Some(entry) = self.interfaces.get(&id) {
292                        if let Some(tunnel_id) = entry.info.tunnel_id {
293                            self.engine.void_tunnel_interface(&tunnel_id);
294                        }
295                    }
296
297                    if let Some(entry) = self.interfaces.get(&id) {
298                        if entry.dynamic {
299                            // Dynamic interfaces are removed entirely
300                            log::info!("[{}] dynamic interface removed", id.0);
301                            self.engine.deregister_interface(id);
302                            self.interfaces.remove(&id);
303                        } else {
304                            // Static interfaces are just marked offline
305                            log::info!("[{}] interface offline", id.0);
306                            self.interfaces.get_mut(&id).unwrap().online = false;
307                        }
308                        self.callbacks.on_interface_down(id);
309                    }
310                }
311                Event::SendOutbound { raw, dest_type, attached_interface } => {
312                    match RawPacket::unpack(&raw) {
313                        Ok(packet) => {
314                            // Track sent DATA packets for proof matching
315                            if packet.flags.packet_type == rns_core::constants::PACKET_TYPE_DATA {
316                                self.sent_packets.insert(
317                                    packet.packet_hash,
318                                    (packet.destination_hash, time::now()),
319                                );
320                            }
321                            let actions = self.engine.handle_outbound(
322                                &packet,
323                                dest_type,
324                                attached_interface,
325                                time::now(),
326                            );
327                            self.dispatch_all(actions);
328                        }
329                        Err(e) => {
330                            log::warn!("SendOutbound: failed to unpack packet: {:?}", e);
331                        }
332                    }
333                }
334                Event::RegisterDestination { dest_hash, dest_type } => {
335                    self.engine.register_destination(dest_hash, dest_type);
336                    self.local_destinations.insert(dest_hash, dest_type);
337                }
338                Event::DeregisterDestination { dest_hash } => {
339                    self.engine.deregister_destination(&dest_hash);
340                    self.local_destinations.remove(&dest_hash);
341                }
342                Event::Query(request, response_tx) => {
343                    let response = self.handle_query_mut(request);
344                    let _ = response_tx.send(response);
345                }
346                Event::RegisterLinkDestination { dest_hash, sig_prv_bytes, sig_pub_bytes } => {
347                    let sig_prv = rns_crypto::ed25519::Ed25519PrivateKey::from_bytes(&sig_prv_bytes);
348                    self.link_manager.register_link_destination(dest_hash, sig_prv, sig_pub_bytes);
349                    // Also register in transport engine so inbound packets are delivered locally
350                    self.engine.register_destination(dest_hash, rns_core::constants::DESTINATION_SINGLE);
351                    self.local_destinations.insert(dest_hash, rns_core::constants::DESTINATION_SINGLE);
352                }
353                Event::RegisterRequestHandler { path, allowed_list, handler } => {
354                    self.link_manager.register_request_handler(&path, allowed_list, move |link_id, p, data, remote| {
355                        handler(link_id, p, data, remote)
356                    });
357                }
358                Event::CreateLink { dest_hash, dest_sig_pub_bytes, response_tx } => {
359                    let hops = self.engine.hops_to(&dest_hash).unwrap_or(0);
360                    let (link_id, link_actions) = self.link_manager.create_link(
361                        &dest_hash, &dest_sig_pub_bytes, hops, &mut self.rng,
362                    );
363                    let _ = response_tx.send(link_id);
364                    self.dispatch_link_actions(link_actions);
365                }
366                Event::SendRequest { link_id, path, data } => {
367                    let link_actions = self.link_manager.send_request(
368                        &link_id, &path, &data, &mut self.rng,
369                    );
370                    self.dispatch_link_actions(link_actions);
371                }
372                Event::IdentifyOnLink { link_id, identity_prv_key } => {
373                    let identity = rns_crypto::identity::Identity::from_private_key(&identity_prv_key);
374                    let link_actions = self.link_manager.identify(&link_id, &identity, &mut self.rng);
375                    self.dispatch_link_actions(link_actions);
376                }
377                Event::TeardownLink { link_id } => {
378                    let link_actions = self.link_manager.teardown_link(&link_id);
379                    self.dispatch_link_actions(link_actions);
380                }
381                Event::SendResource { link_id, data, metadata } => {
382                    let link_actions = self.link_manager.send_resource(
383                        &link_id, &data, metadata.as_deref(), &mut self.rng,
384                    );
385                    self.dispatch_link_actions(link_actions);
386                }
387                Event::SetResourceStrategy { link_id, strategy } => {
388                    use crate::link_manager::ResourceStrategy;
389                    let strat = match strategy {
390                        0 => ResourceStrategy::AcceptNone,
391                        1 => ResourceStrategy::AcceptAll,
392                        2 => ResourceStrategy::AcceptApp,
393                        _ => ResourceStrategy::AcceptNone,
394                    };
395                    self.link_manager.set_resource_strategy(&link_id, strat);
396                }
397                Event::AcceptResource { link_id, resource_hash, accept } => {
398                    let link_actions = self.link_manager.accept_resource(
399                        &link_id, &resource_hash, accept, &mut self.rng,
400                    );
401                    self.dispatch_link_actions(link_actions);
402                }
403                Event::SendChannelMessage { link_id, msgtype, payload } => {
404                    let link_actions = self.link_manager.send_channel_message(
405                        &link_id, msgtype, &payload, &mut self.rng,
406                    );
407                    self.dispatch_link_actions(link_actions);
408                }
409                Event::SendOnLink { link_id, data, context } => {
410                    let link_actions = self.link_manager.send_on_link(
411                        &link_id, &data, context, &mut self.rng,
412                    );
413                    self.dispatch_link_actions(link_actions);
414                }
415                Event::RequestPath { dest_hash } => {
416                    self.handle_request_path(dest_hash);
417                }
418                Event::RegisterProofStrategy { dest_hash, strategy, signing_key } => {
419                    let identity = signing_key.map(|key| {
420                        rns_crypto::identity::Identity::from_private_key(&key)
421                    });
422                    self.proof_strategies.insert(dest_hash, (strategy, identity));
423                }
424                Event::Shutdown => break,
425            }
426        }
427    }
428
429    /// Handle a query request and produce a response.
430    fn handle_query(&self, request: QueryRequest) -> QueryResponse {
431        match request {
432            QueryRequest::InterfaceStats => {
433                let mut interfaces = Vec::new();
434                let mut total_rxb: u64 = 0;
435                let mut total_txb: u64 = 0;
436                for entry in self.interfaces.values() {
437                    total_rxb += entry.stats.rxb;
438                    total_txb += entry.stats.txb;
439                    interfaces.push(SingleInterfaceStat {
440                        name: entry.info.name.clone(),
441                        status: entry.online,
442                        mode: entry.info.mode,
443                        rxb: entry.stats.rxb,
444                        txb: entry.stats.txb,
445                        rx_packets: entry.stats.rx_packets,
446                        tx_packets: entry.stats.tx_packets,
447                        bitrate: entry.info.bitrate,
448                        ifac_size: entry.ifac.as_ref().map(|s| s.size),
449                        started: entry.stats.started,
450                        ia_freq: entry.stats.incoming_announce_freq(),
451                        oa_freq: entry.stats.outgoing_announce_freq(),
452                        interface_type: entry.interface_type.clone(),
453                    });
454                }
455                // Sort by name for consistent output
456                interfaces.sort_by(|a, b| a.name.cmp(&b.name));
457                QueryResponse::InterfaceStats(InterfaceStatsResponse {
458                    interfaces,
459                    transport_id: self.engine.identity_hash().copied(),
460                    transport_enabled: self.engine.transport_enabled(),
461                    transport_uptime: time::now() - self.started,
462                    total_rxb,
463                    total_txb,
464                })
465            }
466            QueryRequest::PathTable { max_hops } => {
467                let entries: Vec<PathTableEntry> = self
468                    .engine
469                    .path_table_entries()
470                    .filter(|(_, entry)| {
471                        max_hops.map_or(true, |max| entry.hops <= max)
472                    })
473                    .map(|(hash, entry)| {
474                        let iface_name = self.interfaces.get(&entry.receiving_interface)
475                            .map(|e| e.info.name.clone())
476                            .or_else(|| self.engine.interface_info(&entry.receiving_interface)
477                                .map(|i| i.name.clone()))
478                            .unwrap_or_default();
479                        PathTableEntry {
480                            hash: *hash,
481                            timestamp: entry.timestamp,
482                            via: entry.next_hop,
483                            hops: entry.hops,
484                            expires: entry.expires,
485                            interface: entry.receiving_interface,
486                            interface_name: iface_name,
487                        }
488                    })
489                    .collect();
490                QueryResponse::PathTable(entries)
491            }
492            QueryRequest::RateTable => {
493                let entries: Vec<RateTableEntry> = self
494                    .engine
495                    .rate_limiter()
496                    .entries()
497                    .map(|(hash, entry)| RateTableEntry {
498                        hash: *hash,
499                        last: entry.last,
500                        rate_violations: entry.rate_violations,
501                        blocked_until: entry.blocked_until,
502                        timestamps: entry.timestamps.clone(),
503                    })
504                    .collect();
505                QueryResponse::RateTable(entries)
506            }
507            QueryRequest::NextHop { dest_hash } => {
508                let resp = self.engine.next_hop(&dest_hash).map(|next_hop| {
509                    NextHopResponse {
510                        next_hop,
511                        hops: self.engine.hops_to(&dest_hash).unwrap_or(0),
512                        interface: self.engine.next_hop_interface(&dest_hash).unwrap_or(InterfaceId(0)),
513                    }
514                });
515                QueryResponse::NextHop(resp)
516            }
517            QueryRequest::NextHopIfName { dest_hash } => {
518                let name = self
519                    .engine
520                    .next_hop_interface(&dest_hash)
521                    .and_then(|id| self.interfaces.get(&id))
522                    .map(|entry| entry.info.name.clone());
523                QueryResponse::NextHopIfName(name)
524            }
525            QueryRequest::LinkCount => {
526                QueryResponse::LinkCount(self.engine.link_table_count() + self.link_manager.link_count())
527            }
528            QueryRequest::DropPath { .. } => {
529                // Mutating queries are handled by handle_query_mut
530                QueryResponse::DropPath(false)
531            }
532            QueryRequest::DropAllVia { .. } => {
533                QueryResponse::DropAllVia(0)
534            }
535            QueryRequest::DropAnnounceQueues => {
536                QueryResponse::DropAnnounceQueues
537            }
538            QueryRequest::TransportIdentity => {
539                QueryResponse::TransportIdentity(self.engine.identity_hash().copied())
540            }
541            QueryRequest::GetBlackholed => {
542                let now = time::now();
543                let entries: Vec<BlackholeInfo> = self.engine.blackholed_entries()
544                    .filter(|(_, e)| e.expires == 0.0 || e.expires > now)
545                    .map(|(hash, entry)| BlackholeInfo {
546                        identity_hash: *hash,
547                        created: entry.created,
548                        expires: entry.expires,
549                        reason: entry.reason.clone(),
550                    })
551                    .collect();
552                QueryResponse::Blackholed(entries)
553            }
554            QueryRequest::BlackholeIdentity { .. }
555            | QueryRequest::UnblackholeIdentity { .. } => {
556                // Mutating queries handled by handle_query_mut
557                QueryResponse::BlackholeResult(false)
558            }
559            QueryRequest::HasPath { dest_hash } => {
560                QueryResponse::HasPath(self.engine.has_path(&dest_hash))
561            }
562            QueryRequest::HopsTo { dest_hash } => {
563                QueryResponse::HopsTo(self.engine.hops_to(&dest_hash))
564            }
565            QueryRequest::RecallIdentity { dest_hash } => {
566                QueryResponse::RecallIdentity(self.known_destinations.get(&dest_hash).cloned())
567            }
568            QueryRequest::LocalDestinations => {
569                let entries: Vec<LocalDestinationEntry> = self
570                    .local_destinations
571                    .iter()
572                    .map(|(hash, dest_type)| LocalDestinationEntry {
573                        hash: *hash,
574                        dest_type: *dest_type,
575                    })
576                    .collect();
577                QueryResponse::LocalDestinations(entries)
578            }
579            QueryRequest::Links => {
580                QueryResponse::Links(self.link_manager.link_entries())
581            }
582            QueryRequest::Resources => {
583                QueryResponse::Resources(self.link_manager.resource_entries())
584            }
585        }
586    }
587
588    /// Handle a mutating query request.
589    fn handle_query_mut(&mut self, request: QueryRequest) -> QueryResponse {
590        match request {
591            QueryRequest::BlackholeIdentity { identity_hash, duration_hours, reason } => {
592                let now = time::now();
593                self.engine.blackhole_identity(identity_hash, now, duration_hours, reason);
594                QueryResponse::BlackholeResult(true)
595            }
596            QueryRequest::UnblackholeIdentity { identity_hash } => {
597                let result = self.engine.unblackhole_identity(&identity_hash);
598                QueryResponse::UnblackholeResult(result)
599            }
600            QueryRequest::DropPath { dest_hash } => {
601                QueryResponse::DropPath(self.engine.drop_path(&dest_hash))
602            }
603            QueryRequest::DropAllVia { transport_hash } => {
604                QueryResponse::DropAllVia(self.engine.drop_all_via(&transport_hash))
605            }
606            QueryRequest::DropAnnounceQueues => {
607                self.engine.drop_announce_queues();
608                QueryResponse::DropAnnounceQueues
609            }
610            other => self.handle_query(other),
611        }
612    }
613
614    /// Handle a tunnel synthesis packet delivered locally.
615    fn handle_tunnel_synth_delivery(&mut self, raw: &[u8]) {
616        // Extract the data payload from the raw packet
617        let packet = match RawPacket::unpack(raw) {
618            Ok(p) => p,
619            Err(_) => return,
620        };
621
622        match rns_core::transport::tunnel::validate_tunnel_synthesize_data(&packet.data) {
623            Ok(validated) => {
624                // Find the interface this tunnel belongs to by computing the expected
625                // tunnel_id for each interface with wants_tunnel
626                let iface_id = self
627                    .interfaces
628                    .iter()
629                    .find(|(_, entry)| entry.info.wants_tunnel && entry.online)
630                    .map(|(id, _)| *id);
631
632                if let Some(iface) = iface_id {
633                    let now = time::now();
634                    let tunnel_actions =
635                        self.engine.handle_tunnel(validated.tunnel_id, iface, now);
636                    self.dispatch_all(tunnel_actions);
637                }
638            }
639            Err(e) => {
640                log::debug!("Tunnel synthesis validation failed: {}", e);
641            }
642        }
643    }
644
645    /// Synthesize a tunnel on an interface that wants it.
646    ///
647    /// Called when an interface with `wants_tunnel` comes up.
648    fn synthesize_tunnel_for_interface(&mut self, interface: InterfaceId) {
649        if let Some(ref identity) = self.transport_identity {
650            let actions = self.engine.synthesize_tunnel(identity, interface, &mut self.rng);
651            self.dispatch_all(actions);
652        }
653    }
654
655    /// Build and send a path request packet for a destination.
656    fn handle_request_path(&mut self, dest_hash: [u8; 16]) {
657        // Build path request data: dest_hash(16) || [transport_id(16)] || random_tag(16)
658        let mut data = Vec::with_capacity(48);
659        data.extend_from_slice(&dest_hash);
660
661        if self.engine.transport_enabled() {
662            if let Some(id_hash) = self.engine.identity_hash() {
663                data.extend_from_slice(id_hash);
664            }
665        }
666
667        // Random tag (16 bytes)
668        let mut tag = [0u8; 16];
669        self.rng.fill_bytes(&mut tag);
670        data.extend_from_slice(&tag);
671
672        // Build as BROADCAST DATA PLAIN packet to rnstransport.path.request
673        let flags = rns_core::packet::PacketFlags {
674            header_type: rns_core::constants::HEADER_1,
675            context_flag: rns_core::constants::FLAG_UNSET,
676            transport_type: rns_core::constants::TRANSPORT_BROADCAST,
677            destination_type: rns_core::constants::DESTINATION_PLAIN,
678            packet_type: rns_core::constants::PACKET_TYPE_DATA,
679        };
680
681        if let Ok(packet) = RawPacket::pack(
682            flags, 0, &self.path_request_dest, None,
683            rns_core::constants::CONTEXT_NONE, &data,
684        ) {
685            let actions = self.engine.handle_outbound(
686                &packet,
687                rns_core::constants::DESTINATION_PLAIN,
688                None,
689                time::now(),
690            );
691            self.dispatch_all(actions);
692        }
693    }
694
695    /// Check if we should generate a proof for a delivered packet,
696    /// and if so, sign and send it.
697    fn maybe_generate_proof(&mut self, dest_hash: [u8; 16], packet_hash: &[u8; 32]) {
698        use rns_core::types::ProofStrategy;
699
700        let (strategy, identity) = match self.proof_strategies.get(&dest_hash) {
701            Some((s, id)) => (*s, id.as_ref()),
702            None => return,
703        };
704
705        let should_prove = match strategy {
706            ProofStrategy::ProveAll => true,
707            ProofStrategy::ProveApp => {
708                self.callbacks.on_proof_requested(
709                    rns_core::types::DestHash(dest_hash),
710                    rns_core::types::PacketHash(*packet_hash),
711                )
712            }
713            ProofStrategy::ProveNone => false,
714        };
715
716        if !should_prove {
717            return;
718        }
719
720        let identity = match identity {
721            Some(id) => id,
722            None => {
723                log::warn!("Cannot generate proof for {:02x?}: no signing key", &dest_hash[..4]);
724                return;
725            }
726        };
727
728        // Sign the packet hash to create the proof
729        let signature = match identity.sign(packet_hash) {
730            Ok(sig) => sig,
731            Err(e) => {
732                log::warn!("Failed to sign proof for {:02x?}: {:?}", &dest_hash[..4], e);
733                return;
734            }
735        };
736
737        // Build explicit proof: [packet_hash:32][signature:64]
738        let mut proof_data = Vec::with_capacity(96);
739        proof_data.extend_from_slice(packet_hash);
740        proof_data.extend_from_slice(&signature);
741
742        // Address the proof to the truncated packet hash (first 16 bytes),
743        // matching Python's ProofDestination (Packet.py:390-394).
744        // Transport nodes create reverse_table entries keyed by truncated
745        // packet hash when forwarding data, so this allows proofs to be
746        // routed back to the sender via the reverse path.
747        let mut proof_dest = [0u8; 16];
748        proof_dest.copy_from_slice(&packet_hash[..16]);
749
750        let flags = rns_core::packet::PacketFlags {
751            header_type: rns_core::constants::HEADER_1,
752            context_flag: rns_core::constants::FLAG_UNSET,
753            transport_type: rns_core::constants::TRANSPORT_BROADCAST,
754            destination_type: rns_core::constants::DESTINATION_SINGLE,
755            packet_type: rns_core::constants::PACKET_TYPE_PROOF,
756        };
757
758        if let Ok(packet) = RawPacket::pack(
759            flags, 0, &proof_dest, None,
760            rns_core::constants::CONTEXT_NONE, &proof_data,
761        ) {
762            let actions = self.engine.handle_outbound(
763                &packet,
764                rns_core::constants::DESTINATION_SINGLE,
765                None,
766                time::now(),
767            );
768            self.dispatch_all(actions);
769            log::debug!("Generated proof for packet on dest {:02x?}", &dest_hash[..4]);
770        }
771    }
772
773    /// Handle an inbound proof packet: validate and fire on_proof callback.
774    fn handle_inbound_proof(&mut self, dest_hash: [u8; 16], proof_data: &[u8], _raw_packet_hash: &[u8; 32]) {
775        // Explicit proof format: [packet_hash:32][signature:64] = 96 bytes
776        if proof_data.len() < 96 {
777            log::debug!("Proof too short for explicit proof: {} bytes", proof_data.len());
778            return;
779        }
780
781        let mut tracked_hash = [0u8; 32];
782        tracked_hash.copy_from_slice(&proof_data[..32]);
783
784        let signature = &proof_data[32..96];
785
786        // Look up the tracked sent packet
787        if let Some((tracked_dest, sent_time)) = self.sent_packets.remove(&tracked_hash) {
788            // Validate the proof signature using the destination's public key
789            // (matches Python's PacketReceipt.validate_proof behavior)
790            if let Some(announced) = self.known_destinations.get(&tracked_dest) {
791                let identity = rns_crypto::identity::Identity::from_public_key(&announced.public_key);
792                let mut sig = [0u8; 64];
793                sig.copy_from_slice(signature);
794                if !identity.verify(&sig, &tracked_hash) {
795                    log::debug!(
796                        "Proof signature invalid for {:02x?}",
797                        &tracked_hash[..4],
798                    );
799                    return;
800                }
801            } else {
802                log::debug!(
803                    "No known identity for dest {:02x?}, accepting proof without signature check",
804                    &tracked_dest[..4],
805                );
806            }
807
808            let rtt = time::now() - sent_time;
809            log::debug!(
810                "Proof received for {:02x?} rtt={:.3}s",
811                &tracked_hash[..4], rtt,
812            );
813            self.callbacks.on_proof(
814                rns_core::types::DestHash(tracked_dest),
815                rns_core::types::PacketHash(tracked_hash),
816                rtt,
817            );
818        } else {
819            log::debug!(
820                "Proof for unknown packet {:02x?} on dest {:02x?}",
821                &tracked_hash[..4], &dest_hash[..4],
822            );
823        }
824    }
825
826    /// Dispatch a list of transport actions.
827    fn dispatch_all(&mut self, actions: Vec<TransportAction>) {
828        for action in actions {
829            match action {
830                TransportAction::SendOnInterface { interface, raw } => {
831                    let is_announce = raw.len() > 2 && (raw[0] & 0x03) == 0x01;
832                    if let Some(entry) = self.interfaces.get_mut(&interface) {
833                        if entry.online {
834                            let data = if let Some(ref ifac_state) = entry.ifac {
835                                ifac::mask_outbound(&raw, ifac_state)
836                            } else {
837                                raw
838                            };
839                            // Update tx stats
840                            entry.stats.txb += data.len() as u64;
841                            entry.stats.tx_packets += 1;
842                            if is_announce {
843                                entry.stats.record_outgoing_announce(time::now());
844                            }
845                            if let Err(e) = entry.writer.send_frame(&data) {
846                                log::warn!("[{}] send failed: {}", entry.info.id.0, e);
847                            }
848                        }
849                    }
850                }
851                TransportAction::BroadcastOnAllInterfaces { raw, exclude } => {
852                    let is_announce = raw.len() > 2 && (raw[0] & 0x03) == 0x01;
853                    for entry in self.interfaces.values_mut() {
854                        if entry.online && Some(entry.id) != exclude {
855                            let data = if let Some(ref ifac_state) = entry.ifac {
856                                ifac::mask_outbound(&raw, ifac_state)
857                            } else {
858                                raw.clone()
859                            };
860                            // Update tx stats
861                            entry.stats.txb += data.len() as u64;
862                            entry.stats.tx_packets += 1;
863                            if is_announce {
864                                entry.stats.record_outgoing_announce(time::now());
865                            }
866                            if let Err(e) = entry.writer.send_frame(&data) {
867                                log::warn!("[{}] broadcast failed: {}", entry.info.id.0, e);
868                            }
869                        }
870                    }
871                }
872                TransportAction::DeliverLocal {
873                    destination_hash,
874                    raw,
875                    packet_hash,
876                } => {
877                    if destination_hash == self.tunnel_synth_dest {
878                        // Tunnel synthesis packet — validate and handle
879                        self.handle_tunnel_synth_delivery(&raw);
880                    } else if destination_hash == self.path_request_dest {
881                        // Path request packet — extract data and handle
882                        if let Ok(packet) = RawPacket::unpack(&raw) {
883                            let actions = self.engine.handle_path_request(
884                                &packet.data,
885                                InterfaceId(0), // no specific interface
886                                time::now(),
887                            );
888                            self.dispatch_all(actions);
889                        }
890                    } else if self.link_manager.is_link_destination(&destination_hash) {
891                        // Link-related packet — route to link manager
892                        let link_actions = self.link_manager.handle_local_delivery(
893                            destination_hash, &raw, packet_hash, &mut self.rng,
894                        );
895                        self.dispatch_link_actions(link_actions);
896                    } else {
897                        // Check if this is a PROOF packet for a packet we sent
898                        if let Ok(packet) = RawPacket::unpack(&raw) {
899                            if packet.flags.packet_type == rns_core::constants::PACKET_TYPE_PROOF {
900                                self.handle_inbound_proof(destination_hash, &packet.data, &packet_hash);
901                                continue;
902                            }
903                        }
904
905                        // Check if destination has a proof strategy — generate proof if needed
906                        self.maybe_generate_proof(destination_hash, &packet_hash);
907
908                        self.callbacks
909                            .on_local_delivery(
910                                rns_core::types::DestHash(destination_hash),
911                                raw,
912                                rns_core::types::PacketHash(packet_hash),
913                            );
914                    }
915                }
916                TransportAction::AnnounceReceived {
917                    destination_hash,
918                    identity_hash,
919                    public_key,
920                    app_data,
921                    hops,
922                    receiving_interface,
923                    ..
924                } => {
925                    if let Some(entry) = self.interfaces.get_mut(&receiving_interface) {
926                        entry.stats.record_incoming_announce(time::now());
927                    }
928                    // Cache the announced identity
929                    let announced = crate::destination::AnnouncedIdentity {
930                        dest_hash: rns_core::types::DestHash(destination_hash),
931                        identity_hash: rns_core::types::IdentityHash(identity_hash),
932                        public_key,
933                        app_data: app_data.clone(),
934                        hops,
935                        received_at: time::now(),
936                    };
937                    self.known_destinations.insert(destination_hash, announced.clone());
938                    self.callbacks.on_announce(announced);
939                }
940                TransportAction::PathUpdated {
941                    destination_hash,
942                    hops,
943                    ..
944                } => {
945                    self.callbacks.on_path_updated(rns_core::types::DestHash(destination_hash), hops);
946                }
947                TransportAction::ForwardToLocalClients { raw, exclude } => {
948                    for entry in self.interfaces.values_mut() {
949                        if entry.online
950                            && entry.info.is_local_client
951                            && Some(entry.id) != exclude
952                        {
953                            let data = if let Some(ref ifac_state) = entry.ifac {
954                                ifac::mask_outbound(&raw, ifac_state)
955                            } else {
956                                raw.clone()
957                            };
958                            entry.stats.txb += data.len() as u64;
959                            entry.stats.tx_packets += 1;
960                            if let Err(e) = entry.writer.send_frame(&data) {
961                                log::warn!("[{}] forward to local client failed: {}", entry.info.id.0, e);
962                            }
963                        }
964                    }
965                }
966                TransportAction::ForwardPlainBroadcast { raw, to_local, exclude } => {
967                    for entry in self.interfaces.values_mut() {
968                        if entry.online
969                            && entry.info.is_local_client == to_local
970                            && Some(entry.id) != exclude
971                        {
972                            let data = if let Some(ref ifac_state) = entry.ifac {
973                                ifac::mask_outbound(&raw, ifac_state)
974                            } else {
975                                raw.clone()
976                            };
977                            entry.stats.txb += data.len() as u64;
978                            entry.stats.tx_packets += 1;
979                            if let Err(e) = entry.writer.send_frame(&data) {
980                                log::warn!("[{}] forward plain broadcast failed: {}", entry.info.id.0, e);
981                            }
982                        }
983                    }
984                }
985                TransportAction::CacheAnnounce { packet_hash, raw } => {
986                    if let Some(ref cache) = self.announce_cache {
987                        if let Err(e) = cache.store(&packet_hash, &raw, None) {
988                            log::warn!("Failed to cache announce: {}", e);
989                        }
990                    }
991                }
992                TransportAction::TunnelSynthesize { interface, data, dest_hash } => {
993                    // Pack as BROADCAST DATA PLAIN packet and send on interface
994                    let flags = rns_core::packet::PacketFlags {
995                        header_type: rns_core::constants::HEADER_1,
996                        context_flag: rns_core::constants::FLAG_UNSET,
997                        transport_type: rns_core::constants::TRANSPORT_BROADCAST,
998                        destination_type: rns_core::constants::DESTINATION_PLAIN,
999                        packet_type: rns_core::constants::PACKET_TYPE_DATA,
1000                    };
1001                    if let Ok(packet) = rns_core::packet::RawPacket::pack(
1002                        flags, 0, &dest_hash, None,
1003                        rns_core::constants::CONTEXT_NONE, &data,
1004                    ) {
1005                        if let Some(entry) = self.interfaces.get_mut(&interface) {
1006                            if entry.online {
1007                                let raw = if let Some(ref ifac_state) = entry.ifac {
1008                                    ifac::mask_outbound(&packet.raw, ifac_state)
1009                                } else {
1010                                    packet.raw
1011                                };
1012                                entry.stats.txb += raw.len() as u64;
1013                                entry.stats.tx_packets += 1;
1014                                if let Err(e) = entry.writer.send_frame(&raw) {
1015                                    log::warn!("[{}] tunnel synthesize send failed: {}", entry.info.id.0, e);
1016                                }
1017                            }
1018                        }
1019                    }
1020                }
1021                TransportAction::TunnelEstablished { tunnel_id, interface } => {
1022                    log::info!("Tunnel established: {:02x?} on interface {}", &tunnel_id[..4], interface.0);
1023                }
1024            }
1025        }
1026    }
1027
1028    /// Dispatch link manager actions.
1029    fn dispatch_link_actions(&mut self, actions: Vec<LinkManagerAction>) {
1030        for action in actions {
1031            match action {
1032                LinkManagerAction::SendPacket { raw, dest_type, attached_interface } => {
1033                    // Route through the transport engine's outbound path
1034                    match RawPacket::unpack(&raw) {
1035                        Ok(packet) => {
1036                            let transport_actions = self.engine.handle_outbound(
1037                                &packet,
1038                                dest_type,
1039                                attached_interface,
1040                                time::now(),
1041                            );
1042                            self.dispatch_all(transport_actions);
1043                        }
1044                        Err(e) => {
1045                            log::warn!("LinkManager SendPacket: failed to unpack: {:?}", e);
1046                        }
1047                    }
1048                }
1049                LinkManagerAction::LinkEstablished { link_id, rtt, is_initiator } => {
1050                    log::info!(
1051                        "Link established: {:02x?} rtt={:.3}s initiator={}",
1052                        &link_id[..4], rtt, is_initiator,
1053                    );
1054                    self.callbacks.on_link_established(rns_core::types::LinkId(link_id), rtt, is_initiator);
1055                }
1056                LinkManagerAction::LinkClosed { link_id, reason } => {
1057                    log::info!("Link closed: {:02x?} reason={:?}", &link_id[..4], reason);
1058                    self.callbacks.on_link_closed(rns_core::types::LinkId(link_id), reason);
1059                }
1060                LinkManagerAction::RemoteIdentified { link_id, identity_hash, public_key } => {
1061                    log::debug!(
1062                        "Remote identified on link {:02x?}: {:02x?}",
1063                        &link_id[..4], &identity_hash[..4],
1064                    );
1065                    self.callbacks.on_remote_identified(
1066                        rns_core::types::LinkId(link_id),
1067                        rns_core::types::IdentityHash(identity_hash),
1068                        public_key,
1069                    );
1070                }
1071                LinkManagerAction::RegisterLinkDest { link_id } => {
1072                    // Register the link_id as a LINK destination in the transport engine
1073                    self.engine.register_destination(link_id, rns_core::constants::DESTINATION_LINK);
1074                }
1075                LinkManagerAction::DeregisterLinkDest { link_id } => {
1076                    self.engine.deregister_destination(&link_id);
1077                }
1078                LinkManagerAction::ManagementRequest {
1079                    link_id, path_hash, data, request_id, remote_identity,
1080                } => {
1081                    self.handle_management_request(
1082                        link_id, path_hash, data, request_id, remote_identity,
1083                    );
1084                }
1085                LinkManagerAction::ResourceReceived { link_id, data, metadata } => {
1086                    self.callbacks.on_resource_received(rns_core::types::LinkId(link_id), data, metadata);
1087                }
1088                LinkManagerAction::ResourceCompleted { link_id } => {
1089                    self.callbacks.on_resource_completed(rns_core::types::LinkId(link_id));
1090                }
1091                LinkManagerAction::ResourceFailed { link_id, error } => {
1092                    log::debug!("Resource failed on link {:02x?}: {}", &link_id[..4], error);
1093                    self.callbacks.on_resource_failed(rns_core::types::LinkId(link_id), error);
1094                }
1095                LinkManagerAction::ResourceProgress { link_id, received, total } => {
1096                    self.callbacks.on_resource_progress(rns_core::types::LinkId(link_id), received, total);
1097                }
1098                LinkManagerAction::ResourceAcceptQuery { link_id, resource_hash, transfer_size, has_metadata } => {
1099                    let accept = self.callbacks.on_resource_accept_query(
1100                        rns_core::types::LinkId(link_id), resource_hash.clone(), transfer_size, has_metadata,
1101                    );
1102                    let accept_actions = self.link_manager.accept_resource(
1103                        &link_id, &resource_hash, accept, &mut self.rng,
1104                    );
1105                    // Re-dispatch (recursive but bounded: accept_resource won't produce more AcceptQuery)
1106                    self.dispatch_link_actions(accept_actions);
1107                }
1108                LinkManagerAction::ChannelMessageReceived { link_id, msgtype, payload } => {
1109                    self.callbacks.on_channel_message(rns_core::types::LinkId(link_id), msgtype, payload);
1110                }
1111                LinkManagerAction::LinkDataReceived { link_id, context, data } => {
1112                    self.callbacks.on_link_data(rns_core::types::LinkId(link_id), context, data);
1113                }
1114                LinkManagerAction::ResponseReceived { link_id, request_id, data } => {
1115                    self.callbacks.on_response(rns_core::types::LinkId(link_id), request_id, data);
1116                }
1117            }
1118        }
1119    }
1120
1121    /// Management announce interval in seconds.
1122    const MANAGEMENT_ANNOUNCE_INTERVAL: f64 = 300.0;
1123
1124    /// Delay before first management announce after startup.
1125    const MANAGEMENT_ANNOUNCE_DELAY: f64 = 5.0;
1126
1127    /// Emit management and/or blackhole announces if enabled and due.
1128    fn tick_management_announces(&mut self, now: f64) {
1129        if self.transport_identity.is_none() {
1130            return;
1131        }
1132
1133        let uptime = now - self.started;
1134
1135        // Wait for initial delay
1136        if !self.initial_announce_sent {
1137            if uptime < Self::MANAGEMENT_ANNOUNCE_DELAY {
1138                return;
1139            }
1140            self.initial_announce_sent = true;
1141            self.emit_management_announces(now);
1142            return;
1143        }
1144
1145        // Periodic re-announce
1146        if now - self.last_management_announce >= Self::MANAGEMENT_ANNOUNCE_INTERVAL {
1147            self.emit_management_announces(now);
1148        }
1149    }
1150
1151    /// Emit management/blackhole announce packets through the engine outbound path.
1152    fn emit_management_announces(&mut self, now: f64) {
1153        use crate::management;
1154
1155        self.last_management_announce = now;
1156
1157        let identity = match self.transport_identity {
1158            Some(ref id) => id,
1159            None => return,
1160        };
1161
1162        // Build announce packets first (immutable borrow of identity), then dispatch
1163        let mgmt_raw = if self.management_config.enable_remote_management {
1164            management::build_management_announce(identity, &mut self.rng)
1165        } else {
1166            None
1167        };
1168
1169        let bh_raw = if self.management_config.publish_blackhole {
1170            management::build_blackhole_announce(identity, &mut self.rng)
1171        } else {
1172            None
1173        };
1174
1175        if let Some(raw) = mgmt_raw {
1176            if let Ok(packet) = RawPacket::unpack(&raw) {
1177                let actions = self.engine.handle_outbound(
1178                    &packet,
1179                    rns_core::constants::DESTINATION_SINGLE,
1180                    None,
1181                    now,
1182                );
1183                self.dispatch_all(actions);
1184                log::debug!("Emitted management destination announce");
1185            }
1186        }
1187
1188        if let Some(raw) = bh_raw {
1189            if let Ok(packet) = RawPacket::unpack(&raw) {
1190                let actions = self.engine.handle_outbound(
1191                    &packet,
1192                    rns_core::constants::DESTINATION_SINGLE,
1193                    None,
1194                    now,
1195                );
1196                self.dispatch_all(actions);
1197                log::debug!("Emitted blackhole info announce");
1198            }
1199        }
1200    }
1201
1202    /// Handle a management request by querying engine state and sending a response.
1203    fn handle_management_request(
1204        &mut self,
1205        link_id: [u8; 16],
1206        path_hash: [u8; 16],
1207        data: Vec<u8>,
1208        request_id: [u8; 16],
1209        remote_identity: Option<([u8; 16], [u8; 64])>,
1210    ) {
1211        use crate::management;
1212
1213        // ACL check for /status and /path (ALLOW_LIST), /list is ALLOW_ALL
1214        let is_restricted = path_hash == management::status_path_hash()
1215            || path_hash == management::path_path_hash();
1216
1217        if is_restricted && !self.management_config.remote_management_allowed.is_empty() {
1218            match remote_identity {
1219                Some((identity_hash, _)) => {
1220                    if !self.management_config.remote_management_allowed.contains(&identity_hash) {
1221                        log::debug!("Management request denied: identity not in allowed list");
1222                        return;
1223                    }
1224                }
1225                None => {
1226                    log::debug!("Management request denied: peer not identified");
1227                    return;
1228                }
1229            }
1230        }
1231
1232        let response_data = if path_hash == management::status_path_hash() {
1233            management::handle_status_request(&data, &self.engine, &self.interfaces, self.started)
1234        } else if path_hash == management::path_path_hash() {
1235            management::handle_path_request(&data, &self.engine)
1236        } else if path_hash == management::list_path_hash() {
1237            management::handle_blackhole_list_request(&self.engine)
1238        } else {
1239            log::warn!("Unknown management path_hash: {:02x?}", &path_hash[..4]);
1240            None
1241        };
1242
1243        if let Some(response) = response_data {
1244            let actions = self.link_manager.send_management_response(
1245                &link_id, &request_id, &response, &mut self.rng,
1246            );
1247            self.dispatch_link_actions(actions);
1248        }
1249    }
1250}
1251
1252#[cfg(test)]
1253mod tests {
1254    use super::*;
1255    use crate::event;
1256    use crate::interface::Writer;
1257    use rns_core::announce::AnnounceData;
1258    use rns_core::constants;
1259    use rns_core::packet::PacketFlags;
1260    use rns_core::transport::types::InterfaceInfo;
1261    use rns_crypto::identity::Identity;
1262    use std::io;
1263    use std::sync::mpsc;
1264    use std::sync::{Arc, Mutex};
1265
1266    struct MockWriter {
1267        sent: Arc<Mutex<Vec<Vec<u8>>>>,
1268    }
1269
1270    impl MockWriter {
1271        fn new() -> (Self, Arc<Mutex<Vec<Vec<u8>>>>) {
1272            let sent = Arc::new(Mutex::new(Vec::new()));
1273            (MockWriter { sent: sent.clone() }, sent)
1274        }
1275    }
1276
1277    impl Writer for MockWriter {
1278        fn send_frame(&mut self, data: &[u8]) -> io::Result<()> {
1279            self.sent.lock().unwrap().push(data.to_vec());
1280            Ok(())
1281        }
1282    }
1283
1284    use rns_core::types::{DestHash, IdentityHash, LinkId as TypedLinkId, PacketHash};
1285
1286    struct MockCallbacks {
1287        announces: Arc<Mutex<Vec<(DestHash, u8)>>>,
1288        paths: Arc<Mutex<Vec<(DestHash, u8)>>>,
1289        deliveries: Arc<Mutex<Vec<DestHash>>>,
1290        iface_ups: Arc<Mutex<Vec<InterfaceId>>>,
1291        iface_downs: Arc<Mutex<Vec<InterfaceId>>>,
1292        link_established: Arc<Mutex<Vec<(TypedLinkId, f64, bool)>>>,
1293        link_closed: Arc<Mutex<Vec<TypedLinkId>>>,
1294        remote_identified: Arc<Mutex<Vec<(TypedLinkId, IdentityHash)>>>,
1295        resources_received: Arc<Mutex<Vec<(TypedLinkId, Vec<u8>)>>>,
1296        resource_completed: Arc<Mutex<Vec<TypedLinkId>>>,
1297        resource_failed: Arc<Mutex<Vec<(TypedLinkId, String)>>>,
1298        channel_messages: Arc<Mutex<Vec<(TypedLinkId, u16, Vec<u8>)>>>,
1299        link_data: Arc<Mutex<Vec<(TypedLinkId, u8, Vec<u8>)>>>,
1300        responses: Arc<Mutex<Vec<(TypedLinkId, [u8; 16], Vec<u8>)>>>,
1301        proofs: Arc<Mutex<Vec<(DestHash, PacketHash, f64)>>>,
1302        proof_requested: Arc<Mutex<Vec<(DestHash, PacketHash)>>>,
1303    }
1304
1305    impl MockCallbacks {
1306        fn new() -> (
1307            Self,
1308            Arc<Mutex<Vec<(DestHash, u8)>>>,
1309            Arc<Mutex<Vec<(DestHash, u8)>>>,
1310            Arc<Mutex<Vec<DestHash>>>,
1311            Arc<Mutex<Vec<InterfaceId>>>,
1312            Arc<Mutex<Vec<InterfaceId>>>,
1313        ) {
1314            let announces = Arc::new(Mutex::new(Vec::new()));
1315            let paths = Arc::new(Mutex::new(Vec::new()));
1316            let deliveries = Arc::new(Mutex::new(Vec::new()));
1317            let iface_ups = Arc::new(Mutex::new(Vec::new()));
1318            let iface_downs = Arc::new(Mutex::new(Vec::new()));
1319            (
1320                MockCallbacks {
1321                    announces: announces.clone(),
1322                    paths: paths.clone(),
1323                    deliveries: deliveries.clone(),
1324                    iface_ups: iface_ups.clone(),
1325                    iface_downs: iface_downs.clone(),
1326                    link_established: Arc::new(Mutex::new(Vec::new())),
1327                    link_closed: Arc::new(Mutex::new(Vec::new())),
1328                    remote_identified: Arc::new(Mutex::new(Vec::new())),
1329                    resources_received: Arc::new(Mutex::new(Vec::new())),
1330                    resource_completed: Arc::new(Mutex::new(Vec::new())),
1331                    resource_failed: Arc::new(Mutex::new(Vec::new())),
1332                    channel_messages: Arc::new(Mutex::new(Vec::new())),
1333                    link_data: Arc::new(Mutex::new(Vec::new())),
1334                    responses: Arc::new(Mutex::new(Vec::new())),
1335                    proofs: Arc::new(Mutex::new(Vec::new())),
1336                    proof_requested: Arc::new(Mutex::new(Vec::new())),
1337                },
1338                announces,
1339                paths,
1340                deliveries,
1341                iface_ups,
1342                iface_downs,
1343            )
1344        }
1345
1346        fn with_link_tracking() -> (
1347            Self,
1348            Arc<Mutex<Vec<(TypedLinkId, f64, bool)>>>,
1349            Arc<Mutex<Vec<TypedLinkId>>>,
1350            Arc<Mutex<Vec<(TypedLinkId, IdentityHash)>>>,
1351        ) {
1352            let link_established = Arc::new(Mutex::new(Vec::new()));
1353            let link_closed = Arc::new(Mutex::new(Vec::new()));
1354            let remote_identified = Arc::new(Mutex::new(Vec::new()));
1355            (
1356                MockCallbacks {
1357                    announces: Arc::new(Mutex::new(Vec::new())),
1358                    paths: Arc::new(Mutex::new(Vec::new())),
1359                    deliveries: Arc::new(Mutex::new(Vec::new())),
1360                    iface_ups: Arc::new(Mutex::new(Vec::new())),
1361                    iface_downs: Arc::new(Mutex::new(Vec::new())),
1362                    link_established: link_established.clone(),
1363                    link_closed: link_closed.clone(),
1364                    remote_identified: remote_identified.clone(),
1365                    resources_received: Arc::new(Mutex::new(Vec::new())),
1366                    resource_completed: Arc::new(Mutex::new(Vec::new())),
1367                    resource_failed: Arc::new(Mutex::new(Vec::new())),
1368                    channel_messages: Arc::new(Mutex::new(Vec::new())),
1369                    link_data: Arc::new(Mutex::new(Vec::new())),
1370                    responses: Arc::new(Mutex::new(Vec::new())),
1371                    proofs: Arc::new(Mutex::new(Vec::new())),
1372                    proof_requested: Arc::new(Mutex::new(Vec::new())),
1373                },
1374                link_established,
1375                link_closed,
1376                remote_identified,
1377            )
1378        }
1379    }
1380
1381    impl Callbacks for MockCallbacks {
1382        fn on_announce(&mut self, announced: crate::destination::AnnouncedIdentity) {
1383            self.announces.lock().unwrap().push((announced.dest_hash, announced.hops));
1384        }
1385
1386        fn on_path_updated(&mut self, dest_hash: DestHash, hops: u8) {
1387            self.paths.lock().unwrap().push((dest_hash, hops));
1388        }
1389
1390        fn on_local_delivery(&mut self, dest_hash: DestHash, _raw: Vec<u8>, _packet_hash: PacketHash) {
1391            self.deliveries.lock().unwrap().push(dest_hash);
1392        }
1393
1394        fn on_interface_up(&mut self, id: InterfaceId) {
1395            self.iface_ups.lock().unwrap().push(id);
1396        }
1397
1398        fn on_interface_down(&mut self, id: InterfaceId) {
1399            self.iface_downs.lock().unwrap().push(id);
1400        }
1401
1402        fn on_link_established(&mut self, link_id: TypedLinkId, rtt: f64, is_initiator: bool) {
1403            self.link_established.lock().unwrap().push((link_id, rtt, is_initiator));
1404        }
1405
1406        fn on_link_closed(&mut self, link_id: TypedLinkId, _reason: Option<rns_core::link::TeardownReason>) {
1407            self.link_closed.lock().unwrap().push(link_id);
1408        }
1409
1410        fn on_remote_identified(&mut self, link_id: TypedLinkId, identity_hash: IdentityHash, _public_key: [u8; 64]) {
1411            self.remote_identified.lock().unwrap().push((link_id, identity_hash));
1412        }
1413
1414        fn on_resource_received(&mut self, link_id: TypedLinkId, data: Vec<u8>, _metadata: Option<Vec<u8>>) {
1415            self.resources_received.lock().unwrap().push((link_id, data));
1416        }
1417
1418        fn on_resource_completed(&mut self, link_id: TypedLinkId) {
1419            self.resource_completed.lock().unwrap().push(link_id);
1420        }
1421
1422        fn on_resource_failed(&mut self, link_id: TypedLinkId, error: String) {
1423            self.resource_failed.lock().unwrap().push((link_id, error));
1424        }
1425
1426        fn on_channel_message(&mut self, link_id: TypedLinkId, msgtype: u16, payload: Vec<u8>) {
1427            self.channel_messages.lock().unwrap().push((link_id, msgtype, payload));
1428        }
1429
1430        fn on_link_data(&mut self, link_id: TypedLinkId, context: u8, data: Vec<u8>) {
1431            self.link_data.lock().unwrap().push((link_id, context, data));
1432        }
1433
1434        fn on_response(&mut self, link_id: TypedLinkId, request_id: [u8; 16], data: Vec<u8>) {
1435            self.responses.lock().unwrap().push((link_id, request_id, data));
1436        }
1437
1438        fn on_proof(&mut self, dest_hash: DestHash, packet_hash: PacketHash, rtt: f64) {
1439            self.proofs.lock().unwrap().push((dest_hash, packet_hash, rtt));
1440        }
1441
1442        fn on_proof_requested(&mut self, dest_hash: DestHash, packet_hash: PacketHash) -> bool {
1443            self.proof_requested.lock().unwrap().push((dest_hash, packet_hash));
1444            true
1445        }
1446    }
1447
1448    fn make_interface_info(id: u64) -> InterfaceInfo {
1449        InterfaceInfo {
1450            id: InterfaceId(id),
1451            name: format!("test-{}", id),
1452            mode: constants::MODE_FULL,
1453            out_capable: true,
1454            in_capable: true,
1455            bitrate: None,
1456            announce_rate_target: None,
1457            announce_rate_grace: 0,
1458            announce_rate_penalty: 0.0,
1459            announce_cap: rns_core::constants::ANNOUNCE_CAP,
1460            is_local_client: false,
1461            wants_tunnel: false,
1462            tunnel_id: None,
1463        }
1464    }
1465
1466    fn make_entry(id: u64, writer: Box<dyn Writer>, online: bool) -> InterfaceEntry {
1467        InterfaceEntry {
1468            id: InterfaceId(id),
1469            info: make_interface_info(id),
1470            writer,
1471            online,
1472            dynamic: false,
1473            ifac: None,
1474            stats: InterfaceStats::default(),
1475            interface_type: String::new(),
1476        }
1477    }
1478
1479    /// Build a valid announce packet that the engine will accept.
1480    fn build_announce_packet(identity: &Identity) -> Vec<u8> {
1481        let dest_hash = rns_core::destination::destination_hash(
1482            "test",
1483            &["app"],
1484            Some(identity.hash()),
1485        );
1486        let name_hash = rns_core::destination::name_hash("test", &["app"]);
1487        let random_hash = [0x42u8; 10];
1488
1489        let (announce_data, _has_ratchet) = AnnounceData::pack(
1490            identity,
1491            &dest_hash,
1492            &name_hash,
1493            &random_hash,
1494            None,
1495            None,
1496        )
1497        .unwrap();
1498
1499        let flags = PacketFlags {
1500            header_type: constants::HEADER_1,
1501            context_flag: constants::FLAG_UNSET,
1502            transport_type: constants::TRANSPORT_BROADCAST,
1503            destination_type: constants::DESTINATION_SINGLE,
1504            packet_type: constants::PACKET_TYPE_ANNOUNCE,
1505        };
1506
1507        let packet = RawPacket::pack(flags, 0, &dest_hash, None, constants::CONTEXT_NONE, &announce_data).unwrap();
1508        packet.raw
1509    }
1510
1511    #[test]
1512    fn process_inbound_frame() {
1513        let (tx, rx) = event::channel();
1514        let (cbs, announces, _, _, _, _) = MockCallbacks::new();
1515        let mut driver = Driver::new(
1516            TransportConfig { transport_enabled: false, identity_hash: None },
1517            rx,
1518            Box::new(cbs),
1519        );
1520        let info = make_interface_info(1);
1521        driver.engine.register_interface(info.clone());
1522        let (writer, _sent) = MockWriter::new();
1523        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
1524
1525        let identity = Identity::new(&mut OsRng);
1526        let announce_raw = build_announce_packet(&identity);
1527
1528        // Send frame then shutdown
1529        tx.send(Event::Frame { interface_id: InterfaceId(1), data: announce_raw }).unwrap();
1530        tx.send(Event::Shutdown).unwrap();
1531        driver.run();
1532
1533        assert_eq!(announces.lock().unwrap().len(), 1);
1534    }
1535
1536    #[test]
1537    fn dispatch_send() {
1538        let (tx, rx) = event::channel();
1539        let (cbs, _, _, _, _, _) = MockCallbacks::new();
1540        let mut driver = Driver::new(
1541            TransportConfig { transport_enabled: false, identity_hash: None },
1542            rx,
1543            Box::new(cbs),
1544        );
1545        let (writer, sent) = MockWriter::new();
1546        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
1547
1548        driver.dispatch_all(vec![TransportAction::SendOnInterface {
1549            interface: InterfaceId(1),
1550            raw: vec![0x01, 0x02, 0x03],
1551        }]);
1552
1553        assert_eq!(sent.lock().unwrap().len(), 1);
1554        assert_eq!(sent.lock().unwrap()[0], vec![0x01, 0x02, 0x03]);
1555
1556        drop(tx);
1557    }
1558
1559    #[test]
1560    fn dispatch_broadcast() {
1561        let (tx, rx) = event::channel();
1562        let (cbs, _, _, _, _, _) = MockCallbacks::new();
1563        let mut driver = Driver::new(
1564            TransportConfig { transport_enabled: false, identity_hash: None },
1565            rx,
1566            Box::new(cbs),
1567        );
1568
1569        let (w1, sent1) = MockWriter::new();
1570        let (w2, sent2) = MockWriter::new();
1571        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(w1), true));
1572        driver.interfaces.insert(InterfaceId(2), make_entry(2, Box::new(w2), true));
1573
1574        driver.dispatch_all(vec![TransportAction::BroadcastOnAllInterfaces {
1575            raw: vec![0xAA],
1576            exclude: None,
1577        }]);
1578
1579        assert_eq!(sent1.lock().unwrap().len(), 1);
1580        assert_eq!(sent2.lock().unwrap().len(), 1);
1581
1582        drop(tx);
1583    }
1584
1585    #[test]
1586    fn dispatch_broadcast_exclude() {
1587        let (tx, rx) = event::channel();
1588        let (cbs, _, _, _, _, _) = MockCallbacks::new();
1589        let mut driver = Driver::new(
1590            TransportConfig { transport_enabled: false, identity_hash: None },
1591            rx,
1592            Box::new(cbs),
1593        );
1594
1595        let (w1, sent1) = MockWriter::new();
1596        let (w2, sent2) = MockWriter::new();
1597        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(w1), true));
1598        driver.interfaces.insert(InterfaceId(2), make_entry(2, Box::new(w2), true));
1599
1600        driver.dispatch_all(vec![TransportAction::BroadcastOnAllInterfaces {
1601            raw: vec![0xBB],
1602            exclude: Some(InterfaceId(1)),
1603        }]);
1604
1605        assert_eq!(sent1.lock().unwrap().len(), 0); // excluded
1606        assert_eq!(sent2.lock().unwrap().len(), 1);
1607
1608        drop(tx);
1609    }
1610
1611    #[test]
1612    fn tick_event() {
1613        let (tx, rx) = event::channel();
1614        let (cbs, _, _, _, _, _) = MockCallbacks::new();
1615        let mut driver = Driver::new(
1616            TransportConfig { transport_enabled: true, identity_hash: Some([0x42; 16]) },
1617            rx,
1618            Box::new(cbs),
1619        );
1620        let info = make_interface_info(1);
1621        driver.engine.register_interface(info.clone());
1622        let (writer, _sent) = MockWriter::new();
1623        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
1624
1625        // Send Tick then Shutdown
1626        tx.send(Event::Tick).unwrap();
1627        tx.send(Event::Shutdown).unwrap();
1628        driver.run();
1629        // No crash = tick was processed successfully
1630    }
1631
1632    #[test]
1633    fn shutdown_event() {
1634        let (tx, rx) = event::channel();
1635        let (cbs, _, _, _, _, _) = MockCallbacks::new();
1636        let mut driver = Driver::new(
1637            TransportConfig { transport_enabled: false, identity_hash: None },
1638            rx,
1639            Box::new(cbs),
1640        );
1641
1642        tx.send(Event::Shutdown).unwrap();
1643        driver.run(); // Should return immediately
1644    }
1645
1646    #[test]
1647    fn announce_callback() {
1648        let (tx, rx) = event::channel();
1649        let (cbs, announces, paths, _, _, _) = MockCallbacks::new();
1650        let mut driver = Driver::new(
1651            TransportConfig { transport_enabled: false, identity_hash: None },
1652            rx,
1653            Box::new(cbs),
1654        );
1655        let info = make_interface_info(1);
1656        driver.engine.register_interface(info.clone());
1657        let (writer, _sent) = MockWriter::new();
1658        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
1659
1660        let identity = Identity::new(&mut OsRng);
1661        let announce_raw = build_announce_packet(&identity);
1662
1663        tx.send(Event::Frame { interface_id: InterfaceId(1), data: announce_raw }).unwrap();
1664        tx.send(Event::Shutdown).unwrap();
1665        driver.run();
1666
1667        let ann = announces.lock().unwrap();
1668        assert_eq!(ann.len(), 1);
1669        // Hops should be 1 (incremented from 0 by handle_inbound)
1670        assert_eq!(ann[0].1, 1);
1671
1672        let p = paths.lock().unwrap();
1673        assert_eq!(p.len(), 1);
1674    }
1675
1676    #[test]
1677    fn dispatch_skips_offline_interface() {
1678        let (tx, rx) = event::channel();
1679        let (cbs, _, _, _, _, _) = MockCallbacks::new();
1680        let mut driver = Driver::new(
1681            TransportConfig { transport_enabled: false, identity_hash: None },
1682            rx,
1683            Box::new(cbs),
1684        );
1685
1686        let (w1, sent1) = MockWriter::new();
1687        let (w2, sent2) = MockWriter::new();
1688        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(w1), false)); // offline
1689        driver.interfaces.insert(InterfaceId(2), make_entry(2, Box::new(w2), true));
1690
1691        // Direct send to offline interface: should be skipped
1692        driver.dispatch_all(vec![TransportAction::SendOnInterface {
1693            interface: InterfaceId(1),
1694            raw: vec![0x01],
1695        }]);
1696        assert_eq!(sent1.lock().unwrap().len(), 0);
1697
1698        // Broadcast: only online interface should receive
1699        driver.dispatch_all(vec![TransportAction::BroadcastOnAllInterfaces {
1700            raw: vec![0x02],
1701            exclude: None,
1702        }]);
1703        assert_eq!(sent1.lock().unwrap().len(), 0); // still offline
1704        assert_eq!(sent2.lock().unwrap().len(), 1);
1705
1706        drop(tx);
1707    }
1708
1709    #[test]
1710    fn interface_up_refreshes_writer() {
1711        let (tx, rx) = event::channel();
1712        let (cbs, _, _, _, _, _) = MockCallbacks::new();
1713        let mut driver = Driver::new(
1714            TransportConfig { transport_enabled: false, identity_hash: None },
1715            rx,
1716            Box::new(cbs),
1717        );
1718
1719        let (w_old, sent_old) = MockWriter::new();
1720        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(w_old), false));
1721
1722        // Simulate reconnect: InterfaceUp with new writer
1723        let (w_new, sent_new) = MockWriter::new();
1724        tx.send(Event::InterfaceUp(InterfaceId(1), Some(Box::new(w_new)), None)).unwrap();
1725        tx.send(Event::Shutdown).unwrap();
1726        driver.run();
1727
1728        // Interface should be online now
1729        assert!(driver.interfaces[&InterfaceId(1)].online);
1730
1731        // Send via the (now-refreshed) interface
1732        driver.dispatch_all(vec![TransportAction::SendOnInterface {
1733            interface: InterfaceId(1),
1734            raw: vec![0xFF],
1735        }]);
1736
1737        // Old writer should not have received anything
1738        assert_eq!(sent_old.lock().unwrap().len(), 0);
1739        // New writer should have received the data
1740        assert_eq!(sent_new.lock().unwrap().len(), 1);
1741        assert_eq!(sent_new.lock().unwrap()[0], vec![0xFF]);
1742
1743        drop(tx);
1744    }
1745
1746    #[test]
1747    fn dynamic_interface_register() {
1748        let (tx, rx) = event::channel();
1749        let (cbs, _, _, _, iface_ups, _) = MockCallbacks::new();
1750        let mut driver = Driver::new(
1751            TransportConfig { transport_enabled: false, identity_hash: None },
1752            rx,
1753            Box::new(cbs),
1754        );
1755
1756        let info = make_interface_info(100);
1757        let (writer, sent) = MockWriter::new();
1758
1759        // InterfaceUp with InterfaceInfo = new dynamic interface
1760        tx.send(Event::InterfaceUp(
1761            InterfaceId(100),
1762            Some(Box::new(writer)),
1763            Some(info),
1764        ))
1765        .unwrap();
1766        tx.send(Event::Shutdown).unwrap();
1767        driver.run();
1768
1769        // Should be registered and online
1770        assert!(driver.interfaces.contains_key(&InterfaceId(100)));
1771        assert!(driver.interfaces[&InterfaceId(100)].online);
1772        assert!(driver.interfaces[&InterfaceId(100)].dynamic);
1773
1774        // Callback should have fired
1775        assert_eq!(iface_ups.lock().unwrap().len(), 1);
1776        assert_eq!(iface_ups.lock().unwrap()[0], InterfaceId(100));
1777
1778        // Can send to it
1779        driver.dispatch_all(vec![TransportAction::SendOnInterface {
1780            interface: InterfaceId(100),
1781            raw: vec![0x42],
1782        }]);
1783        assert_eq!(sent.lock().unwrap().len(), 1);
1784
1785        drop(tx);
1786    }
1787
1788    #[test]
1789    fn dynamic_interface_deregister() {
1790        let (tx, rx) = event::channel();
1791        let (cbs, _, _, _, _, iface_downs) = MockCallbacks::new();
1792        let mut driver = Driver::new(
1793            TransportConfig { transport_enabled: false, identity_hash: None },
1794            rx,
1795            Box::new(cbs),
1796        );
1797
1798        // Register a dynamic interface
1799        let info = make_interface_info(200);
1800        driver.engine.register_interface(info.clone());
1801        let (writer, _sent) = MockWriter::new();
1802        driver.interfaces.insert(InterfaceId(200), InterfaceEntry {
1803            id: InterfaceId(200),
1804            info,
1805            writer: Box::new(writer),
1806            online: true,
1807            dynamic: true,
1808            ifac: None,
1809            stats: InterfaceStats::default(),
1810            interface_type: String::new(),
1811        });
1812
1813        // InterfaceDown for dynamic → should be removed entirely
1814        tx.send(Event::InterfaceDown(InterfaceId(200))).unwrap();
1815        tx.send(Event::Shutdown).unwrap();
1816        driver.run();
1817
1818        assert!(!driver.interfaces.contains_key(&InterfaceId(200)));
1819        assert_eq!(iface_downs.lock().unwrap().len(), 1);
1820        assert_eq!(iface_downs.lock().unwrap()[0], InterfaceId(200));
1821    }
1822
1823    #[test]
1824    fn interface_callbacks_fire() {
1825        let (tx, rx) = event::channel();
1826        let (cbs, _, _, _, iface_ups, iface_downs) = MockCallbacks::new();
1827        let mut driver = Driver::new(
1828            TransportConfig { transport_enabled: false, identity_hash: None },
1829            rx,
1830            Box::new(cbs),
1831        );
1832
1833        // Static interface
1834        let (writer, _) = MockWriter::new();
1835        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), false));
1836
1837        tx.send(Event::InterfaceUp(InterfaceId(1), None, None)).unwrap();
1838        tx.send(Event::InterfaceDown(InterfaceId(1))).unwrap();
1839        tx.send(Event::Shutdown).unwrap();
1840        driver.run();
1841
1842        assert_eq!(iface_ups.lock().unwrap().len(), 1);
1843        assert_eq!(iface_downs.lock().unwrap().len(), 1);
1844        // Static interface should still exist but be offline
1845        assert!(driver.interfaces.contains_key(&InterfaceId(1)));
1846        assert!(!driver.interfaces[&InterfaceId(1)].online);
1847    }
1848
1849    // =========================================================================
1850    // New tests for Phase 6a
1851    // =========================================================================
1852
1853    #[test]
1854    fn frame_updates_rx_stats() {
1855        let (tx, rx) = event::channel();
1856        let (cbs, _, _, _, _, _) = MockCallbacks::new();
1857        let mut driver = Driver::new(
1858            TransportConfig { transport_enabled: false, identity_hash: None },
1859            rx,
1860            Box::new(cbs),
1861        );
1862        let info = make_interface_info(1);
1863        driver.engine.register_interface(info.clone());
1864        let (writer, _sent) = MockWriter::new();
1865        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
1866
1867        let identity = Identity::new(&mut OsRng);
1868        let announce_raw = build_announce_packet(&identity);
1869        let announce_len = announce_raw.len() as u64;
1870
1871        tx.send(Event::Frame { interface_id: InterfaceId(1), data: announce_raw }).unwrap();
1872        tx.send(Event::Shutdown).unwrap();
1873        driver.run();
1874
1875        let stats = &driver.interfaces[&InterfaceId(1)].stats;
1876        assert_eq!(stats.rxb, announce_len);
1877        assert_eq!(stats.rx_packets, 1);
1878    }
1879
1880    #[test]
1881    fn send_updates_tx_stats() {
1882        let (tx, rx) = event::channel();
1883        let (cbs, _, _, _, _, _) = MockCallbacks::new();
1884        let mut driver = Driver::new(
1885            TransportConfig { transport_enabled: false, identity_hash: None },
1886            rx,
1887            Box::new(cbs),
1888        );
1889        let (writer, _sent) = MockWriter::new();
1890        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
1891
1892        driver.dispatch_all(vec![TransportAction::SendOnInterface {
1893            interface: InterfaceId(1),
1894            raw: vec![0x01, 0x02, 0x03],
1895        }]);
1896
1897        let stats = &driver.interfaces[&InterfaceId(1)].stats;
1898        assert_eq!(stats.txb, 3);
1899        assert_eq!(stats.tx_packets, 1);
1900
1901        drop(tx);
1902    }
1903
1904    #[test]
1905    fn broadcast_updates_tx_stats() {
1906        let (tx, rx) = event::channel();
1907        let (cbs, _, _, _, _, _) = MockCallbacks::new();
1908        let mut driver = Driver::new(
1909            TransportConfig { transport_enabled: false, identity_hash: None },
1910            rx,
1911            Box::new(cbs),
1912        );
1913        let (w1, _s1) = MockWriter::new();
1914        let (w2, _s2) = MockWriter::new();
1915        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(w1), true));
1916        driver.interfaces.insert(InterfaceId(2), make_entry(2, Box::new(w2), true));
1917
1918        driver.dispatch_all(vec![TransportAction::BroadcastOnAllInterfaces {
1919            raw: vec![0xAA, 0xBB],
1920            exclude: None,
1921        }]);
1922
1923        // Both interfaces should have tx stats updated
1924        assert_eq!(driver.interfaces[&InterfaceId(1)].stats.txb, 2);
1925        assert_eq!(driver.interfaces[&InterfaceId(1)].stats.tx_packets, 1);
1926        assert_eq!(driver.interfaces[&InterfaceId(2)].stats.txb, 2);
1927        assert_eq!(driver.interfaces[&InterfaceId(2)].stats.tx_packets, 1);
1928
1929        drop(tx);
1930    }
1931
1932    #[test]
1933    fn query_interface_stats() {
1934        let (tx, rx) = event::channel();
1935        let (cbs, _, _, _, _, _) = MockCallbacks::new();
1936        let mut driver = Driver::new(
1937            TransportConfig { transport_enabled: true, identity_hash: Some([0x42; 16]) },
1938            rx,
1939            Box::new(cbs),
1940        );
1941        let (writer, _sent) = MockWriter::new();
1942        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
1943
1944        let (resp_tx, resp_rx) = mpsc::channel();
1945        tx.send(Event::Query(QueryRequest::InterfaceStats, resp_tx)).unwrap();
1946        tx.send(Event::Shutdown).unwrap();
1947        driver.run();
1948
1949        let resp = resp_rx.recv().unwrap();
1950        match resp {
1951            QueryResponse::InterfaceStats(stats) => {
1952                assert_eq!(stats.interfaces.len(), 1);
1953                assert_eq!(stats.interfaces[0].name, "test-1");
1954                assert!(stats.interfaces[0].status);
1955                assert_eq!(stats.transport_id, Some([0x42; 16]));
1956                assert!(stats.transport_enabled);
1957            }
1958            _ => panic!("unexpected response"),
1959        }
1960    }
1961
1962    #[test]
1963    fn query_path_table() {
1964        let (tx, rx) = event::channel();
1965        let (cbs, _, _, _, _, _) = MockCallbacks::new();
1966        let mut driver = Driver::new(
1967            TransportConfig { transport_enabled: false, identity_hash: None },
1968            rx,
1969            Box::new(cbs),
1970        );
1971        let info = make_interface_info(1);
1972        driver.engine.register_interface(info);
1973        let (writer, _sent) = MockWriter::new();
1974        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
1975
1976        // Feed an announce to create a path entry
1977        let identity = Identity::new(&mut OsRng);
1978        let announce_raw = build_announce_packet(&identity);
1979        tx.send(Event::Frame { interface_id: InterfaceId(1), data: announce_raw }).unwrap();
1980
1981        let (resp_tx, resp_rx) = mpsc::channel();
1982        tx.send(Event::Query(QueryRequest::PathTable { max_hops: None }, resp_tx)).unwrap();
1983        tx.send(Event::Shutdown).unwrap();
1984        driver.run();
1985
1986        let resp = resp_rx.recv().unwrap();
1987        match resp {
1988            QueryResponse::PathTable(entries) => {
1989                assert_eq!(entries.len(), 1);
1990                assert_eq!(entries[0].hops, 1);
1991            }
1992            _ => panic!("unexpected response"),
1993        }
1994    }
1995
1996    #[test]
1997    fn query_drop_path() {
1998        let (tx, rx) = event::channel();
1999        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2000        let mut driver = Driver::new(
2001            TransportConfig { transport_enabled: false, identity_hash: None },
2002            rx,
2003            Box::new(cbs),
2004        );
2005        let info = make_interface_info(1);
2006        driver.engine.register_interface(info);
2007        let (writer, _sent) = MockWriter::new();
2008        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
2009
2010        // Feed an announce to create a path entry
2011        let identity = Identity::new(&mut OsRng);
2012        let announce_raw = build_announce_packet(&identity);
2013        let dest_hash = rns_core::destination::destination_hash(
2014            "test", &["app"], Some(identity.hash()),
2015        );
2016
2017        tx.send(Event::Frame { interface_id: InterfaceId(1), data: announce_raw }).unwrap();
2018
2019        let (resp_tx, resp_rx) = mpsc::channel();
2020        tx.send(Event::Query(QueryRequest::DropPath { dest_hash }, resp_tx)).unwrap();
2021        tx.send(Event::Shutdown).unwrap();
2022        driver.run();
2023
2024        let resp = resp_rx.recv().unwrap();
2025        match resp {
2026            QueryResponse::DropPath(dropped) => {
2027                assert!(dropped);
2028            }
2029            _ => panic!("unexpected response"),
2030        }
2031    }
2032
2033    #[test]
2034    fn send_outbound_event() {
2035        let (tx, rx) = event::channel();
2036        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2037        let mut driver = Driver::new(
2038            TransportConfig { transport_enabled: false, identity_hash: None },
2039            rx,
2040            Box::new(cbs),
2041        );
2042        let (writer, sent) = MockWriter::new();
2043        let info = make_interface_info(1);
2044        driver.engine.register_interface(info);
2045        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
2046
2047        // Build a DATA packet to a destination
2048        let dest = [0xAA; 16];
2049        let flags = PacketFlags {
2050            header_type: constants::HEADER_1,
2051            context_flag: constants::FLAG_UNSET,
2052            transport_type: constants::TRANSPORT_BROADCAST,
2053            destination_type: constants::DESTINATION_PLAIN,
2054            packet_type: constants::PACKET_TYPE_DATA,
2055        };
2056        let packet = RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"hello").unwrap();
2057
2058        tx.send(Event::SendOutbound {
2059            raw: packet.raw,
2060            dest_type: constants::DESTINATION_PLAIN,
2061            attached_interface: None,
2062        }).unwrap();
2063        tx.send(Event::Shutdown).unwrap();
2064        driver.run();
2065
2066        // PLAIN packet should be broadcast on all interfaces
2067        assert_eq!(sent.lock().unwrap().len(), 1);
2068    }
2069
2070    #[test]
2071    fn register_destination_and_deliver() {
2072        let (tx, rx) = event::channel();
2073        let (cbs, _, _, deliveries, _, _) = MockCallbacks::new();
2074        let mut driver = Driver::new(
2075            TransportConfig { transport_enabled: false, identity_hash: None },
2076            rx,
2077            Box::new(cbs),
2078        );
2079        let info = make_interface_info(1);
2080        driver.engine.register_interface(info);
2081        let (writer, _sent) = MockWriter::new();
2082        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
2083
2084        let dest = [0xBB; 16];
2085
2086        // Register destination then send a data packet to it
2087        tx.send(Event::RegisterDestination {
2088            dest_hash: dest,
2089            dest_type: constants::DESTINATION_SINGLE,
2090        }).unwrap();
2091
2092        let flags = PacketFlags {
2093            header_type: constants::HEADER_1,
2094            context_flag: constants::FLAG_UNSET,
2095            transport_type: constants::TRANSPORT_BROADCAST,
2096            destination_type: constants::DESTINATION_SINGLE,
2097            packet_type: constants::PACKET_TYPE_DATA,
2098        };
2099        let packet = RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"data").unwrap();
2100        tx.send(Event::Frame { interface_id: InterfaceId(1), data: packet.raw }).unwrap();
2101        tx.send(Event::Shutdown).unwrap();
2102        driver.run();
2103
2104        assert_eq!(deliveries.lock().unwrap().len(), 1);
2105        assert_eq!(deliveries.lock().unwrap()[0], DestHash(dest));
2106    }
2107
2108    #[test]
2109    fn query_transport_identity() {
2110        let (tx, rx) = event::channel();
2111        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2112        let mut driver = Driver::new(
2113            TransportConfig { transport_enabled: true, identity_hash: Some([0xAA; 16]) },
2114            rx,
2115            Box::new(cbs),
2116        );
2117
2118        let (resp_tx, resp_rx) = mpsc::channel();
2119        tx.send(Event::Query(QueryRequest::TransportIdentity, resp_tx)).unwrap();
2120        tx.send(Event::Shutdown).unwrap();
2121        driver.run();
2122
2123        match resp_rx.recv().unwrap() {
2124            QueryResponse::TransportIdentity(Some(hash)) => {
2125                assert_eq!(hash, [0xAA; 16]);
2126            }
2127            _ => panic!("unexpected response"),
2128        }
2129    }
2130
2131    #[test]
2132    fn query_link_count() {
2133        let (tx, rx) = event::channel();
2134        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2135        let mut driver = Driver::new(
2136            TransportConfig { transport_enabled: false, identity_hash: None },
2137            rx,
2138            Box::new(cbs),
2139        );
2140
2141        let (resp_tx, resp_rx) = mpsc::channel();
2142        tx.send(Event::Query(QueryRequest::LinkCount, resp_tx)).unwrap();
2143        tx.send(Event::Shutdown).unwrap();
2144        driver.run();
2145
2146        match resp_rx.recv().unwrap() {
2147            QueryResponse::LinkCount(count) => assert_eq!(count, 0),
2148            _ => panic!("unexpected response"),
2149        }
2150    }
2151
2152    #[test]
2153    fn query_rate_table() {
2154        let (tx, rx) = event::channel();
2155        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2156        let mut driver = Driver::new(
2157            TransportConfig { transport_enabled: false, identity_hash: None },
2158            rx,
2159            Box::new(cbs),
2160        );
2161
2162        let (resp_tx, resp_rx) = mpsc::channel();
2163        tx.send(Event::Query(QueryRequest::RateTable, resp_tx)).unwrap();
2164        tx.send(Event::Shutdown).unwrap();
2165        driver.run();
2166
2167        match resp_rx.recv().unwrap() {
2168            QueryResponse::RateTable(entries) => assert!(entries.is_empty()),
2169            _ => panic!("unexpected response"),
2170        }
2171    }
2172
2173    #[test]
2174    fn query_next_hop() {
2175        let (tx, rx) = event::channel();
2176        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2177        let mut driver = Driver::new(
2178            TransportConfig { transport_enabled: false, identity_hash: None },
2179            rx,
2180            Box::new(cbs),
2181        );
2182
2183        let dest = [0xBB; 16];
2184        let (resp_tx, resp_rx) = mpsc::channel();
2185        tx.send(Event::Query(QueryRequest::NextHop { dest_hash: dest }, resp_tx)).unwrap();
2186        tx.send(Event::Shutdown).unwrap();
2187        driver.run();
2188
2189        match resp_rx.recv().unwrap() {
2190            QueryResponse::NextHop(None) => {}
2191            _ => panic!("unexpected response"),
2192        }
2193    }
2194
2195    #[test]
2196    fn query_next_hop_if_name() {
2197        let (tx, rx) = event::channel();
2198        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2199        let mut driver = Driver::new(
2200            TransportConfig { transport_enabled: false, identity_hash: None },
2201            rx,
2202            Box::new(cbs),
2203        );
2204
2205        let dest = [0xCC; 16];
2206        let (resp_tx, resp_rx) = mpsc::channel();
2207        tx.send(Event::Query(QueryRequest::NextHopIfName { dest_hash: dest }, resp_tx)).unwrap();
2208        tx.send(Event::Shutdown).unwrap();
2209        driver.run();
2210
2211        match resp_rx.recv().unwrap() {
2212            QueryResponse::NextHopIfName(None) => {}
2213            _ => panic!("unexpected response"),
2214        }
2215    }
2216
2217    #[test]
2218    fn query_drop_all_via() {
2219        let (tx, rx) = event::channel();
2220        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2221        let mut driver = Driver::new(
2222            TransportConfig { transport_enabled: false, identity_hash: None },
2223            rx,
2224            Box::new(cbs),
2225        );
2226
2227        let transport = [0xDD; 16];
2228        let (resp_tx, resp_rx) = mpsc::channel();
2229        tx.send(Event::Query(
2230            QueryRequest::DropAllVia { transport_hash: transport },
2231            resp_tx,
2232        )).unwrap();
2233        tx.send(Event::Shutdown).unwrap();
2234        driver.run();
2235
2236        match resp_rx.recv().unwrap() {
2237            QueryResponse::DropAllVia(count) => assert_eq!(count, 0),
2238            _ => panic!("unexpected response"),
2239        }
2240    }
2241
2242    #[test]
2243    fn query_drop_announce_queues() {
2244        let (tx, rx) = event::channel();
2245        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2246        let mut driver = Driver::new(
2247            TransportConfig { transport_enabled: false, identity_hash: None },
2248            rx,
2249            Box::new(cbs),
2250        );
2251
2252        let (resp_tx, resp_rx) = mpsc::channel();
2253        tx.send(Event::Query(QueryRequest::DropAnnounceQueues, resp_tx)).unwrap();
2254        tx.send(Event::Shutdown).unwrap();
2255        driver.run();
2256
2257        match resp_rx.recv().unwrap() {
2258            QueryResponse::DropAnnounceQueues => {}
2259            _ => panic!("unexpected response"),
2260        }
2261    }
2262
2263    // =========================================================================
2264    // Phase 7e: Link wiring integration tests
2265    // =========================================================================
2266
2267    #[test]
2268    fn register_link_dest_event() {
2269        let (tx, rx) = event::channel();
2270        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2271        let mut driver = Driver::new(
2272            TransportConfig { transport_enabled: false, identity_hash: None },
2273            rx,
2274            Box::new(cbs),
2275        );
2276        let info = make_interface_info(1);
2277        driver.engine.register_interface(info);
2278        let (writer, _sent) = MockWriter::new();
2279        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
2280
2281        let mut rng = OsRng;
2282        let sig_prv = rns_crypto::ed25519::Ed25519PrivateKey::generate(&mut rng);
2283        let sig_pub_bytes = sig_prv.public_key().public_bytes();
2284        let sig_prv_bytes = sig_prv.private_bytes();
2285        let dest_hash = [0xDD; 16];
2286
2287        tx.send(Event::RegisterLinkDestination {
2288            dest_hash,
2289            sig_prv_bytes,
2290            sig_pub_bytes,
2291        }).unwrap();
2292        tx.send(Event::Shutdown).unwrap();
2293        driver.run();
2294
2295        // Link manager should know about the destination
2296        assert!(driver.link_manager.is_link_destination(&dest_hash));
2297    }
2298
2299    #[test]
2300    fn create_link_event() {
2301        let (tx, rx) = event::channel();
2302        let (cbs, _link_established, _, _) = MockCallbacks::with_link_tracking();
2303        let mut driver = Driver::new(
2304            TransportConfig { transport_enabled: false, identity_hash: None },
2305            rx,
2306            Box::new(cbs),
2307        );
2308        let info = make_interface_info(1);
2309        driver.engine.register_interface(info);
2310        let (writer, _sent) = MockWriter::new();
2311        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
2312
2313        let dest_hash = [0xDD; 16];
2314        let dummy_sig_pub = [0xAA; 32];
2315
2316        let (resp_tx, resp_rx) = mpsc::channel();
2317        tx.send(Event::CreateLink {
2318            dest_hash,
2319            dest_sig_pub_bytes: dummy_sig_pub,
2320            response_tx: resp_tx,
2321        }).unwrap();
2322        tx.send(Event::Shutdown).unwrap();
2323        driver.run();
2324
2325        // Should have received a link_id
2326        let link_id = resp_rx.recv().unwrap();
2327        assert_ne!(link_id, [0u8; 16]);
2328
2329        // Link should be in pending state in the manager
2330        assert_eq!(driver.link_manager.link_count(), 1);
2331
2332        // The LINKREQUEST packet won't be sent on the wire without a path
2333        // to the destination (DESTINATION_LINK requires a known path or
2334        // attached_interface). In a real scenario, the path would exist from
2335        // an announce received earlier.
2336    }
2337
2338    #[test]
2339    fn deliver_local_routes_to_link_manager() {
2340        // Verify that DeliverLocal for a registered link destination goes to
2341        // the link manager instead of the callbacks.
2342        let (tx, rx) = event::channel();
2343        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2344        let mut driver = Driver::new(
2345            TransportConfig { transport_enabled: false, identity_hash: None },
2346            rx,
2347            Box::new(cbs),
2348        );
2349        let info = make_interface_info(1);
2350        driver.engine.register_interface(info);
2351        let (writer, _sent) = MockWriter::new();
2352        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
2353
2354        // Register a link destination
2355        let mut rng = OsRng;
2356        let sig_prv = rns_crypto::ed25519::Ed25519PrivateKey::generate(&mut rng);
2357        let sig_pub_bytes = sig_prv.public_key().public_bytes();
2358        let dest_hash = [0xEE; 16];
2359        driver.link_manager.register_link_destination(dest_hash, sig_prv, sig_pub_bytes);
2360
2361        // dispatch_all with a DeliverLocal for that dest should route to link_manager
2362        // (not to callbacks). We can't easily test this via run() since we need
2363        // a valid LINKREQUEST, but we can check is_link_destination works.
2364        assert!(driver.link_manager.is_link_destination(&dest_hash));
2365
2366        // Non-link destination should go to callbacks
2367        assert!(!driver.link_manager.is_link_destination(&[0xFF; 16]));
2368
2369        drop(tx);
2370    }
2371
2372    #[test]
2373    fn teardown_link_event() {
2374        let (tx, rx) = event::channel();
2375        let (cbs, _, link_closed, _) = MockCallbacks::with_link_tracking();
2376        let mut driver = Driver::new(
2377            TransportConfig { transport_enabled: false, identity_hash: None },
2378            rx,
2379            Box::new(cbs),
2380        );
2381        let info = make_interface_info(1);
2382        driver.engine.register_interface(info);
2383        let (writer, _sent) = MockWriter::new();
2384        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
2385
2386        // Create a link first
2387        let (resp_tx, resp_rx) = mpsc::channel();
2388        tx.send(Event::CreateLink {
2389            dest_hash: [0xDD; 16],
2390            dest_sig_pub_bytes: [0xAA; 32],
2391            response_tx: resp_tx,
2392        }).unwrap();
2393        // Then tear it down
2394        // We can't receive resp_rx yet since driver.run() hasn't started,
2395        // but we know the link_id will be created. Send teardown after CreateLink.
2396        // Actually, we need to get the link_id first. Let's use a two-phase approach.
2397        tx.send(Event::Shutdown).unwrap();
2398        driver.run();
2399
2400        let link_id = resp_rx.recv().unwrap();
2401        assert_ne!(link_id, [0u8; 16]);
2402        assert_eq!(driver.link_manager.link_count(), 1);
2403
2404        // Now restart with same driver (just use events directly since driver loop exited)
2405        let teardown_actions = driver.link_manager.teardown_link(&link_id);
2406        driver.dispatch_link_actions(teardown_actions);
2407
2408        // Callback should have been called
2409        assert_eq!(link_closed.lock().unwrap().len(), 1);
2410        assert_eq!(link_closed.lock().unwrap()[0], TypedLinkId(link_id));
2411    }
2412
2413    #[test]
2414    fn link_count_includes_link_manager() {
2415        let (tx, rx) = event::channel();
2416        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2417        let mut driver = Driver::new(
2418            TransportConfig { transport_enabled: false, identity_hash: None },
2419            rx,
2420            Box::new(cbs),
2421        );
2422        let info = make_interface_info(1);
2423        driver.engine.register_interface(info);
2424        let (writer, _sent) = MockWriter::new();
2425        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
2426
2427        // Create a link via link_manager directly
2428        let mut rng = OsRng;
2429        let dummy_sig = [0xAA; 32];
2430        driver.link_manager.create_link(&[0xDD; 16], &dummy_sig, 1, &mut rng);
2431
2432        // Query link count — should include link_manager links
2433        let (resp_tx, resp_rx) = mpsc::channel();
2434        tx.send(Event::Query(QueryRequest::LinkCount, resp_tx)).unwrap();
2435        tx.send(Event::Shutdown).unwrap();
2436        driver.run();
2437
2438        match resp_rx.recv().unwrap() {
2439            QueryResponse::LinkCount(count) => assert_eq!(count, 1),
2440            _ => panic!("unexpected response"),
2441        }
2442    }
2443
2444    #[test]
2445    fn register_request_handler_event() {
2446        let (tx, rx) = event::channel();
2447        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2448        let mut driver = Driver::new(
2449            TransportConfig { transport_enabled: false, identity_hash: None },
2450            rx,
2451            Box::new(cbs),
2452        );
2453
2454        tx.send(Event::RegisterRequestHandler {
2455            path: "/status".to_string(),
2456            allowed_list: None,
2457            handler: Box::new(|_link_id, _path, _data, _remote| Some(b"OK".to_vec())),
2458        }).unwrap();
2459        tx.send(Event::Shutdown).unwrap();
2460        driver.run();
2461
2462        // Handler should be registered (we can't directly query the count,
2463        // but at least verify no crash)
2464    }
2465
2466    // Phase 8c: Management announce timing tests
2467
2468    #[test]
2469    fn management_announces_emitted_after_delay() {
2470        let (tx, rx) = event::channel();
2471        let (cbs, announces, _, _, _, _) = MockCallbacks::new();
2472        let identity = Identity::new(&mut OsRng);
2473        let identity_hash = *identity.hash();
2474        let mut driver = Driver::new(
2475            TransportConfig { transport_enabled: true, identity_hash: Some(identity_hash) },
2476            rx,
2477            Box::new(cbs),
2478        );
2479
2480        // Register interface so announces can be sent
2481        let info = make_interface_info(1);
2482        driver.engine.register_interface(info.clone());
2483        let (writer, sent) = MockWriter::new();
2484        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
2485
2486        // Enable management announces
2487        driver.management_config.enable_remote_management = true;
2488        driver.transport_identity = Some(identity);
2489
2490        // Set started time to 10 seconds ago so the 5s delay has passed
2491        driver.started = time::now() - 10.0;
2492
2493        // Send Tick then Shutdown
2494        tx.send(Event::Tick).unwrap();
2495        tx.send(Event::Shutdown).unwrap();
2496        driver.run();
2497
2498        // Should have sent at least one packet (the management announce)
2499        let sent_packets = sent.lock().unwrap();
2500        assert!(!sent_packets.is_empty(),
2501            "Management announce should be sent after startup delay");
2502    }
2503
2504    #[test]
2505    fn management_announces_not_emitted_when_disabled() {
2506        let (tx, rx) = event::channel();
2507        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2508        let identity = Identity::new(&mut OsRng);
2509        let identity_hash = *identity.hash();
2510        let mut driver = Driver::new(
2511            TransportConfig { transport_enabled: true, identity_hash: Some(identity_hash) },
2512            rx,
2513            Box::new(cbs),
2514        );
2515
2516        let info = make_interface_info(1);
2517        driver.engine.register_interface(info.clone());
2518        let (writer, sent) = MockWriter::new();
2519        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
2520
2521        // Management announces disabled (default)
2522        driver.transport_identity = Some(identity);
2523        driver.started = time::now() - 10.0;
2524
2525        tx.send(Event::Tick).unwrap();
2526        tx.send(Event::Shutdown).unwrap();
2527        driver.run();
2528
2529        // Should NOT have sent any packets
2530        let sent_packets = sent.lock().unwrap();
2531        assert!(sent_packets.is_empty(),
2532            "No announces should be sent when management is disabled");
2533    }
2534
2535    #[test]
2536    fn management_announces_not_emitted_before_delay() {
2537        let (tx, rx) = event::channel();
2538        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2539        let identity = Identity::new(&mut OsRng);
2540        let identity_hash = *identity.hash();
2541        let mut driver = Driver::new(
2542            TransportConfig { transport_enabled: true, identity_hash: Some(identity_hash) },
2543            rx,
2544            Box::new(cbs),
2545        );
2546
2547        let info = make_interface_info(1);
2548        driver.engine.register_interface(info.clone());
2549        let (writer, sent) = MockWriter::new();
2550        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
2551
2552        driver.management_config.enable_remote_management = true;
2553        driver.transport_identity = Some(identity);
2554        // Started just now - delay hasn't passed
2555        driver.started = time::now();
2556
2557        tx.send(Event::Tick).unwrap();
2558        tx.send(Event::Shutdown).unwrap();
2559        driver.run();
2560
2561        let sent_packets = sent.lock().unwrap();
2562        assert!(sent_packets.is_empty(),
2563            "No announces before startup delay");
2564    }
2565
2566    // =========================================================================
2567    // Phase 9c: Announce + Discovery tests
2568    // =========================================================================
2569
2570    #[test]
2571    fn announce_received_populates_known_destinations() {
2572        let (tx, rx) = event::channel();
2573        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2574        let mut driver = Driver::new(
2575            TransportConfig { transport_enabled: false, identity_hash: None },
2576            rx,
2577            Box::new(cbs),
2578        );
2579        let info = make_interface_info(1);
2580        driver.engine.register_interface(info);
2581        let (writer, _sent) = MockWriter::new();
2582        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
2583
2584        let identity = Identity::new(&mut OsRng);
2585        let announce_raw = build_announce_packet(&identity);
2586
2587        let dest_hash = rns_core::destination::destination_hash(
2588            "test", &["app"], Some(identity.hash()),
2589        );
2590
2591        tx.send(Event::Frame { interface_id: InterfaceId(1), data: announce_raw }).unwrap();
2592        tx.send(Event::Shutdown).unwrap();
2593        driver.run();
2594
2595        // known_destinations should be populated
2596        assert!(driver.known_destinations.contains_key(&dest_hash));
2597        let recalled = &driver.known_destinations[&dest_hash];
2598        assert_eq!(recalled.dest_hash.0, dest_hash);
2599        assert_eq!(recalled.identity_hash.0, *identity.hash());
2600        assert_eq!(&recalled.public_key, &identity.get_public_key().unwrap());
2601        assert_eq!(recalled.hops, 1);
2602    }
2603
2604    #[test]
2605    fn query_has_path() {
2606        let (tx, rx) = event::channel();
2607        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2608        let mut driver = Driver::new(
2609            TransportConfig { transport_enabled: false, identity_hash: None },
2610            rx,
2611            Box::new(cbs),
2612        );
2613        let info = make_interface_info(1);
2614        driver.engine.register_interface(info);
2615        let (writer, _sent) = MockWriter::new();
2616        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
2617
2618        // No path yet
2619        let (resp_tx, resp_rx) = mpsc::channel();
2620        tx.send(Event::Query(QueryRequest::HasPath { dest_hash: [0xAA; 16] }, resp_tx)).unwrap();
2621
2622        // Feed an announce to create a path
2623        let identity = Identity::new(&mut OsRng);
2624        let announce_raw = build_announce_packet(&identity);
2625        let dest_hash = rns_core::destination::destination_hash(
2626            "test", &["app"], Some(identity.hash()),
2627        );
2628        tx.send(Event::Frame { interface_id: InterfaceId(1), data: announce_raw }).unwrap();
2629
2630        let (resp_tx2, resp_rx2) = mpsc::channel();
2631        tx.send(Event::Query(QueryRequest::HasPath { dest_hash }, resp_tx2)).unwrap();
2632
2633        tx.send(Event::Shutdown).unwrap();
2634        driver.run();
2635
2636        // First query — no path
2637        match resp_rx.recv().unwrap() {
2638            QueryResponse::HasPath(false) => {}
2639            other => panic!("expected HasPath(false), got {:?}", other),
2640        }
2641
2642        // Second query — path exists
2643        match resp_rx2.recv().unwrap() {
2644            QueryResponse::HasPath(true) => {}
2645            other => panic!("expected HasPath(true), got {:?}", other),
2646        }
2647    }
2648
2649    #[test]
2650    fn query_hops_to() {
2651        let (tx, rx) = event::channel();
2652        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2653        let mut driver = Driver::new(
2654            TransportConfig { transport_enabled: false, identity_hash: None },
2655            rx,
2656            Box::new(cbs),
2657        );
2658        let info = make_interface_info(1);
2659        driver.engine.register_interface(info);
2660        let (writer, _sent) = MockWriter::new();
2661        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
2662
2663        // Feed an announce
2664        let identity = Identity::new(&mut OsRng);
2665        let announce_raw = build_announce_packet(&identity);
2666        let dest_hash = rns_core::destination::destination_hash(
2667            "test", &["app"], Some(identity.hash()),
2668        );
2669
2670        tx.send(Event::Frame { interface_id: InterfaceId(1), data: announce_raw }).unwrap();
2671
2672        let (resp_tx, resp_rx) = mpsc::channel();
2673        tx.send(Event::Query(QueryRequest::HopsTo { dest_hash }, resp_tx)).unwrap();
2674        tx.send(Event::Shutdown).unwrap();
2675        driver.run();
2676
2677        match resp_rx.recv().unwrap() {
2678            QueryResponse::HopsTo(Some(1)) => {}
2679            other => panic!("expected HopsTo(Some(1)), got {:?}", other),
2680        }
2681    }
2682
2683    #[test]
2684    fn query_recall_identity() {
2685        let (tx, rx) = event::channel();
2686        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2687        let mut driver = Driver::new(
2688            TransportConfig { transport_enabled: false, identity_hash: None },
2689            rx,
2690            Box::new(cbs),
2691        );
2692        let info = make_interface_info(1);
2693        driver.engine.register_interface(info);
2694        let (writer, _sent) = MockWriter::new();
2695        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
2696
2697        let identity = Identity::new(&mut OsRng);
2698        let announce_raw = build_announce_packet(&identity);
2699        let dest_hash = rns_core::destination::destination_hash(
2700            "test", &["app"], Some(identity.hash()),
2701        );
2702
2703        tx.send(Event::Frame { interface_id: InterfaceId(1), data: announce_raw }).unwrap();
2704
2705        // Recall identity
2706        let (resp_tx, resp_rx) = mpsc::channel();
2707        tx.send(Event::Query(QueryRequest::RecallIdentity { dest_hash }, resp_tx)).unwrap();
2708
2709        // Also recall unknown destination
2710        let (resp_tx2, resp_rx2) = mpsc::channel();
2711        tx.send(Event::Query(QueryRequest::RecallIdentity { dest_hash: [0xFF; 16] }, resp_tx2)).unwrap();
2712
2713        tx.send(Event::Shutdown).unwrap();
2714        driver.run();
2715
2716        match resp_rx.recv().unwrap() {
2717            QueryResponse::RecallIdentity(Some(recalled)) => {
2718                assert_eq!(recalled.dest_hash.0, dest_hash);
2719                assert_eq!(recalled.identity_hash.0, *identity.hash());
2720                assert_eq!(recalled.public_key, identity.get_public_key().unwrap());
2721                assert_eq!(recalled.hops, 1);
2722            }
2723            other => panic!("expected RecallIdentity(Some(..)), got {:?}", other),
2724        }
2725
2726        match resp_rx2.recv().unwrap() {
2727            QueryResponse::RecallIdentity(None) => {}
2728            other => panic!("expected RecallIdentity(None), got {:?}", other),
2729        }
2730    }
2731
2732    #[test]
2733    fn request_path_sends_packet() {
2734        let (tx, rx) = event::channel();
2735        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2736        let mut driver = Driver::new(
2737            TransportConfig { transport_enabled: false, identity_hash: None },
2738            rx,
2739            Box::new(cbs),
2740        );
2741        let info = make_interface_info(1);
2742        driver.engine.register_interface(info);
2743        let (writer, sent) = MockWriter::new();
2744        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
2745
2746        // Send path request
2747        tx.send(Event::RequestPath { dest_hash: [0xAA; 16] }).unwrap();
2748        tx.send(Event::Shutdown).unwrap();
2749        driver.run();
2750
2751        // Should have sent a packet on the wire (broadcast)
2752        let sent_packets = sent.lock().unwrap();
2753        assert!(!sent_packets.is_empty(), "Path request should be sent on wire");
2754
2755        // Verify the sent packet is a DATA PLAIN BROADCAST packet
2756        let raw = &sent_packets[0];
2757        let flags = rns_core::packet::PacketFlags::unpack(raw[0] & 0x7F);
2758        assert_eq!(flags.packet_type, constants::PACKET_TYPE_DATA);
2759        assert_eq!(flags.destination_type, constants::DESTINATION_PLAIN);
2760        assert_eq!(flags.transport_type, constants::TRANSPORT_BROADCAST);
2761    }
2762
2763    #[test]
2764    fn request_path_includes_transport_id() {
2765        let (tx, rx) = event::channel();
2766        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2767        let mut driver = Driver::new(
2768            TransportConfig { transport_enabled: true, identity_hash: Some([0xBB; 16]) },
2769            rx,
2770            Box::new(cbs),
2771        );
2772        let info = make_interface_info(1);
2773        driver.engine.register_interface(info);
2774        let (writer, sent) = MockWriter::new();
2775        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
2776
2777        tx.send(Event::RequestPath { dest_hash: [0xAA; 16] }).unwrap();
2778        tx.send(Event::Shutdown).unwrap();
2779        driver.run();
2780
2781        let sent_packets = sent.lock().unwrap();
2782        assert!(!sent_packets.is_empty());
2783
2784        // Unpack the packet to check data length includes transport_id
2785        let raw = &sent_packets[0];
2786        if let Ok(packet) = RawPacket::unpack(raw) {
2787            // Data: dest_hash(16) + transport_id(16) + random_tag(16) = 48 bytes
2788            assert_eq!(packet.data.len(), 48, "Path request data should be 48 bytes with transport_id");
2789            assert_eq!(&packet.data[..16], &[0xAA; 16], "First 16 bytes should be dest_hash");
2790            assert_eq!(&packet.data[16..32], &[0xBB; 16], "Next 16 bytes should be transport_id");
2791        } else {
2792            panic!("Could not unpack sent packet");
2793        }
2794    }
2795
2796    #[test]
2797    fn path_request_dest_registered() {
2798        let (tx, rx) = event::channel();
2799        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2800        let driver = Driver::new(
2801            TransportConfig { transport_enabled: false, identity_hash: None },
2802            rx,
2803            Box::new(cbs),
2804        );
2805
2806        // The path request dest should be registered as a local PLAIN destination
2807        let expected_dest = rns_core::destination::destination_hash(
2808            "rnstransport", &["path", "request"], None,
2809        );
2810        assert_eq!(driver.path_request_dest, expected_dest);
2811
2812        drop(tx);
2813    }
2814
2815    // =========================================================================
2816    // Phase 9d: send_packet + proofs tests
2817    // =========================================================================
2818
2819    #[test]
2820    fn register_proof_strategy_event() {
2821        let (tx, rx) = event::channel();
2822        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2823        let mut driver = Driver::new(
2824            TransportConfig { transport_enabled: false, identity_hash: None },
2825            rx,
2826            Box::new(cbs),
2827        );
2828
2829        let dest = [0xAA; 16];
2830        let identity = Identity::new(&mut OsRng);
2831        let prv_key = identity.get_private_key().unwrap();
2832
2833        tx.send(Event::RegisterProofStrategy {
2834            dest_hash: dest,
2835            strategy: rns_core::types::ProofStrategy::ProveAll,
2836            signing_key: Some(prv_key),
2837        }).unwrap();
2838        tx.send(Event::Shutdown).unwrap();
2839        driver.run();
2840
2841        assert!(driver.proof_strategies.contains_key(&dest));
2842        let (strategy, ref id_opt) = driver.proof_strategies[&dest];
2843        assert_eq!(strategy, rns_core::types::ProofStrategy::ProveAll);
2844        assert!(id_opt.is_some());
2845    }
2846
2847    #[test]
2848    fn register_proof_strategy_prove_none_no_identity() {
2849        let (tx, rx) = event::channel();
2850        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2851        let mut driver = Driver::new(
2852            TransportConfig { transport_enabled: false, identity_hash: None },
2853            rx,
2854            Box::new(cbs),
2855        );
2856
2857        let dest = [0xBB; 16];
2858        tx.send(Event::RegisterProofStrategy {
2859            dest_hash: dest,
2860            strategy: rns_core::types::ProofStrategy::ProveNone,
2861            signing_key: None,
2862        }).unwrap();
2863        tx.send(Event::Shutdown).unwrap();
2864        driver.run();
2865
2866        assert!(driver.proof_strategies.contains_key(&dest));
2867        let (strategy, ref id_opt) = driver.proof_strategies[&dest];
2868        assert_eq!(strategy, rns_core::types::ProofStrategy::ProveNone);
2869        assert!(id_opt.is_none());
2870    }
2871
2872    #[test]
2873    fn send_outbound_tracks_sent_packets() {
2874        let (tx, rx) = event::channel();
2875        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2876        let mut driver = Driver::new(
2877            TransportConfig { transport_enabled: false, identity_hash: None },
2878            rx,
2879            Box::new(cbs),
2880        );
2881        let info = make_interface_info(1);
2882        driver.engine.register_interface(info);
2883        let (writer, _sent) = MockWriter::new();
2884        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
2885
2886        // Build a DATA packet
2887        let dest = [0xCC; 16];
2888        let flags = PacketFlags {
2889            header_type: constants::HEADER_1,
2890            context_flag: constants::FLAG_UNSET,
2891            transport_type: constants::TRANSPORT_BROADCAST,
2892            destination_type: constants::DESTINATION_PLAIN,
2893            packet_type: constants::PACKET_TYPE_DATA,
2894        };
2895        let packet = RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"test data").unwrap();
2896        let expected_hash = packet.packet_hash;
2897
2898        tx.send(Event::SendOutbound {
2899            raw: packet.raw,
2900            dest_type: constants::DESTINATION_PLAIN,
2901            attached_interface: None,
2902        }).unwrap();
2903        tx.send(Event::Shutdown).unwrap();
2904        driver.run();
2905
2906        // Should be tracking the sent packet
2907        assert!(driver.sent_packets.contains_key(&expected_hash));
2908        let (tracked_dest, _sent_time) = &driver.sent_packets[&expected_hash];
2909        assert_eq!(tracked_dest, &dest);
2910    }
2911
2912    #[test]
2913    fn prove_all_generates_proof_on_delivery() {
2914        let (tx, rx) = event::channel();
2915        let (cbs, _, _, deliveries, _, _) = MockCallbacks::new();
2916        let mut driver = Driver::new(
2917            TransportConfig { transport_enabled: false, identity_hash: None },
2918            rx,
2919            Box::new(cbs),
2920        );
2921        let info = make_interface_info(1);
2922        driver.engine.register_interface(info);
2923        let (writer, sent) = MockWriter::new();
2924        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
2925
2926        // Register a destination with ProveAll
2927        let dest = [0xDD; 16];
2928        let identity = Identity::new(&mut OsRng);
2929        let prv_key = identity.get_private_key().unwrap();
2930        driver.engine.register_destination(dest, constants::DESTINATION_SINGLE);
2931        driver.proof_strategies.insert(dest, (
2932            rns_core::types::ProofStrategy::ProveAll,
2933            Some(Identity::from_private_key(&prv_key)),
2934        ));
2935
2936        // Send a DATA packet to that destination
2937        let flags = PacketFlags {
2938            header_type: constants::HEADER_1,
2939            context_flag: constants::FLAG_UNSET,
2940            transport_type: constants::TRANSPORT_BROADCAST,
2941            destination_type: constants::DESTINATION_SINGLE,
2942            packet_type: constants::PACKET_TYPE_DATA,
2943        };
2944        let packet = RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"hello").unwrap();
2945
2946        tx.send(Event::Frame { interface_id: InterfaceId(1), data: packet.raw }).unwrap();
2947        tx.send(Event::Shutdown).unwrap();
2948        driver.run();
2949
2950        // Should have delivered the packet
2951        assert_eq!(deliveries.lock().unwrap().len(), 1);
2952
2953        // Should have sent at least one proof packet on the wire
2954        let sent_packets = sent.lock().unwrap();
2955        // The original DATA is not sent out (it was delivered locally), but a PROOF should be
2956        let has_proof = sent_packets.iter().any(|raw| {
2957            let flags = PacketFlags::unpack(raw[0] & 0x7F);
2958            flags.packet_type == constants::PACKET_TYPE_PROOF
2959        });
2960        assert!(has_proof, "ProveAll should generate a proof packet: sent {} packets", sent_packets.len());
2961    }
2962
2963    #[test]
2964    fn prove_none_does_not_generate_proof() {
2965        let (tx, rx) = event::channel();
2966        let (cbs, _, _, deliveries, _, _) = MockCallbacks::new();
2967        let mut driver = Driver::new(
2968            TransportConfig { transport_enabled: false, identity_hash: None },
2969            rx,
2970            Box::new(cbs),
2971        );
2972        let info = make_interface_info(1);
2973        driver.engine.register_interface(info);
2974        let (writer, sent) = MockWriter::new();
2975        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
2976
2977        // Register a destination with ProveNone
2978        let dest = [0xDD; 16];
2979        driver.engine.register_destination(dest, constants::DESTINATION_SINGLE);
2980        driver.proof_strategies.insert(dest, (
2981            rns_core::types::ProofStrategy::ProveNone,
2982            None,
2983        ));
2984
2985        // Send a DATA packet to that destination
2986        let flags = PacketFlags {
2987            header_type: constants::HEADER_1,
2988            context_flag: constants::FLAG_UNSET,
2989            transport_type: constants::TRANSPORT_BROADCAST,
2990            destination_type: constants::DESTINATION_SINGLE,
2991            packet_type: constants::PACKET_TYPE_DATA,
2992        };
2993        let packet = RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"hello").unwrap();
2994
2995        tx.send(Event::Frame { interface_id: InterfaceId(1), data: packet.raw }).unwrap();
2996        tx.send(Event::Shutdown).unwrap();
2997        driver.run();
2998
2999        // Should have delivered the packet
3000        assert_eq!(deliveries.lock().unwrap().len(), 1);
3001
3002        // Should NOT have sent any proof
3003        let sent_packets = sent.lock().unwrap();
3004        let has_proof = sent_packets.iter().any(|raw| {
3005            let flags = PacketFlags::unpack(raw[0] & 0x7F);
3006            flags.packet_type == constants::PACKET_TYPE_PROOF
3007        });
3008        assert!(!has_proof, "ProveNone should not generate a proof packet");
3009    }
3010
3011    #[test]
3012    fn no_proof_strategy_does_not_generate_proof() {
3013        let (tx, rx) = event::channel();
3014        let (cbs, _, _, deliveries, _, _) = MockCallbacks::new();
3015        let mut driver = Driver::new(
3016            TransportConfig { transport_enabled: false, identity_hash: None },
3017            rx,
3018            Box::new(cbs),
3019        );
3020        let info = make_interface_info(1);
3021        driver.engine.register_interface(info);
3022        let (writer, sent) = MockWriter::new();
3023        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
3024
3025        // Register destination but NO proof strategy
3026        let dest = [0xDD; 16];
3027        driver.engine.register_destination(dest, constants::DESTINATION_SINGLE);
3028
3029        let flags = PacketFlags {
3030            header_type: constants::HEADER_1,
3031            context_flag: constants::FLAG_UNSET,
3032            transport_type: constants::TRANSPORT_BROADCAST,
3033            destination_type: constants::DESTINATION_SINGLE,
3034            packet_type: constants::PACKET_TYPE_DATA,
3035        };
3036        let packet = RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"hello").unwrap();
3037
3038        tx.send(Event::Frame { interface_id: InterfaceId(1), data: packet.raw }).unwrap();
3039        tx.send(Event::Shutdown).unwrap();
3040        driver.run();
3041
3042        assert_eq!(deliveries.lock().unwrap().len(), 1);
3043
3044        let sent_packets = sent.lock().unwrap();
3045        let has_proof = sent_packets.iter().any(|raw| {
3046            let flags = PacketFlags::unpack(raw[0] & 0x7F);
3047            flags.packet_type == constants::PACKET_TYPE_PROOF
3048        });
3049        assert!(!has_proof, "No proof strategy means no proof generated");
3050    }
3051
3052    #[test]
3053    fn prove_app_calls_callback() {
3054        let (tx, rx) = event::channel();
3055        let proof_requested = Arc::new(Mutex::new(Vec::new()));
3056        let deliveries = Arc::new(Mutex::new(Vec::new()));
3057        let cbs = MockCallbacks {
3058            announces: Arc::new(Mutex::new(Vec::new())),
3059            paths: Arc::new(Mutex::new(Vec::new())),
3060            deliveries: deliveries.clone(),
3061            iface_ups: Arc::new(Mutex::new(Vec::new())),
3062            iface_downs: Arc::new(Mutex::new(Vec::new())),
3063            link_established: Arc::new(Mutex::new(Vec::new())),
3064            link_closed: Arc::new(Mutex::new(Vec::new())),
3065            remote_identified: Arc::new(Mutex::new(Vec::new())),
3066            resources_received: Arc::new(Mutex::new(Vec::new())),
3067            resource_completed: Arc::new(Mutex::new(Vec::new())),
3068            resource_failed: Arc::new(Mutex::new(Vec::new())),
3069            channel_messages: Arc::new(Mutex::new(Vec::new())),
3070            link_data: Arc::new(Mutex::new(Vec::new())),
3071            responses: Arc::new(Mutex::new(Vec::new())),
3072            proofs: Arc::new(Mutex::new(Vec::new())),
3073            proof_requested: proof_requested.clone(),
3074        };
3075
3076        let mut driver = Driver::new(
3077            TransportConfig { transport_enabled: false, identity_hash: None },
3078            rx,
3079            Box::new(cbs),
3080        );
3081        let info = make_interface_info(1);
3082        driver.engine.register_interface(info);
3083        let (writer, sent) = MockWriter::new();
3084        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
3085
3086        // Register dest with ProveApp
3087        let dest = [0xDD; 16];
3088        let identity = Identity::new(&mut OsRng);
3089        let prv_key = identity.get_private_key().unwrap();
3090        driver.engine.register_destination(dest, constants::DESTINATION_SINGLE);
3091        driver.proof_strategies.insert(dest, (
3092            rns_core::types::ProofStrategy::ProveApp,
3093            Some(Identity::from_private_key(&prv_key)),
3094        ));
3095
3096        let flags = PacketFlags {
3097            header_type: constants::HEADER_1,
3098            context_flag: constants::FLAG_UNSET,
3099            transport_type: constants::TRANSPORT_BROADCAST,
3100            destination_type: constants::DESTINATION_SINGLE,
3101            packet_type: constants::PACKET_TYPE_DATA,
3102        };
3103        let packet = RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"app test").unwrap();
3104
3105        tx.send(Event::Frame { interface_id: InterfaceId(1), data: packet.raw }).unwrap();
3106        tx.send(Event::Shutdown).unwrap();
3107        driver.run();
3108
3109        // on_proof_requested should have been called
3110        let prs = proof_requested.lock().unwrap();
3111        assert_eq!(prs.len(), 1);
3112        assert_eq!(prs[0].0, DestHash(dest));
3113
3114        // Since our mock returns true, a proof should also have been sent
3115        let sent_packets = sent.lock().unwrap();
3116        let has_proof = sent_packets.iter().any(|raw| {
3117            let flags = PacketFlags::unpack(raw[0] & 0x7F);
3118            flags.packet_type == constants::PACKET_TYPE_PROOF
3119        });
3120        assert!(has_proof, "ProveApp (callback returns true) should generate a proof");
3121    }
3122
3123    #[test]
3124    fn inbound_proof_fires_callback() {
3125        let (tx, rx) = event::channel();
3126        let proofs = Arc::new(Mutex::new(Vec::new()));
3127        let cbs = MockCallbacks {
3128            announces: Arc::new(Mutex::new(Vec::new())),
3129            paths: Arc::new(Mutex::new(Vec::new())),
3130            deliveries: Arc::new(Mutex::new(Vec::new())),
3131            iface_ups: Arc::new(Mutex::new(Vec::new())),
3132            iface_downs: Arc::new(Mutex::new(Vec::new())),
3133            link_established: Arc::new(Mutex::new(Vec::new())),
3134            link_closed: Arc::new(Mutex::new(Vec::new())),
3135            remote_identified: Arc::new(Mutex::new(Vec::new())),
3136            resources_received: Arc::new(Mutex::new(Vec::new())),
3137            resource_completed: Arc::new(Mutex::new(Vec::new())),
3138            resource_failed: Arc::new(Mutex::new(Vec::new())),
3139            channel_messages: Arc::new(Mutex::new(Vec::new())),
3140            link_data: Arc::new(Mutex::new(Vec::new())),
3141            responses: Arc::new(Mutex::new(Vec::new())),
3142            proofs: proofs.clone(),
3143            proof_requested: Arc::new(Mutex::new(Vec::new())),
3144        };
3145
3146        let mut driver = Driver::new(
3147            TransportConfig { transport_enabled: false, identity_hash: None },
3148            rx,
3149            Box::new(cbs),
3150        );
3151        let info = make_interface_info(1);
3152        driver.engine.register_interface(info);
3153        let (writer, _sent) = MockWriter::new();
3154        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
3155
3156        // Register a destination so proof packets can be delivered locally
3157        let dest = [0xEE; 16];
3158        driver.engine.register_destination(dest, constants::DESTINATION_SINGLE);
3159
3160        // Simulate a sent packet that we're tracking
3161        let tracked_hash = [0x42u8; 32];
3162        let sent_time = time::now() - 0.5; // 500ms ago
3163        driver.sent_packets.insert(tracked_hash, (dest, sent_time));
3164
3165        // Build a PROOF packet with the tracked hash + dummy signature
3166        let mut proof_data = Vec::new();
3167        proof_data.extend_from_slice(&tracked_hash);
3168        proof_data.extend_from_slice(&[0xAA; 64]); // dummy signature
3169
3170        let flags = PacketFlags {
3171            header_type: constants::HEADER_1,
3172            context_flag: constants::FLAG_UNSET,
3173            transport_type: constants::TRANSPORT_BROADCAST,
3174            destination_type: constants::DESTINATION_SINGLE,
3175            packet_type: constants::PACKET_TYPE_PROOF,
3176        };
3177        let packet = RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, &proof_data).unwrap();
3178
3179        tx.send(Event::Frame { interface_id: InterfaceId(1), data: packet.raw }).unwrap();
3180        tx.send(Event::Shutdown).unwrap();
3181        driver.run();
3182
3183        // on_proof callback should have been fired
3184        let proof_list = proofs.lock().unwrap();
3185        assert_eq!(proof_list.len(), 1);
3186        assert_eq!(proof_list[0].0, DestHash(dest));
3187        assert_eq!(proof_list[0].1, PacketHash(tracked_hash));
3188        assert!(proof_list[0].2 >= 0.4, "RTT should be approximately 0.5s, got {}", proof_list[0].2);
3189
3190        // Tracked packet should be removed
3191        assert!(!driver.sent_packets.contains_key(&tracked_hash));
3192    }
3193
3194    #[test]
3195    fn inbound_proof_for_unknown_packet_is_ignored() {
3196        let (tx, rx) = event::channel();
3197        let proofs = Arc::new(Mutex::new(Vec::new()));
3198        let cbs = MockCallbacks {
3199            announces: Arc::new(Mutex::new(Vec::new())),
3200            paths: Arc::new(Mutex::new(Vec::new())),
3201            deliveries: Arc::new(Mutex::new(Vec::new())),
3202            iface_ups: Arc::new(Mutex::new(Vec::new())),
3203            iface_downs: Arc::new(Mutex::new(Vec::new())),
3204            link_established: Arc::new(Mutex::new(Vec::new())),
3205            link_closed: Arc::new(Mutex::new(Vec::new())),
3206            remote_identified: Arc::new(Mutex::new(Vec::new())),
3207            resources_received: Arc::new(Mutex::new(Vec::new())),
3208            resource_completed: Arc::new(Mutex::new(Vec::new())),
3209            resource_failed: Arc::new(Mutex::new(Vec::new())),
3210            channel_messages: Arc::new(Mutex::new(Vec::new())),
3211            link_data: Arc::new(Mutex::new(Vec::new())),
3212            responses: Arc::new(Mutex::new(Vec::new())),
3213            proofs: proofs.clone(),
3214            proof_requested: Arc::new(Mutex::new(Vec::new())),
3215        };
3216
3217        let mut driver = Driver::new(
3218            TransportConfig { transport_enabled: false, identity_hash: None },
3219            rx,
3220            Box::new(cbs),
3221        );
3222        let info = make_interface_info(1);
3223        driver.engine.register_interface(info);
3224        let (writer, _sent) = MockWriter::new();
3225        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
3226
3227        let dest = [0xEE; 16];
3228        driver.engine.register_destination(dest, constants::DESTINATION_SINGLE);
3229
3230        // Build a PROOF packet for an untracked hash
3231        let unknown_hash = [0xFF; 32];
3232        let mut proof_data = Vec::new();
3233        proof_data.extend_from_slice(&unknown_hash);
3234        proof_data.extend_from_slice(&[0xAA; 64]);
3235
3236        let flags = PacketFlags {
3237            header_type: constants::HEADER_1,
3238            context_flag: constants::FLAG_UNSET,
3239            transport_type: constants::TRANSPORT_BROADCAST,
3240            destination_type: constants::DESTINATION_SINGLE,
3241            packet_type: constants::PACKET_TYPE_PROOF,
3242        };
3243        let packet = RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, &proof_data).unwrap();
3244
3245        tx.send(Event::Frame { interface_id: InterfaceId(1), data: packet.raw }).unwrap();
3246        tx.send(Event::Shutdown).unwrap();
3247        driver.run();
3248
3249        // on_proof should NOT have been called
3250        assert!(proofs.lock().unwrap().is_empty());
3251    }
3252
3253    #[test]
3254    fn inbound_proof_with_valid_signature_fires_callback() {
3255        // When the destination IS in known_destinations, the proof signature is verified
3256        let (tx, rx) = event::channel();
3257        let proofs = Arc::new(Mutex::new(Vec::new()));
3258        let cbs = MockCallbacks {
3259            announces: Arc::new(Mutex::new(Vec::new())),
3260            paths: Arc::new(Mutex::new(Vec::new())),
3261            deliveries: Arc::new(Mutex::new(Vec::new())),
3262            iface_ups: Arc::new(Mutex::new(Vec::new())),
3263            iface_downs: Arc::new(Mutex::new(Vec::new())),
3264            link_established: Arc::new(Mutex::new(Vec::new())),
3265            link_closed: Arc::new(Mutex::new(Vec::new())),
3266            remote_identified: Arc::new(Mutex::new(Vec::new())),
3267            resources_received: Arc::new(Mutex::new(Vec::new())),
3268            resource_completed: Arc::new(Mutex::new(Vec::new())),
3269            resource_failed: Arc::new(Mutex::new(Vec::new())),
3270            channel_messages: Arc::new(Mutex::new(Vec::new())),
3271            link_data: Arc::new(Mutex::new(Vec::new())),
3272            responses: Arc::new(Mutex::new(Vec::new())),
3273            proofs: proofs.clone(),
3274            proof_requested: Arc::new(Mutex::new(Vec::new())),
3275        };
3276
3277        let mut driver = Driver::new(
3278            TransportConfig { transport_enabled: false, identity_hash: None },
3279            rx,
3280            Box::new(cbs),
3281        );
3282        let info = make_interface_info(1);
3283        driver.engine.register_interface(info);
3284        let (writer, _sent) = MockWriter::new();
3285        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
3286
3287        let dest = [0xEE; 16];
3288        driver.engine.register_destination(dest, constants::DESTINATION_SINGLE);
3289
3290        // Create real identity and add to known_destinations
3291        let identity = Identity::new(&mut OsRng);
3292        let pub_key = identity.get_public_key();
3293        driver.known_destinations.insert(dest, crate::destination::AnnouncedIdentity {
3294            dest_hash: DestHash(dest),
3295            identity_hash: IdentityHash(*identity.hash()),
3296            public_key: pub_key.unwrap(),
3297            app_data: None,
3298            hops: 0,
3299            received_at: time::now(),
3300        });
3301
3302        // Sign a packet hash with the identity
3303        let tracked_hash = [0x42u8; 32];
3304        let sent_time = time::now() - 0.5;
3305        driver.sent_packets.insert(tracked_hash, (dest, sent_time));
3306
3307        let signature = identity.sign(&tracked_hash).unwrap();
3308        let mut proof_data = Vec::new();
3309        proof_data.extend_from_slice(&tracked_hash);
3310        proof_data.extend_from_slice(&signature);
3311
3312        let flags = PacketFlags {
3313            header_type: constants::HEADER_1,
3314            context_flag: constants::FLAG_UNSET,
3315            transport_type: constants::TRANSPORT_BROADCAST,
3316            destination_type: constants::DESTINATION_SINGLE,
3317            packet_type: constants::PACKET_TYPE_PROOF,
3318        };
3319        let packet = RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, &proof_data).unwrap();
3320
3321        tx.send(Event::Frame { interface_id: InterfaceId(1), data: packet.raw }).unwrap();
3322        tx.send(Event::Shutdown).unwrap();
3323        driver.run();
3324
3325        // Valid signature: on_proof should fire
3326        let proof_list = proofs.lock().unwrap();
3327        assert_eq!(proof_list.len(), 1);
3328        assert_eq!(proof_list[0].0, DestHash(dest));
3329        assert_eq!(proof_list[0].1, PacketHash(tracked_hash));
3330    }
3331
3332    #[test]
3333    fn inbound_proof_with_invalid_signature_rejected() {
3334        // When known_destinations has the public key, bad signatures are rejected
3335        let (tx, rx) = event::channel();
3336        let proofs = Arc::new(Mutex::new(Vec::new()));
3337        let cbs = MockCallbacks {
3338            announces: Arc::new(Mutex::new(Vec::new())),
3339            paths: Arc::new(Mutex::new(Vec::new())),
3340            deliveries: Arc::new(Mutex::new(Vec::new())),
3341            iface_ups: Arc::new(Mutex::new(Vec::new())),
3342            iface_downs: Arc::new(Mutex::new(Vec::new())),
3343            link_established: Arc::new(Mutex::new(Vec::new())),
3344            link_closed: Arc::new(Mutex::new(Vec::new())),
3345            remote_identified: Arc::new(Mutex::new(Vec::new())),
3346            resources_received: Arc::new(Mutex::new(Vec::new())),
3347            resource_completed: Arc::new(Mutex::new(Vec::new())),
3348            resource_failed: Arc::new(Mutex::new(Vec::new())),
3349            channel_messages: Arc::new(Mutex::new(Vec::new())),
3350            link_data: Arc::new(Mutex::new(Vec::new())),
3351            responses: Arc::new(Mutex::new(Vec::new())),
3352            proofs: proofs.clone(),
3353            proof_requested: Arc::new(Mutex::new(Vec::new())),
3354        };
3355
3356        let mut driver = Driver::new(
3357            TransportConfig { transport_enabled: false, identity_hash: None },
3358            rx,
3359            Box::new(cbs),
3360        );
3361        let info = make_interface_info(1);
3362        driver.engine.register_interface(info);
3363        let (writer, _sent) = MockWriter::new();
3364        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
3365
3366        let dest = [0xEE; 16];
3367        driver.engine.register_destination(dest, constants::DESTINATION_SINGLE);
3368
3369        // Create identity and add to known_destinations
3370        let identity = Identity::new(&mut OsRng);
3371        let pub_key = identity.get_public_key();
3372        driver.known_destinations.insert(dest, crate::destination::AnnouncedIdentity {
3373            dest_hash: DestHash(dest),
3374            identity_hash: IdentityHash(*identity.hash()),
3375            public_key: pub_key.unwrap(),
3376            app_data: None,
3377            hops: 0,
3378            received_at: time::now(),
3379        });
3380
3381        // Track a sent packet
3382        let tracked_hash = [0x42u8; 32];
3383        let sent_time = time::now() - 0.5;
3384        driver.sent_packets.insert(tracked_hash, (dest, sent_time));
3385
3386        // Use WRONG signature (all 0xAA — invalid for this identity)
3387        let mut proof_data = Vec::new();
3388        proof_data.extend_from_slice(&tracked_hash);
3389        proof_data.extend_from_slice(&[0xAA; 64]);
3390
3391        let flags = PacketFlags {
3392            header_type: constants::HEADER_1,
3393            context_flag: constants::FLAG_UNSET,
3394            transport_type: constants::TRANSPORT_BROADCAST,
3395            destination_type: constants::DESTINATION_SINGLE,
3396            packet_type: constants::PACKET_TYPE_PROOF,
3397        };
3398        let packet = RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, &proof_data).unwrap();
3399
3400        tx.send(Event::Frame { interface_id: InterfaceId(1), data: packet.raw }).unwrap();
3401        tx.send(Event::Shutdown).unwrap();
3402        driver.run();
3403
3404        // Invalid signature: on_proof should NOT fire
3405        assert!(proofs.lock().unwrap().is_empty());
3406    }
3407
3408    #[test]
3409    fn proof_data_is_valid_explicit_proof() {
3410        // Verify that the proof generated by ProveAll is a valid explicit proof
3411        let (tx, rx) = event::channel();
3412        let (cbs, _, _, _, _, _) = MockCallbacks::new();
3413        let mut driver = Driver::new(
3414            TransportConfig { transport_enabled: false, identity_hash: None },
3415            rx,
3416            Box::new(cbs),
3417        );
3418        let info = make_interface_info(1);
3419        driver.engine.register_interface(info);
3420        let (writer, sent) = MockWriter::new();
3421        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
3422
3423        let dest = [0xDD; 16];
3424        let identity = Identity::new(&mut OsRng);
3425        let prv_key = identity.get_private_key().unwrap();
3426        driver.engine.register_destination(dest, constants::DESTINATION_SINGLE);
3427        driver.proof_strategies.insert(dest, (
3428            rns_core::types::ProofStrategy::ProveAll,
3429            Some(Identity::from_private_key(&prv_key)),
3430        ));
3431
3432        let flags = PacketFlags {
3433            header_type: constants::HEADER_1,
3434            context_flag: constants::FLAG_UNSET,
3435            transport_type: constants::TRANSPORT_BROADCAST,
3436            destination_type: constants::DESTINATION_SINGLE,
3437            packet_type: constants::PACKET_TYPE_DATA,
3438        };
3439        let data_packet = RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"verify me").unwrap();
3440        let data_packet_hash = data_packet.packet_hash;
3441
3442        tx.send(Event::Frame { interface_id: InterfaceId(1), data: data_packet.raw }).unwrap();
3443        tx.send(Event::Shutdown).unwrap();
3444        driver.run();
3445
3446        // Find the proof packet in sent
3447        let sent_packets = sent.lock().unwrap();
3448        let proof_raw = sent_packets.iter().find(|raw| {
3449            let f = PacketFlags::unpack(raw[0] & 0x7F);
3450            f.packet_type == constants::PACKET_TYPE_PROOF
3451        });
3452        assert!(proof_raw.is_some(), "Should have sent a proof");
3453
3454        let proof_packet = RawPacket::unpack(proof_raw.unwrap()).unwrap();
3455        // Proof data should be 96 bytes: packet_hash(32) + signature(64)
3456        assert_eq!(proof_packet.data.len(), 96, "Explicit proof should be 96 bytes");
3457
3458        // Validate using rns-core's receipt module
3459        let result = rns_core::receipt::validate_proof(
3460            &proof_packet.data,
3461            &data_packet_hash,
3462            &Identity::from_private_key(&prv_key), // same identity
3463        );
3464        assert_eq!(result, rns_core::receipt::ProofResult::Valid);
3465    }
3466
3467    #[test]
3468    fn query_local_destinations_empty() {
3469        let (tx, rx) = event::channel();
3470        let (cbs, _, _, _, _, _) = MockCallbacks::new();
3471        let driver_config = TransportConfig { transport_enabled: false, identity_hash: None };
3472        let mut driver = Driver::new(driver_config, rx, Box::new(cbs));
3473
3474        let (resp_tx, resp_rx) = mpsc::channel();
3475        tx.send(Event::Query(QueryRequest::LocalDestinations, resp_tx)).unwrap();
3476        tx.send(Event::Shutdown).unwrap();
3477        driver.run();
3478
3479        match resp_rx.recv().unwrap() {
3480            QueryResponse::LocalDestinations(entries) => {
3481                // Should contain the two internal destinations (tunnel_synth + path_request)
3482                assert_eq!(entries.len(), 2);
3483                for entry in &entries {
3484                    assert_eq!(entry.dest_type, rns_core::constants::DESTINATION_PLAIN);
3485                }
3486            }
3487            other => panic!("expected LocalDestinations, got {:?}", other),
3488        }
3489    }
3490
3491    #[test]
3492    fn query_local_destinations_with_registered() {
3493        let (tx, rx) = event::channel();
3494        let (cbs, _, _, _, _, _) = MockCallbacks::new();
3495        let driver_config = TransportConfig { transport_enabled: false, identity_hash: None };
3496        let mut driver = Driver::new(driver_config, rx, Box::new(cbs));
3497
3498        let dest_hash = [0xAA; 16];
3499        tx.send(Event::RegisterDestination {
3500            dest_hash,
3501            dest_type: rns_core::constants::DESTINATION_SINGLE,
3502        }).unwrap();
3503
3504        let (resp_tx, resp_rx) = mpsc::channel();
3505        tx.send(Event::Query(QueryRequest::LocalDestinations, resp_tx)).unwrap();
3506        tx.send(Event::Shutdown).unwrap();
3507        driver.run();
3508
3509        match resp_rx.recv().unwrap() {
3510            QueryResponse::LocalDestinations(entries) => {
3511                // 2 internal + 1 registered
3512                assert_eq!(entries.len(), 3);
3513                assert!(entries.iter().any(|e| e.hash == dest_hash
3514                    && e.dest_type == rns_core::constants::DESTINATION_SINGLE));
3515            }
3516            other => panic!("expected LocalDestinations, got {:?}", other),
3517        }
3518    }
3519
3520    #[test]
3521    fn query_local_destinations_tracks_link_dest() {
3522        let (tx, rx) = event::channel();
3523        let (cbs, _, _, _, _, _) = MockCallbacks::new();
3524        let driver_config = TransportConfig { transport_enabled: false, identity_hash: None };
3525        let mut driver = Driver::new(driver_config, rx, Box::new(cbs));
3526
3527        let dest_hash = [0xBB; 16];
3528        tx.send(Event::RegisterLinkDestination {
3529            dest_hash,
3530            sig_prv_bytes: [0x11; 32],
3531            sig_pub_bytes: [0x22; 32],
3532        }).unwrap();
3533
3534        let (resp_tx, resp_rx) = mpsc::channel();
3535        tx.send(Event::Query(QueryRequest::LocalDestinations, resp_tx)).unwrap();
3536        tx.send(Event::Shutdown).unwrap();
3537        driver.run();
3538
3539        match resp_rx.recv().unwrap() {
3540            QueryResponse::LocalDestinations(entries) => {
3541                // 2 internal + 1 link destination
3542                assert_eq!(entries.len(), 3);
3543                assert!(entries.iter().any(|e| e.hash == dest_hash
3544                    && e.dest_type == rns_core::constants::DESTINATION_SINGLE));
3545            }
3546            other => panic!("expected LocalDestinations, got {:?}", other),
3547        }
3548    }
3549
3550    #[test]
3551    fn query_links_empty() {
3552        let (tx, rx) = event::channel();
3553        let (cbs, _, _, _, _, _) = MockCallbacks::new();
3554        let driver_config = TransportConfig { transport_enabled: false, identity_hash: None };
3555        let mut driver = Driver::new(driver_config, rx, Box::new(cbs));
3556
3557        let (resp_tx, resp_rx) = mpsc::channel();
3558        tx.send(Event::Query(QueryRequest::Links, resp_tx)).unwrap();
3559        tx.send(Event::Shutdown).unwrap();
3560        driver.run();
3561
3562        match resp_rx.recv().unwrap() {
3563            QueryResponse::Links(entries) => {
3564                assert!(entries.is_empty());
3565            }
3566            other => panic!("expected Links, got {:?}", other),
3567        }
3568    }
3569
3570    #[test]
3571    fn query_resources_empty() {
3572        let (tx, rx) = event::channel();
3573        let (cbs, _, _, _, _, _) = MockCallbacks::new();
3574        let driver_config = TransportConfig { transport_enabled: false, identity_hash: None };
3575        let mut driver = Driver::new(driver_config, rx, Box::new(cbs));
3576
3577        let (resp_tx, resp_rx) = mpsc::channel();
3578        tx.send(Event::Query(QueryRequest::Resources, resp_tx)).unwrap();
3579        tx.send(Event::Shutdown).unwrap();
3580        driver.run();
3581
3582        match resp_rx.recv().unwrap() {
3583            QueryResponse::Resources(entries) => {
3584                assert!(entries.is_empty());
3585            }
3586            other => panic!("expected Resources, got {:?}", other),
3587        }
3588    }
3589
3590    #[test]
3591    fn infer_interface_type_from_name() {
3592        assert_eq!(
3593            super::infer_interface_type("TCPServerInterface/Client-1234"),
3594            "TCPServerClientInterface"
3595        );
3596        assert_eq!(
3597            super::infer_interface_type("BackboneInterface/5"),
3598            "BackboneInterface"
3599        );
3600        assert_eq!(
3601            super::infer_interface_type("LocalInterface"),
3602            "LocalServerClientInterface"
3603        );
3604        assert_eq!(
3605            super::infer_interface_type("MyAutoGroup:fe80::1"),
3606            "AutoInterface"
3607        );
3608    }
3609}