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, _dest_hash: rns_core::types::DestHash, _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                        if link_actions.is_empty() {
896                            // Link manager couldn't handle (e.g. opportunistic DATA
897                            // for a registered link destination). Fall back to
898                            // regular delivery.
899                            if let Ok(packet) = RawPacket::unpack(&raw) {
900                                if packet.flags.packet_type == rns_core::constants::PACKET_TYPE_PROOF {
901                                    self.handle_inbound_proof(destination_hash, &packet.data, &packet_hash);
902                                    continue;
903                                }
904                            }
905                            self.maybe_generate_proof(destination_hash, &packet_hash);
906                            self.callbacks.on_local_delivery(
907                                rns_core::types::DestHash(destination_hash),
908                                raw,
909                                rns_core::types::PacketHash(packet_hash),
910                            );
911                        } else {
912                            self.dispatch_link_actions(link_actions);
913                        }
914                    } else {
915                        // Check if this is a PROOF packet for a packet we sent
916                        if let Ok(packet) = RawPacket::unpack(&raw) {
917                            if packet.flags.packet_type == rns_core::constants::PACKET_TYPE_PROOF {
918                                self.handle_inbound_proof(destination_hash, &packet.data, &packet_hash);
919                                continue;
920                            }
921                        }
922
923                        // Check if destination has a proof strategy — generate proof if needed
924                        self.maybe_generate_proof(destination_hash, &packet_hash);
925
926                        self.callbacks
927                            .on_local_delivery(
928                                rns_core::types::DestHash(destination_hash),
929                                raw,
930                                rns_core::types::PacketHash(packet_hash),
931                            );
932                    }
933                }
934                TransportAction::AnnounceReceived {
935                    destination_hash,
936                    identity_hash,
937                    public_key,
938                    app_data,
939                    hops,
940                    receiving_interface,
941                    ..
942                } => {
943                    if let Some(entry) = self.interfaces.get_mut(&receiving_interface) {
944                        entry.stats.record_incoming_announce(time::now());
945                    }
946                    // Cache the announced identity
947                    let announced = crate::destination::AnnouncedIdentity {
948                        dest_hash: rns_core::types::DestHash(destination_hash),
949                        identity_hash: rns_core::types::IdentityHash(identity_hash),
950                        public_key,
951                        app_data: app_data.clone(),
952                        hops,
953                        received_at: time::now(),
954                    };
955                    self.known_destinations.insert(destination_hash, announced.clone());
956                    self.callbacks.on_announce(announced);
957                }
958                TransportAction::PathUpdated {
959                    destination_hash,
960                    hops,
961                    ..
962                } => {
963                    self.callbacks.on_path_updated(rns_core::types::DestHash(destination_hash), hops);
964                }
965                TransportAction::ForwardToLocalClients { raw, exclude } => {
966                    for entry in self.interfaces.values_mut() {
967                        if entry.online
968                            && entry.info.is_local_client
969                            && Some(entry.id) != exclude
970                        {
971                            let data = if let Some(ref ifac_state) = entry.ifac {
972                                ifac::mask_outbound(&raw, ifac_state)
973                            } else {
974                                raw.clone()
975                            };
976                            entry.stats.txb += data.len() as u64;
977                            entry.stats.tx_packets += 1;
978                            if let Err(e) = entry.writer.send_frame(&data) {
979                                log::warn!("[{}] forward to local client failed: {}", entry.info.id.0, e);
980                            }
981                        }
982                    }
983                }
984                TransportAction::ForwardPlainBroadcast { raw, to_local, exclude } => {
985                    for entry in self.interfaces.values_mut() {
986                        if entry.online
987                            && entry.info.is_local_client == to_local
988                            && Some(entry.id) != exclude
989                        {
990                            let data = if let Some(ref ifac_state) = entry.ifac {
991                                ifac::mask_outbound(&raw, ifac_state)
992                            } else {
993                                raw.clone()
994                            };
995                            entry.stats.txb += data.len() as u64;
996                            entry.stats.tx_packets += 1;
997                            if let Err(e) = entry.writer.send_frame(&data) {
998                                log::warn!("[{}] forward plain broadcast failed: {}", entry.info.id.0, e);
999                            }
1000                        }
1001                    }
1002                }
1003                TransportAction::CacheAnnounce { packet_hash, raw } => {
1004                    if let Some(ref cache) = self.announce_cache {
1005                        if let Err(e) = cache.store(&packet_hash, &raw, None) {
1006                            log::warn!("Failed to cache announce: {}", e);
1007                        }
1008                    }
1009                }
1010                TransportAction::TunnelSynthesize { interface, data, dest_hash } => {
1011                    // Pack as BROADCAST DATA PLAIN packet and send on interface
1012                    let flags = rns_core::packet::PacketFlags {
1013                        header_type: rns_core::constants::HEADER_1,
1014                        context_flag: rns_core::constants::FLAG_UNSET,
1015                        transport_type: rns_core::constants::TRANSPORT_BROADCAST,
1016                        destination_type: rns_core::constants::DESTINATION_PLAIN,
1017                        packet_type: rns_core::constants::PACKET_TYPE_DATA,
1018                    };
1019                    if let Ok(packet) = rns_core::packet::RawPacket::pack(
1020                        flags, 0, &dest_hash, None,
1021                        rns_core::constants::CONTEXT_NONE, &data,
1022                    ) {
1023                        if let Some(entry) = self.interfaces.get_mut(&interface) {
1024                            if entry.online {
1025                                let raw = if let Some(ref ifac_state) = entry.ifac {
1026                                    ifac::mask_outbound(&packet.raw, ifac_state)
1027                                } else {
1028                                    packet.raw
1029                                };
1030                                entry.stats.txb += raw.len() as u64;
1031                                entry.stats.tx_packets += 1;
1032                                if let Err(e) = entry.writer.send_frame(&raw) {
1033                                    log::warn!("[{}] tunnel synthesize send failed: {}", entry.info.id.0, e);
1034                                }
1035                            }
1036                        }
1037                    }
1038                }
1039                TransportAction::TunnelEstablished { tunnel_id, interface } => {
1040                    log::info!("Tunnel established: {:02x?} on interface {}", &tunnel_id[..4], interface.0);
1041                }
1042            }
1043        }
1044    }
1045
1046    /// Dispatch link manager actions.
1047    fn dispatch_link_actions(&mut self, actions: Vec<LinkManagerAction>) {
1048        for action in actions {
1049            match action {
1050                LinkManagerAction::SendPacket { raw, dest_type, attached_interface } => {
1051                    // Route through the transport engine's outbound path
1052                    match RawPacket::unpack(&raw) {
1053                        Ok(packet) => {
1054                            let transport_actions = self.engine.handle_outbound(
1055                                &packet,
1056                                dest_type,
1057                                attached_interface,
1058                                time::now(),
1059                            );
1060                            self.dispatch_all(transport_actions);
1061                        }
1062                        Err(e) => {
1063                            log::warn!("LinkManager SendPacket: failed to unpack: {:?}", e);
1064                        }
1065                    }
1066                }
1067                LinkManagerAction::LinkEstablished { link_id, dest_hash, rtt, is_initiator } => {
1068                    log::info!(
1069                        "Link established: {:02x?} rtt={:.3}s initiator={}",
1070                        &link_id[..4], rtt, is_initiator,
1071                    );
1072                    self.callbacks.on_link_established(
1073                        rns_core::types::LinkId(link_id),
1074                        rns_core::types::DestHash(dest_hash),
1075                        rtt,
1076                        is_initiator,
1077                    );
1078                }
1079                LinkManagerAction::LinkClosed { link_id, reason } => {
1080                    log::info!("Link closed: {:02x?} reason={:?}", &link_id[..4], reason);
1081                    self.callbacks.on_link_closed(rns_core::types::LinkId(link_id), reason);
1082                }
1083                LinkManagerAction::RemoteIdentified { link_id, identity_hash, public_key } => {
1084                    log::debug!(
1085                        "Remote identified on link {:02x?}: {:02x?}",
1086                        &link_id[..4], &identity_hash[..4],
1087                    );
1088                    self.callbacks.on_remote_identified(
1089                        rns_core::types::LinkId(link_id),
1090                        rns_core::types::IdentityHash(identity_hash),
1091                        public_key,
1092                    );
1093                }
1094                LinkManagerAction::RegisterLinkDest { link_id } => {
1095                    // Register the link_id as a LINK destination in the transport engine
1096                    self.engine.register_destination(link_id, rns_core::constants::DESTINATION_LINK);
1097                }
1098                LinkManagerAction::DeregisterLinkDest { link_id } => {
1099                    self.engine.deregister_destination(&link_id);
1100                }
1101                LinkManagerAction::ManagementRequest {
1102                    link_id, path_hash, data, request_id, remote_identity,
1103                } => {
1104                    self.handle_management_request(
1105                        link_id, path_hash, data, request_id, remote_identity,
1106                    );
1107                }
1108                LinkManagerAction::ResourceReceived { link_id, data, metadata } => {
1109                    self.callbacks.on_resource_received(rns_core::types::LinkId(link_id), data, metadata);
1110                }
1111                LinkManagerAction::ResourceCompleted { link_id } => {
1112                    self.callbacks.on_resource_completed(rns_core::types::LinkId(link_id));
1113                }
1114                LinkManagerAction::ResourceFailed { link_id, error } => {
1115                    log::debug!("Resource failed on link {:02x?}: {}", &link_id[..4], error);
1116                    self.callbacks.on_resource_failed(rns_core::types::LinkId(link_id), error);
1117                }
1118                LinkManagerAction::ResourceProgress { link_id, received, total } => {
1119                    self.callbacks.on_resource_progress(rns_core::types::LinkId(link_id), received, total);
1120                }
1121                LinkManagerAction::ResourceAcceptQuery { link_id, resource_hash, transfer_size, has_metadata } => {
1122                    let accept = self.callbacks.on_resource_accept_query(
1123                        rns_core::types::LinkId(link_id), resource_hash.clone(), transfer_size, has_metadata,
1124                    );
1125                    let accept_actions = self.link_manager.accept_resource(
1126                        &link_id, &resource_hash, accept, &mut self.rng,
1127                    );
1128                    // Re-dispatch (recursive but bounded: accept_resource won't produce more AcceptQuery)
1129                    self.dispatch_link_actions(accept_actions);
1130                }
1131                LinkManagerAction::ChannelMessageReceived { link_id, msgtype, payload } => {
1132                    self.callbacks.on_channel_message(rns_core::types::LinkId(link_id), msgtype, payload);
1133                }
1134                LinkManagerAction::LinkDataReceived { link_id, context, data } => {
1135                    self.callbacks.on_link_data(rns_core::types::LinkId(link_id), context, data);
1136                }
1137                LinkManagerAction::ResponseReceived { link_id, request_id, data } => {
1138                    self.callbacks.on_response(rns_core::types::LinkId(link_id), request_id, data);
1139                }
1140            }
1141        }
1142    }
1143
1144    /// Management announce interval in seconds.
1145    const MANAGEMENT_ANNOUNCE_INTERVAL: f64 = 300.0;
1146
1147    /// Delay before first management announce after startup.
1148    const MANAGEMENT_ANNOUNCE_DELAY: f64 = 5.0;
1149
1150    /// Emit management and/or blackhole announces if enabled and due.
1151    fn tick_management_announces(&mut self, now: f64) {
1152        if self.transport_identity.is_none() {
1153            return;
1154        }
1155
1156        let uptime = now - self.started;
1157
1158        // Wait for initial delay
1159        if !self.initial_announce_sent {
1160            if uptime < Self::MANAGEMENT_ANNOUNCE_DELAY {
1161                return;
1162            }
1163            self.initial_announce_sent = true;
1164            self.emit_management_announces(now);
1165            return;
1166        }
1167
1168        // Periodic re-announce
1169        if now - self.last_management_announce >= Self::MANAGEMENT_ANNOUNCE_INTERVAL {
1170            self.emit_management_announces(now);
1171        }
1172    }
1173
1174    /// Emit management/blackhole announce packets through the engine outbound path.
1175    fn emit_management_announces(&mut self, now: f64) {
1176        use crate::management;
1177
1178        self.last_management_announce = now;
1179
1180        let identity = match self.transport_identity {
1181            Some(ref id) => id,
1182            None => return,
1183        };
1184
1185        // Build announce packets first (immutable borrow of identity), then dispatch
1186        let mgmt_raw = if self.management_config.enable_remote_management {
1187            management::build_management_announce(identity, &mut self.rng)
1188        } else {
1189            None
1190        };
1191
1192        let bh_raw = if self.management_config.publish_blackhole {
1193            management::build_blackhole_announce(identity, &mut self.rng)
1194        } else {
1195            None
1196        };
1197
1198        if let Some(raw) = mgmt_raw {
1199            if let Ok(packet) = RawPacket::unpack(&raw) {
1200                let actions = self.engine.handle_outbound(
1201                    &packet,
1202                    rns_core::constants::DESTINATION_SINGLE,
1203                    None,
1204                    now,
1205                );
1206                self.dispatch_all(actions);
1207                log::debug!("Emitted management destination announce");
1208            }
1209        }
1210
1211        if let Some(raw) = bh_raw {
1212            if let Ok(packet) = RawPacket::unpack(&raw) {
1213                let actions = self.engine.handle_outbound(
1214                    &packet,
1215                    rns_core::constants::DESTINATION_SINGLE,
1216                    None,
1217                    now,
1218                );
1219                self.dispatch_all(actions);
1220                log::debug!("Emitted blackhole info announce");
1221            }
1222        }
1223    }
1224
1225    /// Handle a management request by querying engine state and sending a response.
1226    fn handle_management_request(
1227        &mut self,
1228        link_id: [u8; 16],
1229        path_hash: [u8; 16],
1230        data: Vec<u8>,
1231        request_id: [u8; 16],
1232        remote_identity: Option<([u8; 16], [u8; 64])>,
1233    ) {
1234        use crate::management;
1235
1236        // ACL check for /status and /path (ALLOW_LIST), /list is ALLOW_ALL
1237        let is_restricted = path_hash == management::status_path_hash()
1238            || path_hash == management::path_path_hash();
1239
1240        if is_restricted && !self.management_config.remote_management_allowed.is_empty() {
1241            match remote_identity {
1242                Some((identity_hash, _)) => {
1243                    if !self.management_config.remote_management_allowed.contains(&identity_hash) {
1244                        log::debug!("Management request denied: identity not in allowed list");
1245                        return;
1246                    }
1247                }
1248                None => {
1249                    log::debug!("Management request denied: peer not identified");
1250                    return;
1251                }
1252            }
1253        }
1254
1255        let response_data = if path_hash == management::status_path_hash() {
1256            management::handle_status_request(&data, &self.engine, &self.interfaces, self.started)
1257        } else if path_hash == management::path_path_hash() {
1258            management::handle_path_request(&data, &self.engine)
1259        } else if path_hash == management::list_path_hash() {
1260            management::handle_blackhole_list_request(&self.engine)
1261        } else {
1262            log::warn!("Unknown management path_hash: {:02x?}", &path_hash[..4]);
1263            None
1264        };
1265
1266        if let Some(response) = response_data {
1267            let actions = self.link_manager.send_management_response(
1268                &link_id, &request_id, &response, &mut self.rng,
1269            );
1270            self.dispatch_link_actions(actions);
1271        }
1272    }
1273}
1274
1275#[cfg(test)]
1276mod tests {
1277    use super::*;
1278    use crate::event;
1279    use crate::interface::Writer;
1280    use rns_core::announce::AnnounceData;
1281    use rns_core::constants;
1282    use rns_core::packet::PacketFlags;
1283    use rns_core::transport::types::InterfaceInfo;
1284    use rns_crypto::identity::Identity;
1285    use std::io;
1286    use std::sync::mpsc;
1287    use std::sync::{Arc, Mutex};
1288
1289    struct MockWriter {
1290        sent: Arc<Mutex<Vec<Vec<u8>>>>,
1291    }
1292
1293    impl MockWriter {
1294        fn new() -> (Self, Arc<Mutex<Vec<Vec<u8>>>>) {
1295            let sent = Arc::new(Mutex::new(Vec::new()));
1296            (MockWriter { sent: sent.clone() }, sent)
1297        }
1298    }
1299
1300    impl Writer for MockWriter {
1301        fn send_frame(&mut self, data: &[u8]) -> io::Result<()> {
1302            self.sent.lock().unwrap().push(data.to_vec());
1303            Ok(())
1304        }
1305    }
1306
1307    use rns_core::types::{DestHash, IdentityHash, LinkId as TypedLinkId, PacketHash};
1308
1309    struct MockCallbacks {
1310        announces: Arc<Mutex<Vec<(DestHash, u8)>>>,
1311        paths: Arc<Mutex<Vec<(DestHash, u8)>>>,
1312        deliveries: Arc<Mutex<Vec<DestHash>>>,
1313        iface_ups: Arc<Mutex<Vec<InterfaceId>>>,
1314        iface_downs: Arc<Mutex<Vec<InterfaceId>>>,
1315        link_established: Arc<Mutex<Vec<(TypedLinkId, f64, bool)>>>,
1316        link_closed: Arc<Mutex<Vec<TypedLinkId>>>,
1317        remote_identified: Arc<Mutex<Vec<(TypedLinkId, IdentityHash)>>>,
1318        resources_received: Arc<Mutex<Vec<(TypedLinkId, Vec<u8>)>>>,
1319        resource_completed: Arc<Mutex<Vec<TypedLinkId>>>,
1320        resource_failed: Arc<Mutex<Vec<(TypedLinkId, String)>>>,
1321        channel_messages: Arc<Mutex<Vec<(TypedLinkId, u16, Vec<u8>)>>>,
1322        link_data: Arc<Mutex<Vec<(TypedLinkId, u8, Vec<u8>)>>>,
1323        responses: Arc<Mutex<Vec<(TypedLinkId, [u8; 16], Vec<u8>)>>>,
1324        proofs: Arc<Mutex<Vec<(DestHash, PacketHash, f64)>>>,
1325        proof_requested: Arc<Mutex<Vec<(DestHash, PacketHash)>>>,
1326    }
1327
1328    impl MockCallbacks {
1329        fn new() -> (
1330            Self,
1331            Arc<Mutex<Vec<(DestHash, u8)>>>,
1332            Arc<Mutex<Vec<(DestHash, u8)>>>,
1333            Arc<Mutex<Vec<DestHash>>>,
1334            Arc<Mutex<Vec<InterfaceId>>>,
1335            Arc<Mutex<Vec<InterfaceId>>>,
1336        ) {
1337            let announces = Arc::new(Mutex::new(Vec::new()));
1338            let paths = Arc::new(Mutex::new(Vec::new()));
1339            let deliveries = Arc::new(Mutex::new(Vec::new()));
1340            let iface_ups = Arc::new(Mutex::new(Vec::new()));
1341            let iface_downs = Arc::new(Mutex::new(Vec::new()));
1342            (
1343                MockCallbacks {
1344                    announces: announces.clone(),
1345                    paths: paths.clone(),
1346                    deliveries: deliveries.clone(),
1347                    iface_ups: iface_ups.clone(),
1348                    iface_downs: iface_downs.clone(),
1349                    link_established: Arc::new(Mutex::new(Vec::new())),
1350                    link_closed: Arc::new(Mutex::new(Vec::new())),
1351                    remote_identified: Arc::new(Mutex::new(Vec::new())),
1352                    resources_received: Arc::new(Mutex::new(Vec::new())),
1353                    resource_completed: Arc::new(Mutex::new(Vec::new())),
1354                    resource_failed: Arc::new(Mutex::new(Vec::new())),
1355                    channel_messages: Arc::new(Mutex::new(Vec::new())),
1356                    link_data: Arc::new(Mutex::new(Vec::new())),
1357                    responses: Arc::new(Mutex::new(Vec::new())),
1358                    proofs: Arc::new(Mutex::new(Vec::new())),
1359                    proof_requested: Arc::new(Mutex::new(Vec::new())),
1360                },
1361                announces,
1362                paths,
1363                deliveries,
1364                iface_ups,
1365                iface_downs,
1366            )
1367        }
1368
1369        fn with_link_tracking() -> (
1370            Self,
1371            Arc<Mutex<Vec<(TypedLinkId, f64, bool)>>>,
1372            Arc<Mutex<Vec<TypedLinkId>>>,
1373            Arc<Mutex<Vec<(TypedLinkId, IdentityHash)>>>,
1374        ) {
1375            let link_established = Arc::new(Mutex::new(Vec::new()));
1376            let link_closed = Arc::new(Mutex::new(Vec::new()));
1377            let remote_identified = Arc::new(Mutex::new(Vec::new()));
1378            (
1379                MockCallbacks {
1380                    announces: Arc::new(Mutex::new(Vec::new())),
1381                    paths: Arc::new(Mutex::new(Vec::new())),
1382                    deliveries: Arc::new(Mutex::new(Vec::new())),
1383                    iface_ups: Arc::new(Mutex::new(Vec::new())),
1384                    iface_downs: Arc::new(Mutex::new(Vec::new())),
1385                    link_established: link_established.clone(),
1386                    link_closed: link_closed.clone(),
1387                    remote_identified: remote_identified.clone(),
1388                    resources_received: Arc::new(Mutex::new(Vec::new())),
1389                    resource_completed: Arc::new(Mutex::new(Vec::new())),
1390                    resource_failed: Arc::new(Mutex::new(Vec::new())),
1391                    channel_messages: Arc::new(Mutex::new(Vec::new())),
1392                    link_data: Arc::new(Mutex::new(Vec::new())),
1393                    responses: Arc::new(Mutex::new(Vec::new())),
1394                    proofs: Arc::new(Mutex::new(Vec::new())),
1395                    proof_requested: Arc::new(Mutex::new(Vec::new())),
1396                },
1397                link_established,
1398                link_closed,
1399                remote_identified,
1400            )
1401        }
1402    }
1403
1404    impl Callbacks for MockCallbacks {
1405        fn on_announce(&mut self, announced: crate::destination::AnnouncedIdentity) {
1406            self.announces.lock().unwrap().push((announced.dest_hash, announced.hops));
1407        }
1408
1409        fn on_path_updated(&mut self, dest_hash: DestHash, hops: u8) {
1410            self.paths.lock().unwrap().push((dest_hash, hops));
1411        }
1412
1413        fn on_local_delivery(&mut self, dest_hash: DestHash, _raw: Vec<u8>, _packet_hash: PacketHash) {
1414            self.deliveries.lock().unwrap().push(dest_hash);
1415        }
1416
1417        fn on_interface_up(&mut self, id: InterfaceId) {
1418            self.iface_ups.lock().unwrap().push(id);
1419        }
1420
1421        fn on_interface_down(&mut self, id: InterfaceId) {
1422            self.iface_downs.lock().unwrap().push(id);
1423        }
1424
1425        fn on_link_established(&mut self, link_id: TypedLinkId, _dest_hash: DestHash, rtt: f64, is_initiator: bool) {
1426            self.link_established.lock().unwrap().push((link_id, rtt, is_initiator));
1427        }
1428
1429        fn on_link_closed(&mut self, link_id: TypedLinkId, _reason: Option<rns_core::link::TeardownReason>) {
1430            self.link_closed.lock().unwrap().push(link_id);
1431        }
1432
1433        fn on_remote_identified(&mut self, link_id: TypedLinkId, identity_hash: IdentityHash, _public_key: [u8; 64]) {
1434            self.remote_identified.lock().unwrap().push((link_id, identity_hash));
1435        }
1436
1437        fn on_resource_received(&mut self, link_id: TypedLinkId, data: Vec<u8>, _metadata: Option<Vec<u8>>) {
1438            self.resources_received.lock().unwrap().push((link_id, data));
1439        }
1440
1441        fn on_resource_completed(&mut self, link_id: TypedLinkId) {
1442            self.resource_completed.lock().unwrap().push(link_id);
1443        }
1444
1445        fn on_resource_failed(&mut self, link_id: TypedLinkId, error: String) {
1446            self.resource_failed.lock().unwrap().push((link_id, error));
1447        }
1448
1449        fn on_channel_message(&mut self, link_id: TypedLinkId, msgtype: u16, payload: Vec<u8>) {
1450            self.channel_messages.lock().unwrap().push((link_id, msgtype, payload));
1451        }
1452
1453        fn on_link_data(&mut self, link_id: TypedLinkId, context: u8, data: Vec<u8>) {
1454            self.link_data.lock().unwrap().push((link_id, context, data));
1455        }
1456
1457        fn on_response(&mut self, link_id: TypedLinkId, request_id: [u8; 16], data: Vec<u8>) {
1458            self.responses.lock().unwrap().push((link_id, request_id, data));
1459        }
1460
1461        fn on_proof(&mut self, dest_hash: DestHash, packet_hash: PacketHash, rtt: f64) {
1462            self.proofs.lock().unwrap().push((dest_hash, packet_hash, rtt));
1463        }
1464
1465        fn on_proof_requested(&mut self, dest_hash: DestHash, packet_hash: PacketHash) -> bool {
1466            self.proof_requested.lock().unwrap().push((dest_hash, packet_hash));
1467            true
1468        }
1469    }
1470
1471    fn make_interface_info(id: u64) -> InterfaceInfo {
1472        InterfaceInfo {
1473            id: InterfaceId(id),
1474            name: format!("test-{}", id),
1475            mode: constants::MODE_FULL,
1476            out_capable: true,
1477            in_capable: true,
1478            bitrate: None,
1479            announce_rate_target: None,
1480            announce_rate_grace: 0,
1481            announce_rate_penalty: 0.0,
1482            announce_cap: rns_core::constants::ANNOUNCE_CAP,
1483            is_local_client: false,
1484            wants_tunnel: false,
1485            tunnel_id: None,
1486        }
1487    }
1488
1489    fn make_entry(id: u64, writer: Box<dyn Writer>, online: bool) -> InterfaceEntry {
1490        InterfaceEntry {
1491            id: InterfaceId(id),
1492            info: make_interface_info(id),
1493            writer,
1494            online,
1495            dynamic: false,
1496            ifac: None,
1497            stats: InterfaceStats::default(),
1498            interface_type: String::new(),
1499        }
1500    }
1501
1502    /// Build a valid announce packet that the engine will accept.
1503    fn build_announce_packet(identity: &Identity) -> Vec<u8> {
1504        let dest_hash = rns_core::destination::destination_hash(
1505            "test",
1506            &["app"],
1507            Some(identity.hash()),
1508        );
1509        let name_hash = rns_core::destination::name_hash("test", &["app"]);
1510        let random_hash = [0x42u8; 10];
1511
1512        let (announce_data, _has_ratchet) = AnnounceData::pack(
1513            identity,
1514            &dest_hash,
1515            &name_hash,
1516            &random_hash,
1517            None,
1518            None,
1519        )
1520        .unwrap();
1521
1522        let flags = PacketFlags {
1523            header_type: constants::HEADER_1,
1524            context_flag: constants::FLAG_UNSET,
1525            transport_type: constants::TRANSPORT_BROADCAST,
1526            destination_type: constants::DESTINATION_SINGLE,
1527            packet_type: constants::PACKET_TYPE_ANNOUNCE,
1528        };
1529
1530        let packet = RawPacket::pack(flags, 0, &dest_hash, None, constants::CONTEXT_NONE, &announce_data).unwrap();
1531        packet.raw
1532    }
1533
1534    #[test]
1535    fn process_inbound_frame() {
1536        let (tx, rx) = event::channel();
1537        let (cbs, announces, _, _, _, _) = MockCallbacks::new();
1538        let mut driver = Driver::new(
1539            TransportConfig { transport_enabled: false, identity_hash: None },
1540            rx,
1541            Box::new(cbs),
1542        );
1543        let info = make_interface_info(1);
1544        driver.engine.register_interface(info.clone());
1545        let (writer, _sent) = MockWriter::new();
1546        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
1547
1548        let identity = Identity::new(&mut OsRng);
1549        let announce_raw = build_announce_packet(&identity);
1550
1551        // Send frame then shutdown
1552        tx.send(Event::Frame { interface_id: InterfaceId(1), data: announce_raw }).unwrap();
1553        tx.send(Event::Shutdown).unwrap();
1554        driver.run();
1555
1556        assert_eq!(announces.lock().unwrap().len(), 1);
1557    }
1558
1559    #[test]
1560    fn dispatch_send() {
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        let (writer, sent) = MockWriter::new();
1569        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
1570
1571        driver.dispatch_all(vec![TransportAction::SendOnInterface {
1572            interface: InterfaceId(1),
1573            raw: vec![0x01, 0x02, 0x03],
1574        }]);
1575
1576        assert_eq!(sent.lock().unwrap().len(), 1);
1577        assert_eq!(sent.lock().unwrap()[0], vec![0x01, 0x02, 0x03]);
1578
1579        drop(tx);
1580    }
1581
1582    #[test]
1583    fn dispatch_broadcast() {
1584        let (tx, rx) = event::channel();
1585        let (cbs, _, _, _, _, _) = MockCallbacks::new();
1586        let mut driver = Driver::new(
1587            TransportConfig { transport_enabled: false, identity_hash: None },
1588            rx,
1589            Box::new(cbs),
1590        );
1591
1592        let (w1, sent1) = MockWriter::new();
1593        let (w2, sent2) = MockWriter::new();
1594        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(w1), true));
1595        driver.interfaces.insert(InterfaceId(2), make_entry(2, Box::new(w2), true));
1596
1597        driver.dispatch_all(vec![TransportAction::BroadcastOnAllInterfaces {
1598            raw: vec![0xAA],
1599            exclude: None,
1600        }]);
1601
1602        assert_eq!(sent1.lock().unwrap().len(), 1);
1603        assert_eq!(sent2.lock().unwrap().len(), 1);
1604
1605        drop(tx);
1606    }
1607
1608    #[test]
1609    fn dispatch_broadcast_exclude() {
1610        let (tx, rx) = event::channel();
1611        let (cbs, _, _, _, _, _) = MockCallbacks::new();
1612        let mut driver = Driver::new(
1613            TransportConfig { transport_enabled: false, identity_hash: None },
1614            rx,
1615            Box::new(cbs),
1616        );
1617
1618        let (w1, sent1) = MockWriter::new();
1619        let (w2, sent2) = MockWriter::new();
1620        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(w1), true));
1621        driver.interfaces.insert(InterfaceId(2), make_entry(2, Box::new(w2), true));
1622
1623        driver.dispatch_all(vec![TransportAction::BroadcastOnAllInterfaces {
1624            raw: vec![0xBB],
1625            exclude: Some(InterfaceId(1)),
1626        }]);
1627
1628        assert_eq!(sent1.lock().unwrap().len(), 0); // excluded
1629        assert_eq!(sent2.lock().unwrap().len(), 1);
1630
1631        drop(tx);
1632    }
1633
1634    #[test]
1635    fn tick_event() {
1636        let (tx, rx) = event::channel();
1637        let (cbs, _, _, _, _, _) = MockCallbacks::new();
1638        let mut driver = Driver::new(
1639            TransportConfig { transport_enabled: true, identity_hash: Some([0x42; 16]) },
1640            rx,
1641            Box::new(cbs),
1642        );
1643        let info = make_interface_info(1);
1644        driver.engine.register_interface(info.clone());
1645        let (writer, _sent) = MockWriter::new();
1646        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
1647
1648        // Send Tick then Shutdown
1649        tx.send(Event::Tick).unwrap();
1650        tx.send(Event::Shutdown).unwrap();
1651        driver.run();
1652        // No crash = tick was processed successfully
1653    }
1654
1655    #[test]
1656    fn shutdown_event() {
1657        let (tx, rx) = event::channel();
1658        let (cbs, _, _, _, _, _) = MockCallbacks::new();
1659        let mut driver = Driver::new(
1660            TransportConfig { transport_enabled: false, identity_hash: None },
1661            rx,
1662            Box::new(cbs),
1663        );
1664
1665        tx.send(Event::Shutdown).unwrap();
1666        driver.run(); // Should return immediately
1667    }
1668
1669    #[test]
1670    fn announce_callback() {
1671        let (tx, rx) = event::channel();
1672        let (cbs, announces, paths, _, _, _) = MockCallbacks::new();
1673        let mut driver = Driver::new(
1674            TransportConfig { transport_enabled: false, identity_hash: None },
1675            rx,
1676            Box::new(cbs),
1677        );
1678        let info = make_interface_info(1);
1679        driver.engine.register_interface(info.clone());
1680        let (writer, _sent) = MockWriter::new();
1681        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
1682
1683        let identity = Identity::new(&mut OsRng);
1684        let announce_raw = build_announce_packet(&identity);
1685
1686        tx.send(Event::Frame { interface_id: InterfaceId(1), data: announce_raw }).unwrap();
1687        tx.send(Event::Shutdown).unwrap();
1688        driver.run();
1689
1690        let ann = announces.lock().unwrap();
1691        assert_eq!(ann.len(), 1);
1692        // Hops should be 1 (incremented from 0 by handle_inbound)
1693        assert_eq!(ann[0].1, 1);
1694
1695        let p = paths.lock().unwrap();
1696        assert_eq!(p.len(), 1);
1697    }
1698
1699    #[test]
1700    fn dispatch_skips_offline_interface() {
1701        let (tx, rx) = event::channel();
1702        let (cbs, _, _, _, _, _) = MockCallbacks::new();
1703        let mut driver = Driver::new(
1704            TransportConfig { transport_enabled: false, identity_hash: None },
1705            rx,
1706            Box::new(cbs),
1707        );
1708
1709        let (w1, sent1) = MockWriter::new();
1710        let (w2, sent2) = MockWriter::new();
1711        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(w1), false)); // offline
1712        driver.interfaces.insert(InterfaceId(2), make_entry(2, Box::new(w2), true));
1713
1714        // Direct send to offline interface: should be skipped
1715        driver.dispatch_all(vec![TransportAction::SendOnInterface {
1716            interface: InterfaceId(1),
1717            raw: vec![0x01],
1718        }]);
1719        assert_eq!(sent1.lock().unwrap().len(), 0);
1720
1721        // Broadcast: only online interface should receive
1722        driver.dispatch_all(vec![TransportAction::BroadcastOnAllInterfaces {
1723            raw: vec![0x02],
1724            exclude: None,
1725        }]);
1726        assert_eq!(sent1.lock().unwrap().len(), 0); // still offline
1727        assert_eq!(sent2.lock().unwrap().len(), 1);
1728
1729        drop(tx);
1730    }
1731
1732    #[test]
1733    fn interface_up_refreshes_writer() {
1734        let (tx, rx) = event::channel();
1735        let (cbs, _, _, _, _, _) = MockCallbacks::new();
1736        let mut driver = Driver::new(
1737            TransportConfig { transport_enabled: false, identity_hash: None },
1738            rx,
1739            Box::new(cbs),
1740        );
1741
1742        let (w_old, sent_old) = MockWriter::new();
1743        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(w_old), false));
1744
1745        // Simulate reconnect: InterfaceUp with new writer
1746        let (w_new, sent_new) = MockWriter::new();
1747        tx.send(Event::InterfaceUp(InterfaceId(1), Some(Box::new(w_new)), None)).unwrap();
1748        tx.send(Event::Shutdown).unwrap();
1749        driver.run();
1750
1751        // Interface should be online now
1752        assert!(driver.interfaces[&InterfaceId(1)].online);
1753
1754        // Send via the (now-refreshed) interface
1755        driver.dispatch_all(vec![TransportAction::SendOnInterface {
1756            interface: InterfaceId(1),
1757            raw: vec![0xFF],
1758        }]);
1759
1760        // Old writer should not have received anything
1761        assert_eq!(sent_old.lock().unwrap().len(), 0);
1762        // New writer should have received the data
1763        assert_eq!(sent_new.lock().unwrap().len(), 1);
1764        assert_eq!(sent_new.lock().unwrap()[0], vec![0xFF]);
1765
1766        drop(tx);
1767    }
1768
1769    #[test]
1770    fn dynamic_interface_register() {
1771        let (tx, rx) = event::channel();
1772        let (cbs, _, _, _, iface_ups, _) = MockCallbacks::new();
1773        let mut driver = Driver::new(
1774            TransportConfig { transport_enabled: false, identity_hash: None },
1775            rx,
1776            Box::new(cbs),
1777        );
1778
1779        let info = make_interface_info(100);
1780        let (writer, sent) = MockWriter::new();
1781
1782        // InterfaceUp with InterfaceInfo = new dynamic interface
1783        tx.send(Event::InterfaceUp(
1784            InterfaceId(100),
1785            Some(Box::new(writer)),
1786            Some(info),
1787        ))
1788        .unwrap();
1789        tx.send(Event::Shutdown).unwrap();
1790        driver.run();
1791
1792        // Should be registered and online
1793        assert!(driver.interfaces.contains_key(&InterfaceId(100)));
1794        assert!(driver.interfaces[&InterfaceId(100)].online);
1795        assert!(driver.interfaces[&InterfaceId(100)].dynamic);
1796
1797        // Callback should have fired
1798        assert_eq!(iface_ups.lock().unwrap().len(), 1);
1799        assert_eq!(iface_ups.lock().unwrap()[0], InterfaceId(100));
1800
1801        // Can send to it
1802        driver.dispatch_all(vec![TransportAction::SendOnInterface {
1803            interface: InterfaceId(100),
1804            raw: vec![0x42],
1805        }]);
1806        assert_eq!(sent.lock().unwrap().len(), 1);
1807
1808        drop(tx);
1809    }
1810
1811    #[test]
1812    fn dynamic_interface_deregister() {
1813        let (tx, rx) = event::channel();
1814        let (cbs, _, _, _, _, iface_downs) = MockCallbacks::new();
1815        let mut driver = Driver::new(
1816            TransportConfig { transport_enabled: false, identity_hash: None },
1817            rx,
1818            Box::new(cbs),
1819        );
1820
1821        // Register a dynamic interface
1822        let info = make_interface_info(200);
1823        driver.engine.register_interface(info.clone());
1824        let (writer, _sent) = MockWriter::new();
1825        driver.interfaces.insert(InterfaceId(200), InterfaceEntry {
1826            id: InterfaceId(200),
1827            info,
1828            writer: Box::new(writer),
1829            online: true,
1830            dynamic: true,
1831            ifac: None,
1832            stats: InterfaceStats::default(),
1833            interface_type: String::new(),
1834        });
1835
1836        // InterfaceDown for dynamic → should be removed entirely
1837        tx.send(Event::InterfaceDown(InterfaceId(200))).unwrap();
1838        tx.send(Event::Shutdown).unwrap();
1839        driver.run();
1840
1841        assert!(!driver.interfaces.contains_key(&InterfaceId(200)));
1842        assert_eq!(iface_downs.lock().unwrap().len(), 1);
1843        assert_eq!(iface_downs.lock().unwrap()[0], InterfaceId(200));
1844    }
1845
1846    #[test]
1847    fn interface_callbacks_fire() {
1848        let (tx, rx) = event::channel();
1849        let (cbs, _, _, _, iface_ups, iface_downs) = MockCallbacks::new();
1850        let mut driver = Driver::new(
1851            TransportConfig { transport_enabled: false, identity_hash: None },
1852            rx,
1853            Box::new(cbs),
1854        );
1855
1856        // Static interface
1857        let (writer, _) = MockWriter::new();
1858        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), false));
1859
1860        tx.send(Event::InterfaceUp(InterfaceId(1), None, None)).unwrap();
1861        tx.send(Event::InterfaceDown(InterfaceId(1))).unwrap();
1862        tx.send(Event::Shutdown).unwrap();
1863        driver.run();
1864
1865        assert_eq!(iface_ups.lock().unwrap().len(), 1);
1866        assert_eq!(iface_downs.lock().unwrap().len(), 1);
1867        // Static interface should still exist but be offline
1868        assert!(driver.interfaces.contains_key(&InterfaceId(1)));
1869        assert!(!driver.interfaces[&InterfaceId(1)].online);
1870    }
1871
1872    // =========================================================================
1873    // New tests for Phase 6a
1874    // =========================================================================
1875
1876    #[test]
1877    fn frame_updates_rx_stats() {
1878        let (tx, rx) = event::channel();
1879        let (cbs, _, _, _, _, _) = MockCallbacks::new();
1880        let mut driver = Driver::new(
1881            TransportConfig { transport_enabled: false, identity_hash: None },
1882            rx,
1883            Box::new(cbs),
1884        );
1885        let info = make_interface_info(1);
1886        driver.engine.register_interface(info.clone());
1887        let (writer, _sent) = MockWriter::new();
1888        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
1889
1890        let identity = Identity::new(&mut OsRng);
1891        let announce_raw = build_announce_packet(&identity);
1892        let announce_len = announce_raw.len() as u64;
1893
1894        tx.send(Event::Frame { interface_id: InterfaceId(1), data: announce_raw }).unwrap();
1895        tx.send(Event::Shutdown).unwrap();
1896        driver.run();
1897
1898        let stats = &driver.interfaces[&InterfaceId(1)].stats;
1899        assert_eq!(stats.rxb, announce_len);
1900        assert_eq!(stats.rx_packets, 1);
1901    }
1902
1903    #[test]
1904    fn send_updates_tx_stats() {
1905        let (tx, rx) = event::channel();
1906        let (cbs, _, _, _, _, _) = MockCallbacks::new();
1907        let mut driver = Driver::new(
1908            TransportConfig { transport_enabled: false, identity_hash: None },
1909            rx,
1910            Box::new(cbs),
1911        );
1912        let (writer, _sent) = MockWriter::new();
1913        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
1914
1915        driver.dispatch_all(vec![TransportAction::SendOnInterface {
1916            interface: InterfaceId(1),
1917            raw: vec![0x01, 0x02, 0x03],
1918        }]);
1919
1920        let stats = &driver.interfaces[&InterfaceId(1)].stats;
1921        assert_eq!(stats.txb, 3);
1922        assert_eq!(stats.tx_packets, 1);
1923
1924        drop(tx);
1925    }
1926
1927    #[test]
1928    fn broadcast_updates_tx_stats() {
1929        let (tx, rx) = event::channel();
1930        let (cbs, _, _, _, _, _) = MockCallbacks::new();
1931        let mut driver = Driver::new(
1932            TransportConfig { transport_enabled: false, identity_hash: None },
1933            rx,
1934            Box::new(cbs),
1935        );
1936        let (w1, _s1) = MockWriter::new();
1937        let (w2, _s2) = MockWriter::new();
1938        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(w1), true));
1939        driver.interfaces.insert(InterfaceId(2), make_entry(2, Box::new(w2), true));
1940
1941        driver.dispatch_all(vec![TransportAction::BroadcastOnAllInterfaces {
1942            raw: vec![0xAA, 0xBB],
1943            exclude: None,
1944        }]);
1945
1946        // Both interfaces should have tx stats updated
1947        assert_eq!(driver.interfaces[&InterfaceId(1)].stats.txb, 2);
1948        assert_eq!(driver.interfaces[&InterfaceId(1)].stats.tx_packets, 1);
1949        assert_eq!(driver.interfaces[&InterfaceId(2)].stats.txb, 2);
1950        assert_eq!(driver.interfaces[&InterfaceId(2)].stats.tx_packets, 1);
1951
1952        drop(tx);
1953    }
1954
1955    #[test]
1956    fn query_interface_stats() {
1957        let (tx, rx) = event::channel();
1958        let (cbs, _, _, _, _, _) = MockCallbacks::new();
1959        let mut driver = Driver::new(
1960            TransportConfig { transport_enabled: true, identity_hash: Some([0x42; 16]) },
1961            rx,
1962            Box::new(cbs),
1963        );
1964        let (writer, _sent) = MockWriter::new();
1965        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
1966
1967        let (resp_tx, resp_rx) = mpsc::channel();
1968        tx.send(Event::Query(QueryRequest::InterfaceStats, resp_tx)).unwrap();
1969        tx.send(Event::Shutdown).unwrap();
1970        driver.run();
1971
1972        let resp = resp_rx.recv().unwrap();
1973        match resp {
1974            QueryResponse::InterfaceStats(stats) => {
1975                assert_eq!(stats.interfaces.len(), 1);
1976                assert_eq!(stats.interfaces[0].name, "test-1");
1977                assert!(stats.interfaces[0].status);
1978                assert_eq!(stats.transport_id, Some([0x42; 16]));
1979                assert!(stats.transport_enabled);
1980            }
1981            _ => panic!("unexpected response"),
1982        }
1983    }
1984
1985    #[test]
1986    fn query_path_table() {
1987        let (tx, rx) = event::channel();
1988        let (cbs, _, _, _, _, _) = MockCallbacks::new();
1989        let mut driver = Driver::new(
1990            TransportConfig { transport_enabled: false, identity_hash: None },
1991            rx,
1992            Box::new(cbs),
1993        );
1994        let info = make_interface_info(1);
1995        driver.engine.register_interface(info);
1996        let (writer, _sent) = MockWriter::new();
1997        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
1998
1999        // Feed an announce to create a path entry
2000        let identity = Identity::new(&mut OsRng);
2001        let announce_raw = build_announce_packet(&identity);
2002        tx.send(Event::Frame { interface_id: InterfaceId(1), data: announce_raw }).unwrap();
2003
2004        let (resp_tx, resp_rx) = mpsc::channel();
2005        tx.send(Event::Query(QueryRequest::PathTable { max_hops: None }, resp_tx)).unwrap();
2006        tx.send(Event::Shutdown).unwrap();
2007        driver.run();
2008
2009        let resp = resp_rx.recv().unwrap();
2010        match resp {
2011            QueryResponse::PathTable(entries) => {
2012                assert_eq!(entries.len(), 1);
2013                assert_eq!(entries[0].hops, 1);
2014            }
2015            _ => panic!("unexpected response"),
2016        }
2017    }
2018
2019    #[test]
2020    fn query_drop_path() {
2021        let (tx, rx) = event::channel();
2022        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2023        let mut driver = Driver::new(
2024            TransportConfig { transport_enabled: false, identity_hash: None },
2025            rx,
2026            Box::new(cbs),
2027        );
2028        let info = make_interface_info(1);
2029        driver.engine.register_interface(info);
2030        let (writer, _sent) = MockWriter::new();
2031        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
2032
2033        // Feed an announce to create a path entry
2034        let identity = Identity::new(&mut OsRng);
2035        let announce_raw = build_announce_packet(&identity);
2036        let dest_hash = rns_core::destination::destination_hash(
2037            "test", &["app"], Some(identity.hash()),
2038        );
2039
2040        tx.send(Event::Frame { interface_id: InterfaceId(1), data: announce_raw }).unwrap();
2041
2042        let (resp_tx, resp_rx) = mpsc::channel();
2043        tx.send(Event::Query(QueryRequest::DropPath { dest_hash }, resp_tx)).unwrap();
2044        tx.send(Event::Shutdown).unwrap();
2045        driver.run();
2046
2047        let resp = resp_rx.recv().unwrap();
2048        match resp {
2049            QueryResponse::DropPath(dropped) => {
2050                assert!(dropped);
2051            }
2052            _ => panic!("unexpected response"),
2053        }
2054    }
2055
2056    #[test]
2057    fn send_outbound_event() {
2058        let (tx, rx) = event::channel();
2059        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2060        let mut driver = Driver::new(
2061            TransportConfig { transport_enabled: false, identity_hash: None },
2062            rx,
2063            Box::new(cbs),
2064        );
2065        let (writer, sent) = MockWriter::new();
2066        let info = make_interface_info(1);
2067        driver.engine.register_interface(info);
2068        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
2069
2070        // Build a DATA packet to a destination
2071        let dest = [0xAA; 16];
2072        let flags = PacketFlags {
2073            header_type: constants::HEADER_1,
2074            context_flag: constants::FLAG_UNSET,
2075            transport_type: constants::TRANSPORT_BROADCAST,
2076            destination_type: constants::DESTINATION_PLAIN,
2077            packet_type: constants::PACKET_TYPE_DATA,
2078        };
2079        let packet = RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"hello").unwrap();
2080
2081        tx.send(Event::SendOutbound {
2082            raw: packet.raw,
2083            dest_type: constants::DESTINATION_PLAIN,
2084            attached_interface: None,
2085        }).unwrap();
2086        tx.send(Event::Shutdown).unwrap();
2087        driver.run();
2088
2089        // PLAIN packet should be broadcast on all interfaces
2090        assert_eq!(sent.lock().unwrap().len(), 1);
2091    }
2092
2093    #[test]
2094    fn register_destination_and_deliver() {
2095        let (tx, rx) = event::channel();
2096        let (cbs, _, _, deliveries, _, _) = MockCallbacks::new();
2097        let mut driver = Driver::new(
2098            TransportConfig { transport_enabled: false, identity_hash: None },
2099            rx,
2100            Box::new(cbs),
2101        );
2102        let info = make_interface_info(1);
2103        driver.engine.register_interface(info);
2104        let (writer, _sent) = MockWriter::new();
2105        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
2106
2107        let dest = [0xBB; 16];
2108
2109        // Register destination then send a data packet to it
2110        tx.send(Event::RegisterDestination {
2111            dest_hash: dest,
2112            dest_type: constants::DESTINATION_SINGLE,
2113        }).unwrap();
2114
2115        let flags = PacketFlags {
2116            header_type: constants::HEADER_1,
2117            context_flag: constants::FLAG_UNSET,
2118            transport_type: constants::TRANSPORT_BROADCAST,
2119            destination_type: constants::DESTINATION_SINGLE,
2120            packet_type: constants::PACKET_TYPE_DATA,
2121        };
2122        let packet = RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"data").unwrap();
2123        tx.send(Event::Frame { interface_id: InterfaceId(1), data: packet.raw }).unwrap();
2124        tx.send(Event::Shutdown).unwrap();
2125        driver.run();
2126
2127        assert_eq!(deliveries.lock().unwrap().len(), 1);
2128        assert_eq!(deliveries.lock().unwrap()[0], DestHash(dest));
2129    }
2130
2131    #[test]
2132    fn query_transport_identity() {
2133        let (tx, rx) = event::channel();
2134        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2135        let mut driver = Driver::new(
2136            TransportConfig { transport_enabled: true, identity_hash: Some([0xAA; 16]) },
2137            rx,
2138            Box::new(cbs),
2139        );
2140
2141        let (resp_tx, resp_rx) = mpsc::channel();
2142        tx.send(Event::Query(QueryRequest::TransportIdentity, resp_tx)).unwrap();
2143        tx.send(Event::Shutdown).unwrap();
2144        driver.run();
2145
2146        match resp_rx.recv().unwrap() {
2147            QueryResponse::TransportIdentity(Some(hash)) => {
2148                assert_eq!(hash, [0xAA; 16]);
2149            }
2150            _ => panic!("unexpected response"),
2151        }
2152    }
2153
2154    #[test]
2155    fn query_link_count() {
2156        let (tx, rx) = event::channel();
2157        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2158        let mut driver = Driver::new(
2159            TransportConfig { transport_enabled: false, identity_hash: None },
2160            rx,
2161            Box::new(cbs),
2162        );
2163
2164        let (resp_tx, resp_rx) = mpsc::channel();
2165        tx.send(Event::Query(QueryRequest::LinkCount, resp_tx)).unwrap();
2166        tx.send(Event::Shutdown).unwrap();
2167        driver.run();
2168
2169        match resp_rx.recv().unwrap() {
2170            QueryResponse::LinkCount(count) => assert_eq!(count, 0),
2171            _ => panic!("unexpected response"),
2172        }
2173    }
2174
2175    #[test]
2176    fn query_rate_table() {
2177        let (tx, rx) = event::channel();
2178        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2179        let mut driver = Driver::new(
2180            TransportConfig { transport_enabled: false, identity_hash: None },
2181            rx,
2182            Box::new(cbs),
2183        );
2184
2185        let (resp_tx, resp_rx) = mpsc::channel();
2186        tx.send(Event::Query(QueryRequest::RateTable, resp_tx)).unwrap();
2187        tx.send(Event::Shutdown).unwrap();
2188        driver.run();
2189
2190        match resp_rx.recv().unwrap() {
2191            QueryResponse::RateTable(entries) => assert!(entries.is_empty()),
2192            _ => panic!("unexpected response"),
2193        }
2194    }
2195
2196    #[test]
2197    fn query_next_hop() {
2198        let (tx, rx) = event::channel();
2199        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2200        let mut driver = Driver::new(
2201            TransportConfig { transport_enabled: false, identity_hash: None },
2202            rx,
2203            Box::new(cbs),
2204        );
2205
2206        let dest = [0xBB; 16];
2207        let (resp_tx, resp_rx) = mpsc::channel();
2208        tx.send(Event::Query(QueryRequest::NextHop { dest_hash: dest }, resp_tx)).unwrap();
2209        tx.send(Event::Shutdown).unwrap();
2210        driver.run();
2211
2212        match resp_rx.recv().unwrap() {
2213            QueryResponse::NextHop(None) => {}
2214            _ => panic!("unexpected response"),
2215        }
2216    }
2217
2218    #[test]
2219    fn query_next_hop_if_name() {
2220        let (tx, rx) = event::channel();
2221        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2222        let mut driver = Driver::new(
2223            TransportConfig { transport_enabled: false, identity_hash: None },
2224            rx,
2225            Box::new(cbs),
2226        );
2227
2228        let dest = [0xCC; 16];
2229        let (resp_tx, resp_rx) = mpsc::channel();
2230        tx.send(Event::Query(QueryRequest::NextHopIfName { dest_hash: dest }, resp_tx)).unwrap();
2231        tx.send(Event::Shutdown).unwrap();
2232        driver.run();
2233
2234        match resp_rx.recv().unwrap() {
2235            QueryResponse::NextHopIfName(None) => {}
2236            _ => panic!("unexpected response"),
2237        }
2238    }
2239
2240    #[test]
2241    fn query_drop_all_via() {
2242        let (tx, rx) = event::channel();
2243        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2244        let mut driver = Driver::new(
2245            TransportConfig { transport_enabled: false, identity_hash: None },
2246            rx,
2247            Box::new(cbs),
2248        );
2249
2250        let transport = [0xDD; 16];
2251        let (resp_tx, resp_rx) = mpsc::channel();
2252        tx.send(Event::Query(
2253            QueryRequest::DropAllVia { transport_hash: transport },
2254            resp_tx,
2255        )).unwrap();
2256        tx.send(Event::Shutdown).unwrap();
2257        driver.run();
2258
2259        match resp_rx.recv().unwrap() {
2260            QueryResponse::DropAllVia(count) => assert_eq!(count, 0),
2261            _ => panic!("unexpected response"),
2262        }
2263    }
2264
2265    #[test]
2266    fn query_drop_announce_queues() {
2267        let (tx, rx) = event::channel();
2268        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2269        let mut driver = Driver::new(
2270            TransportConfig { transport_enabled: false, identity_hash: None },
2271            rx,
2272            Box::new(cbs),
2273        );
2274
2275        let (resp_tx, resp_rx) = mpsc::channel();
2276        tx.send(Event::Query(QueryRequest::DropAnnounceQueues, resp_tx)).unwrap();
2277        tx.send(Event::Shutdown).unwrap();
2278        driver.run();
2279
2280        match resp_rx.recv().unwrap() {
2281            QueryResponse::DropAnnounceQueues => {}
2282            _ => panic!("unexpected response"),
2283        }
2284    }
2285
2286    // =========================================================================
2287    // Phase 7e: Link wiring integration tests
2288    // =========================================================================
2289
2290    #[test]
2291    fn register_link_dest_event() {
2292        let (tx, rx) = event::channel();
2293        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2294        let mut driver = Driver::new(
2295            TransportConfig { transport_enabled: false, identity_hash: None },
2296            rx,
2297            Box::new(cbs),
2298        );
2299        let info = make_interface_info(1);
2300        driver.engine.register_interface(info);
2301        let (writer, _sent) = MockWriter::new();
2302        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
2303
2304        let mut rng = OsRng;
2305        let sig_prv = rns_crypto::ed25519::Ed25519PrivateKey::generate(&mut rng);
2306        let sig_pub_bytes = sig_prv.public_key().public_bytes();
2307        let sig_prv_bytes = sig_prv.private_bytes();
2308        let dest_hash = [0xDD; 16];
2309
2310        tx.send(Event::RegisterLinkDestination {
2311            dest_hash,
2312            sig_prv_bytes,
2313            sig_pub_bytes,
2314        }).unwrap();
2315        tx.send(Event::Shutdown).unwrap();
2316        driver.run();
2317
2318        // Link manager should know about the destination
2319        assert!(driver.link_manager.is_link_destination(&dest_hash));
2320    }
2321
2322    #[test]
2323    fn create_link_event() {
2324        let (tx, rx) = event::channel();
2325        let (cbs, _link_established, _, _) = MockCallbacks::with_link_tracking();
2326        let mut driver = Driver::new(
2327            TransportConfig { transport_enabled: false, identity_hash: None },
2328            rx,
2329            Box::new(cbs),
2330        );
2331        let info = make_interface_info(1);
2332        driver.engine.register_interface(info);
2333        let (writer, _sent) = MockWriter::new();
2334        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
2335
2336        let dest_hash = [0xDD; 16];
2337        let dummy_sig_pub = [0xAA; 32];
2338
2339        let (resp_tx, resp_rx) = mpsc::channel();
2340        tx.send(Event::CreateLink {
2341            dest_hash,
2342            dest_sig_pub_bytes: dummy_sig_pub,
2343            response_tx: resp_tx,
2344        }).unwrap();
2345        tx.send(Event::Shutdown).unwrap();
2346        driver.run();
2347
2348        // Should have received a link_id
2349        let link_id = resp_rx.recv().unwrap();
2350        assert_ne!(link_id, [0u8; 16]);
2351
2352        // Link should be in pending state in the manager
2353        assert_eq!(driver.link_manager.link_count(), 1);
2354
2355        // The LINKREQUEST packet won't be sent on the wire without a path
2356        // to the destination (DESTINATION_LINK requires a known path or
2357        // attached_interface). In a real scenario, the path would exist from
2358        // an announce received earlier.
2359    }
2360
2361    #[test]
2362    fn deliver_local_routes_to_link_manager() {
2363        // Verify that DeliverLocal for a registered link destination goes to
2364        // the link manager instead of the callbacks.
2365        let (tx, rx) = event::channel();
2366        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2367        let mut driver = Driver::new(
2368            TransportConfig { transport_enabled: false, identity_hash: None },
2369            rx,
2370            Box::new(cbs),
2371        );
2372        let info = make_interface_info(1);
2373        driver.engine.register_interface(info);
2374        let (writer, _sent) = MockWriter::new();
2375        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
2376
2377        // Register a link destination
2378        let mut rng = OsRng;
2379        let sig_prv = rns_crypto::ed25519::Ed25519PrivateKey::generate(&mut rng);
2380        let sig_pub_bytes = sig_prv.public_key().public_bytes();
2381        let dest_hash = [0xEE; 16];
2382        driver.link_manager.register_link_destination(dest_hash, sig_prv, sig_pub_bytes);
2383
2384        // dispatch_all with a DeliverLocal for that dest should route to link_manager
2385        // (not to callbacks). We can't easily test this via run() since we need
2386        // a valid LINKREQUEST, but we can check is_link_destination works.
2387        assert!(driver.link_manager.is_link_destination(&dest_hash));
2388
2389        // Non-link destination should go to callbacks
2390        assert!(!driver.link_manager.is_link_destination(&[0xFF; 16]));
2391
2392        drop(tx);
2393    }
2394
2395    #[test]
2396    fn teardown_link_event() {
2397        let (tx, rx) = event::channel();
2398        let (cbs, _, link_closed, _) = MockCallbacks::with_link_tracking();
2399        let mut driver = Driver::new(
2400            TransportConfig { transport_enabled: false, identity_hash: None },
2401            rx,
2402            Box::new(cbs),
2403        );
2404        let info = make_interface_info(1);
2405        driver.engine.register_interface(info);
2406        let (writer, _sent) = MockWriter::new();
2407        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
2408
2409        // Create a link first
2410        let (resp_tx, resp_rx) = mpsc::channel();
2411        tx.send(Event::CreateLink {
2412            dest_hash: [0xDD; 16],
2413            dest_sig_pub_bytes: [0xAA; 32],
2414            response_tx: resp_tx,
2415        }).unwrap();
2416        // Then tear it down
2417        // We can't receive resp_rx yet since driver.run() hasn't started,
2418        // but we know the link_id will be created. Send teardown after CreateLink.
2419        // Actually, we need to get the link_id first. Let's use a two-phase approach.
2420        tx.send(Event::Shutdown).unwrap();
2421        driver.run();
2422
2423        let link_id = resp_rx.recv().unwrap();
2424        assert_ne!(link_id, [0u8; 16]);
2425        assert_eq!(driver.link_manager.link_count(), 1);
2426
2427        // Now restart with same driver (just use events directly since driver loop exited)
2428        let teardown_actions = driver.link_manager.teardown_link(&link_id);
2429        driver.dispatch_link_actions(teardown_actions);
2430
2431        // Callback should have been called
2432        assert_eq!(link_closed.lock().unwrap().len(), 1);
2433        assert_eq!(link_closed.lock().unwrap()[0], TypedLinkId(link_id));
2434    }
2435
2436    #[test]
2437    fn link_count_includes_link_manager() {
2438        let (tx, rx) = event::channel();
2439        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2440        let mut driver = Driver::new(
2441            TransportConfig { transport_enabled: false, identity_hash: None },
2442            rx,
2443            Box::new(cbs),
2444        );
2445        let info = make_interface_info(1);
2446        driver.engine.register_interface(info);
2447        let (writer, _sent) = MockWriter::new();
2448        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
2449
2450        // Create a link via link_manager directly
2451        let mut rng = OsRng;
2452        let dummy_sig = [0xAA; 32];
2453        driver.link_manager.create_link(&[0xDD; 16], &dummy_sig, 1, &mut rng);
2454
2455        // Query link count — should include link_manager links
2456        let (resp_tx, resp_rx) = mpsc::channel();
2457        tx.send(Event::Query(QueryRequest::LinkCount, resp_tx)).unwrap();
2458        tx.send(Event::Shutdown).unwrap();
2459        driver.run();
2460
2461        match resp_rx.recv().unwrap() {
2462            QueryResponse::LinkCount(count) => assert_eq!(count, 1),
2463            _ => panic!("unexpected response"),
2464        }
2465    }
2466
2467    #[test]
2468    fn register_request_handler_event() {
2469        let (tx, rx) = event::channel();
2470        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2471        let mut driver = Driver::new(
2472            TransportConfig { transport_enabled: false, identity_hash: None },
2473            rx,
2474            Box::new(cbs),
2475        );
2476
2477        tx.send(Event::RegisterRequestHandler {
2478            path: "/status".to_string(),
2479            allowed_list: None,
2480            handler: Box::new(|_link_id, _path, _data, _remote| Some(b"OK".to_vec())),
2481        }).unwrap();
2482        tx.send(Event::Shutdown).unwrap();
2483        driver.run();
2484
2485        // Handler should be registered (we can't directly query the count,
2486        // but at least verify no crash)
2487    }
2488
2489    // Phase 8c: Management announce timing tests
2490
2491    #[test]
2492    fn management_announces_emitted_after_delay() {
2493        let (tx, rx) = event::channel();
2494        let (cbs, announces, _, _, _, _) = MockCallbacks::new();
2495        let identity = Identity::new(&mut OsRng);
2496        let identity_hash = *identity.hash();
2497        let mut driver = Driver::new(
2498            TransportConfig { transport_enabled: true, identity_hash: Some(identity_hash) },
2499            rx,
2500            Box::new(cbs),
2501        );
2502
2503        // Register interface so announces can be sent
2504        let info = make_interface_info(1);
2505        driver.engine.register_interface(info.clone());
2506        let (writer, sent) = MockWriter::new();
2507        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
2508
2509        // Enable management announces
2510        driver.management_config.enable_remote_management = true;
2511        driver.transport_identity = Some(identity);
2512
2513        // Set started time to 10 seconds ago so the 5s delay has passed
2514        driver.started = time::now() - 10.0;
2515
2516        // Send Tick then Shutdown
2517        tx.send(Event::Tick).unwrap();
2518        tx.send(Event::Shutdown).unwrap();
2519        driver.run();
2520
2521        // Should have sent at least one packet (the management announce)
2522        let sent_packets = sent.lock().unwrap();
2523        assert!(!sent_packets.is_empty(),
2524            "Management announce should be sent after startup delay");
2525    }
2526
2527    #[test]
2528    fn management_announces_not_emitted_when_disabled() {
2529        let (tx, rx) = event::channel();
2530        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2531        let identity = Identity::new(&mut OsRng);
2532        let identity_hash = *identity.hash();
2533        let mut driver = Driver::new(
2534            TransportConfig { transport_enabled: true, identity_hash: Some(identity_hash) },
2535            rx,
2536            Box::new(cbs),
2537        );
2538
2539        let info = make_interface_info(1);
2540        driver.engine.register_interface(info.clone());
2541        let (writer, sent) = MockWriter::new();
2542        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
2543
2544        // Management announces disabled (default)
2545        driver.transport_identity = Some(identity);
2546        driver.started = time::now() - 10.0;
2547
2548        tx.send(Event::Tick).unwrap();
2549        tx.send(Event::Shutdown).unwrap();
2550        driver.run();
2551
2552        // Should NOT have sent any packets
2553        let sent_packets = sent.lock().unwrap();
2554        assert!(sent_packets.is_empty(),
2555            "No announces should be sent when management is disabled");
2556    }
2557
2558    #[test]
2559    fn management_announces_not_emitted_before_delay() {
2560        let (tx, rx) = event::channel();
2561        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2562        let identity = Identity::new(&mut OsRng);
2563        let identity_hash = *identity.hash();
2564        let mut driver = Driver::new(
2565            TransportConfig { transport_enabled: true, identity_hash: Some(identity_hash) },
2566            rx,
2567            Box::new(cbs),
2568        );
2569
2570        let info = make_interface_info(1);
2571        driver.engine.register_interface(info.clone());
2572        let (writer, sent) = MockWriter::new();
2573        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
2574
2575        driver.management_config.enable_remote_management = true;
2576        driver.transport_identity = Some(identity);
2577        // Started just now - delay hasn't passed
2578        driver.started = time::now();
2579
2580        tx.send(Event::Tick).unwrap();
2581        tx.send(Event::Shutdown).unwrap();
2582        driver.run();
2583
2584        let sent_packets = sent.lock().unwrap();
2585        assert!(sent_packets.is_empty(),
2586            "No announces before startup delay");
2587    }
2588
2589    // =========================================================================
2590    // Phase 9c: Announce + Discovery tests
2591    // =========================================================================
2592
2593    #[test]
2594    fn announce_received_populates_known_destinations() {
2595        let (tx, rx) = event::channel();
2596        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2597        let mut driver = Driver::new(
2598            TransportConfig { transport_enabled: false, identity_hash: None },
2599            rx,
2600            Box::new(cbs),
2601        );
2602        let info = make_interface_info(1);
2603        driver.engine.register_interface(info);
2604        let (writer, _sent) = MockWriter::new();
2605        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
2606
2607        let identity = Identity::new(&mut OsRng);
2608        let announce_raw = build_announce_packet(&identity);
2609
2610        let dest_hash = rns_core::destination::destination_hash(
2611            "test", &["app"], Some(identity.hash()),
2612        );
2613
2614        tx.send(Event::Frame { interface_id: InterfaceId(1), data: announce_raw }).unwrap();
2615        tx.send(Event::Shutdown).unwrap();
2616        driver.run();
2617
2618        // known_destinations should be populated
2619        assert!(driver.known_destinations.contains_key(&dest_hash));
2620        let recalled = &driver.known_destinations[&dest_hash];
2621        assert_eq!(recalled.dest_hash.0, dest_hash);
2622        assert_eq!(recalled.identity_hash.0, *identity.hash());
2623        assert_eq!(&recalled.public_key, &identity.get_public_key().unwrap());
2624        assert_eq!(recalled.hops, 1);
2625    }
2626
2627    #[test]
2628    fn query_has_path() {
2629        let (tx, rx) = event::channel();
2630        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2631        let mut driver = Driver::new(
2632            TransportConfig { transport_enabled: false, identity_hash: None },
2633            rx,
2634            Box::new(cbs),
2635        );
2636        let info = make_interface_info(1);
2637        driver.engine.register_interface(info);
2638        let (writer, _sent) = MockWriter::new();
2639        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
2640
2641        // No path yet
2642        let (resp_tx, resp_rx) = mpsc::channel();
2643        tx.send(Event::Query(QueryRequest::HasPath { dest_hash: [0xAA; 16] }, resp_tx)).unwrap();
2644
2645        // Feed an announce to create a path
2646        let identity = Identity::new(&mut OsRng);
2647        let announce_raw = build_announce_packet(&identity);
2648        let dest_hash = rns_core::destination::destination_hash(
2649            "test", &["app"], Some(identity.hash()),
2650        );
2651        tx.send(Event::Frame { interface_id: InterfaceId(1), data: announce_raw }).unwrap();
2652
2653        let (resp_tx2, resp_rx2) = mpsc::channel();
2654        tx.send(Event::Query(QueryRequest::HasPath { dest_hash }, resp_tx2)).unwrap();
2655
2656        tx.send(Event::Shutdown).unwrap();
2657        driver.run();
2658
2659        // First query — no path
2660        match resp_rx.recv().unwrap() {
2661            QueryResponse::HasPath(false) => {}
2662            other => panic!("expected HasPath(false), got {:?}", other),
2663        }
2664
2665        // Second query — path exists
2666        match resp_rx2.recv().unwrap() {
2667            QueryResponse::HasPath(true) => {}
2668            other => panic!("expected HasPath(true), got {:?}", other),
2669        }
2670    }
2671
2672    #[test]
2673    fn query_hops_to() {
2674        let (tx, rx) = event::channel();
2675        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2676        let mut driver = Driver::new(
2677            TransportConfig { transport_enabled: false, identity_hash: None },
2678            rx,
2679            Box::new(cbs),
2680        );
2681        let info = make_interface_info(1);
2682        driver.engine.register_interface(info);
2683        let (writer, _sent) = MockWriter::new();
2684        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
2685
2686        // Feed an announce
2687        let identity = Identity::new(&mut OsRng);
2688        let announce_raw = build_announce_packet(&identity);
2689        let dest_hash = rns_core::destination::destination_hash(
2690            "test", &["app"], Some(identity.hash()),
2691        );
2692
2693        tx.send(Event::Frame { interface_id: InterfaceId(1), data: announce_raw }).unwrap();
2694
2695        let (resp_tx, resp_rx) = mpsc::channel();
2696        tx.send(Event::Query(QueryRequest::HopsTo { dest_hash }, resp_tx)).unwrap();
2697        tx.send(Event::Shutdown).unwrap();
2698        driver.run();
2699
2700        match resp_rx.recv().unwrap() {
2701            QueryResponse::HopsTo(Some(1)) => {}
2702            other => panic!("expected HopsTo(Some(1)), got {:?}", other),
2703        }
2704    }
2705
2706    #[test]
2707    fn query_recall_identity() {
2708        let (tx, rx) = event::channel();
2709        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2710        let mut driver = Driver::new(
2711            TransportConfig { transport_enabled: false, identity_hash: None },
2712            rx,
2713            Box::new(cbs),
2714        );
2715        let info = make_interface_info(1);
2716        driver.engine.register_interface(info);
2717        let (writer, _sent) = MockWriter::new();
2718        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
2719
2720        let identity = Identity::new(&mut OsRng);
2721        let announce_raw = build_announce_packet(&identity);
2722        let dest_hash = rns_core::destination::destination_hash(
2723            "test", &["app"], Some(identity.hash()),
2724        );
2725
2726        tx.send(Event::Frame { interface_id: InterfaceId(1), data: announce_raw }).unwrap();
2727
2728        // Recall identity
2729        let (resp_tx, resp_rx) = mpsc::channel();
2730        tx.send(Event::Query(QueryRequest::RecallIdentity { dest_hash }, resp_tx)).unwrap();
2731
2732        // Also recall unknown destination
2733        let (resp_tx2, resp_rx2) = mpsc::channel();
2734        tx.send(Event::Query(QueryRequest::RecallIdentity { dest_hash: [0xFF; 16] }, resp_tx2)).unwrap();
2735
2736        tx.send(Event::Shutdown).unwrap();
2737        driver.run();
2738
2739        match resp_rx.recv().unwrap() {
2740            QueryResponse::RecallIdentity(Some(recalled)) => {
2741                assert_eq!(recalled.dest_hash.0, dest_hash);
2742                assert_eq!(recalled.identity_hash.0, *identity.hash());
2743                assert_eq!(recalled.public_key, identity.get_public_key().unwrap());
2744                assert_eq!(recalled.hops, 1);
2745            }
2746            other => panic!("expected RecallIdentity(Some(..)), got {:?}", other),
2747        }
2748
2749        match resp_rx2.recv().unwrap() {
2750            QueryResponse::RecallIdentity(None) => {}
2751            other => panic!("expected RecallIdentity(None), got {:?}", other),
2752        }
2753    }
2754
2755    #[test]
2756    fn request_path_sends_packet() {
2757        let (tx, rx) = event::channel();
2758        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2759        let mut driver = Driver::new(
2760            TransportConfig { transport_enabled: false, identity_hash: None },
2761            rx,
2762            Box::new(cbs),
2763        );
2764        let info = make_interface_info(1);
2765        driver.engine.register_interface(info);
2766        let (writer, sent) = MockWriter::new();
2767        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
2768
2769        // Send path request
2770        tx.send(Event::RequestPath { dest_hash: [0xAA; 16] }).unwrap();
2771        tx.send(Event::Shutdown).unwrap();
2772        driver.run();
2773
2774        // Should have sent a packet on the wire (broadcast)
2775        let sent_packets = sent.lock().unwrap();
2776        assert!(!sent_packets.is_empty(), "Path request should be sent on wire");
2777
2778        // Verify the sent packet is a DATA PLAIN BROADCAST packet
2779        let raw = &sent_packets[0];
2780        let flags = rns_core::packet::PacketFlags::unpack(raw[0] & 0x7F);
2781        assert_eq!(flags.packet_type, constants::PACKET_TYPE_DATA);
2782        assert_eq!(flags.destination_type, constants::DESTINATION_PLAIN);
2783        assert_eq!(flags.transport_type, constants::TRANSPORT_BROADCAST);
2784    }
2785
2786    #[test]
2787    fn request_path_includes_transport_id() {
2788        let (tx, rx) = event::channel();
2789        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2790        let mut driver = Driver::new(
2791            TransportConfig { transport_enabled: true, identity_hash: Some([0xBB; 16]) },
2792            rx,
2793            Box::new(cbs),
2794        );
2795        let info = make_interface_info(1);
2796        driver.engine.register_interface(info);
2797        let (writer, sent) = MockWriter::new();
2798        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
2799
2800        tx.send(Event::RequestPath { dest_hash: [0xAA; 16] }).unwrap();
2801        tx.send(Event::Shutdown).unwrap();
2802        driver.run();
2803
2804        let sent_packets = sent.lock().unwrap();
2805        assert!(!sent_packets.is_empty());
2806
2807        // Unpack the packet to check data length includes transport_id
2808        let raw = &sent_packets[0];
2809        if let Ok(packet) = RawPacket::unpack(raw) {
2810            // Data: dest_hash(16) + transport_id(16) + random_tag(16) = 48 bytes
2811            assert_eq!(packet.data.len(), 48, "Path request data should be 48 bytes with transport_id");
2812            assert_eq!(&packet.data[..16], &[0xAA; 16], "First 16 bytes should be dest_hash");
2813            assert_eq!(&packet.data[16..32], &[0xBB; 16], "Next 16 bytes should be transport_id");
2814        } else {
2815            panic!("Could not unpack sent packet");
2816        }
2817    }
2818
2819    #[test]
2820    fn path_request_dest_registered() {
2821        let (tx, rx) = event::channel();
2822        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2823        let driver = Driver::new(
2824            TransportConfig { transport_enabled: false, identity_hash: None },
2825            rx,
2826            Box::new(cbs),
2827        );
2828
2829        // The path request dest should be registered as a local PLAIN destination
2830        let expected_dest = rns_core::destination::destination_hash(
2831            "rnstransport", &["path", "request"], None,
2832        );
2833        assert_eq!(driver.path_request_dest, expected_dest);
2834
2835        drop(tx);
2836    }
2837
2838    // =========================================================================
2839    // Phase 9d: send_packet + proofs tests
2840    // =========================================================================
2841
2842    #[test]
2843    fn register_proof_strategy_event() {
2844        let (tx, rx) = event::channel();
2845        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2846        let mut driver = Driver::new(
2847            TransportConfig { transport_enabled: false, identity_hash: None },
2848            rx,
2849            Box::new(cbs),
2850        );
2851
2852        let dest = [0xAA; 16];
2853        let identity = Identity::new(&mut OsRng);
2854        let prv_key = identity.get_private_key().unwrap();
2855
2856        tx.send(Event::RegisterProofStrategy {
2857            dest_hash: dest,
2858            strategy: rns_core::types::ProofStrategy::ProveAll,
2859            signing_key: Some(prv_key),
2860        }).unwrap();
2861        tx.send(Event::Shutdown).unwrap();
2862        driver.run();
2863
2864        assert!(driver.proof_strategies.contains_key(&dest));
2865        let (strategy, ref id_opt) = driver.proof_strategies[&dest];
2866        assert_eq!(strategy, rns_core::types::ProofStrategy::ProveAll);
2867        assert!(id_opt.is_some());
2868    }
2869
2870    #[test]
2871    fn register_proof_strategy_prove_none_no_identity() {
2872        let (tx, rx) = event::channel();
2873        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2874        let mut driver = Driver::new(
2875            TransportConfig { transport_enabled: false, identity_hash: None },
2876            rx,
2877            Box::new(cbs),
2878        );
2879
2880        let dest = [0xBB; 16];
2881        tx.send(Event::RegisterProofStrategy {
2882            dest_hash: dest,
2883            strategy: rns_core::types::ProofStrategy::ProveNone,
2884            signing_key: None,
2885        }).unwrap();
2886        tx.send(Event::Shutdown).unwrap();
2887        driver.run();
2888
2889        assert!(driver.proof_strategies.contains_key(&dest));
2890        let (strategy, ref id_opt) = driver.proof_strategies[&dest];
2891        assert_eq!(strategy, rns_core::types::ProofStrategy::ProveNone);
2892        assert!(id_opt.is_none());
2893    }
2894
2895    #[test]
2896    fn send_outbound_tracks_sent_packets() {
2897        let (tx, rx) = event::channel();
2898        let (cbs, _, _, _, _, _) = MockCallbacks::new();
2899        let mut driver = Driver::new(
2900            TransportConfig { transport_enabled: false, identity_hash: None },
2901            rx,
2902            Box::new(cbs),
2903        );
2904        let info = make_interface_info(1);
2905        driver.engine.register_interface(info);
2906        let (writer, _sent) = MockWriter::new();
2907        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
2908
2909        // Build a DATA packet
2910        let dest = [0xCC; 16];
2911        let flags = PacketFlags {
2912            header_type: constants::HEADER_1,
2913            context_flag: constants::FLAG_UNSET,
2914            transport_type: constants::TRANSPORT_BROADCAST,
2915            destination_type: constants::DESTINATION_PLAIN,
2916            packet_type: constants::PACKET_TYPE_DATA,
2917        };
2918        let packet = RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"test data").unwrap();
2919        let expected_hash = packet.packet_hash;
2920
2921        tx.send(Event::SendOutbound {
2922            raw: packet.raw,
2923            dest_type: constants::DESTINATION_PLAIN,
2924            attached_interface: None,
2925        }).unwrap();
2926        tx.send(Event::Shutdown).unwrap();
2927        driver.run();
2928
2929        // Should be tracking the sent packet
2930        assert!(driver.sent_packets.contains_key(&expected_hash));
2931        let (tracked_dest, _sent_time) = &driver.sent_packets[&expected_hash];
2932        assert_eq!(tracked_dest, &dest);
2933    }
2934
2935    #[test]
2936    fn prove_all_generates_proof_on_delivery() {
2937        let (tx, rx) = event::channel();
2938        let (cbs, _, _, deliveries, _, _) = MockCallbacks::new();
2939        let mut driver = Driver::new(
2940            TransportConfig { transport_enabled: false, identity_hash: None },
2941            rx,
2942            Box::new(cbs),
2943        );
2944        let info = make_interface_info(1);
2945        driver.engine.register_interface(info);
2946        let (writer, sent) = MockWriter::new();
2947        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
2948
2949        // Register a destination with ProveAll
2950        let dest = [0xDD; 16];
2951        let identity = Identity::new(&mut OsRng);
2952        let prv_key = identity.get_private_key().unwrap();
2953        driver.engine.register_destination(dest, constants::DESTINATION_SINGLE);
2954        driver.proof_strategies.insert(dest, (
2955            rns_core::types::ProofStrategy::ProveAll,
2956            Some(Identity::from_private_key(&prv_key)),
2957        ));
2958
2959        // Send a DATA packet to that destination
2960        let flags = PacketFlags {
2961            header_type: constants::HEADER_1,
2962            context_flag: constants::FLAG_UNSET,
2963            transport_type: constants::TRANSPORT_BROADCAST,
2964            destination_type: constants::DESTINATION_SINGLE,
2965            packet_type: constants::PACKET_TYPE_DATA,
2966        };
2967        let packet = RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"hello").unwrap();
2968
2969        tx.send(Event::Frame { interface_id: InterfaceId(1), data: packet.raw }).unwrap();
2970        tx.send(Event::Shutdown).unwrap();
2971        driver.run();
2972
2973        // Should have delivered the packet
2974        assert_eq!(deliveries.lock().unwrap().len(), 1);
2975
2976        // Should have sent at least one proof packet on the wire
2977        let sent_packets = sent.lock().unwrap();
2978        // The original DATA is not sent out (it was delivered locally), but a PROOF should be
2979        let has_proof = sent_packets.iter().any(|raw| {
2980            let flags = PacketFlags::unpack(raw[0] & 0x7F);
2981            flags.packet_type == constants::PACKET_TYPE_PROOF
2982        });
2983        assert!(has_proof, "ProveAll should generate a proof packet: sent {} packets", sent_packets.len());
2984    }
2985
2986    #[test]
2987    fn prove_none_does_not_generate_proof() {
2988        let (tx, rx) = event::channel();
2989        let (cbs, _, _, deliveries, _, _) = MockCallbacks::new();
2990        let mut driver = Driver::new(
2991            TransportConfig { transport_enabled: false, identity_hash: None },
2992            rx,
2993            Box::new(cbs),
2994        );
2995        let info = make_interface_info(1);
2996        driver.engine.register_interface(info);
2997        let (writer, sent) = MockWriter::new();
2998        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
2999
3000        // Register a destination with ProveNone
3001        let dest = [0xDD; 16];
3002        driver.engine.register_destination(dest, constants::DESTINATION_SINGLE);
3003        driver.proof_strategies.insert(dest, (
3004            rns_core::types::ProofStrategy::ProveNone,
3005            None,
3006        ));
3007
3008        // Send a DATA packet to that destination
3009        let flags = PacketFlags {
3010            header_type: constants::HEADER_1,
3011            context_flag: constants::FLAG_UNSET,
3012            transport_type: constants::TRANSPORT_BROADCAST,
3013            destination_type: constants::DESTINATION_SINGLE,
3014            packet_type: constants::PACKET_TYPE_DATA,
3015        };
3016        let packet = RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"hello").unwrap();
3017
3018        tx.send(Event::Frame { interface_id: InterfaceId(1), data: packet.raw }).unwrap();
3019        tx.send(Event::Shutdown).unwrap();
3020        driver.run();
3021
3022        // Should have delivered the packet
3023        assert_eq!(deliveries.lock().unwrap().len(), 1);
3024
3025        // Should NOT have sent any proof
3026        let sent_packets = sent.lock().unwrap();
3027        let has_proof = sent_packets.iter().any(|raw| {
3028            let flags = PacketFlags::unpack(raw[0] & 0x7F);
3029            flags.packet_type == constants::PACKET_TYPE_PROOF
3030        });
3031        assert!(!has_proof, "ProveNone should not generate a proof packet");
3032    }
3033
3034    #[test]
3035    fn no_proof_strategy_does_not_generate_proof() {
3036        let (tx, rx) = event::channel();
3037        let (cbs, _, _, deliveries, _, _) = MockCallbacks::new();
3038        let mut driver = Driver::new(
3039            TransportConfig { transport_enabled: false, identity_hash: None },
3040            rx,
3041            Box::new(cbs),
3042        );
3043        let info = make_interface_info(1);
3044        driver.engine.register_interface(info);
3045        let (writer, sent) = MockWriter::new();
3046        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
3047
3048        // Register destination but NO proof strategy
3049        let dest = [0xDD; 16];
3050        driver.engine.register_destination(dest, constants::DESTINATION_SINGLE);
3051
3052        let flags = PacketFlags {
3053            header_type: constants::HEADER_1,
3054            context_flag: constants::FLAG_UNSET,
3055            transport_type: constants::TRANSPORT_BROADCAST,
3056            destination_type: constants::DESTINATION_SINGLE,
3057            packet_type: constants::PACKET_TYPE_DATA,
3058        };
3059        let packet = RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"hello").unwrap();
3060
3061        tx.send(Event::Frame { interface_id: InterfaceId(1), data: packet.raw }).unwrap();
3062        tx.send(Event::Shutdown).unwrap();
3063        driver.run();
3064
3065        assert_eq!(deliveries.lock().unwrap().len(), 1);
3066
3067        let sent_packets = sent.lock().unwrap();
3068        let has_proof = sent_packets.iter().any(|raw| {
3069            let flags = PacketFlags::unpack(raw[0] & 0x7F);
3070            flags.packet_type == constants::PACKET_TYPE_PROOF
3071        });
3072        assert!(!has_proof, "No proof strategy means no proof generated");
3073    }
3074
3075    #[test]
3076    fn prove_app_calls_callback() {
3077        let (tx, rx) = event::channel();
3078        let proof_requested = Arc::new(Mutex::new(Vec::new()));
3079        let deliveries = Arc::new(Mutex::new(Vec::new()));
3080        let cbs = MockCallbacks {
3081            announces: Arc::new(Mutex::new(Vec::new())),
3082            paths: Arc::new(Mutex::new(Vec::new())),
3083            deliveries: deliveries.clone(),
3084            iface_ups: Arc::new(Mutex::new(Vec::new())),
3085            iface_downs: Arc::new(Mutex::new(Vec::new())),
3086            link_established: Arc::new(Mutex::new(Vec::new())),
3087            link_closed: Arc::new(Mutex::new(Vec::new())),
3088            remote_identified: Arc::new(Mutex::new(Vec::new())),
3089            resources_received: Arc::new(Mutex::new(Vec::new())),
3090            resource_completed: Arc::new(Mutex::new(Vec::new())),
3091            resource_failed: Arc::new(Mutex::new(Vec::new())),
3092            channel_messages: Arc::new(Mutex::new(Vec::new())),
3093            link_data: Arc::new(Mutex::new(Vec::new())),
3094            responses: Arc::new(Mutex::new(Vec::new())),
3095            proofs: Arc::new(Mutex::new(Vec::new())),
3096            proof_requested: proof_requested.clone(),
3097        };
3098
3099        let mut driver = Driver::new(
3100            TransportConfig { transport_enabled: false, identity_hash: None },
3101            rx,
3102            Box::new(cbs),
3103        );
3104        let info = make_interface_info(1);
3105        driver.engine.register_interface(info);
3106        let (writer, sent) = MockWriter::new();
3107        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
3108
3109        // Register dest with ProveApp
3110        let dest = [0xDD; 16];
3111        let identity = Identity::new(&mut OsRng);
3112        let prv_key = identity.get_private_key().unwrap();
3113        driver.engine.register_destination(dest, constants::DESTINATION_SINGLE);
3114        driver.proof_strategies.insert(dest, (
3115            rns_core::types::ProofStrategy::ProveApp,
3116            Some(Identity::from_private_key(&prv_key)),
3117        ));
3118
3119        let flags = PacketFlags {
3120            header_type: constants::HEADER_1,
3121            context_flag: constants::FLAG_UNSET,
3122            transport_type: constants::TRANSPORT_BROADCAST,
3123            destination_type: constants::DESTINATION_SINGLE,
3124            packet_type: constants::PACKET_TYPE_DATA,
3125        };
3126        let packet = RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"app test").unwrap();
3127
3128        tx.send(Event::Frame { interface_id: InterfaceId(1), data: packet.raw }).unwrap();
3129        tx.send(Event::Shutdown).unwrap();
3130        driver.run();
3131
3132        // on_proof_requested should have been called
3133        let prs = proof_requested.lock().unwrap();
3134        assert_eq!(prs.len(), 1);
3135        assert_eq!(prs[0].0, DestHash(dest));
3136
3137        // Since our mock returns true, a proof should also have been sent
3138        let sent_packets = sent.lock().unwrap();
3139        let has_proof = sent_packets.iter().any(|raw| {
3140            let flags = PacketFlags::unpack(raw[0] & 0x7F);
3141            flags.packet_type == constants::PACKET_TYPE_PROOF
3142        });
3143        assert!(has_proof, "ProveApp (callback returns true) should generate a proof");
3144    }
3145
3146    #[test]
3147    fn inbound_proof_fires_callback() {
3148        let (tx, rx) = event::channel();
3149        let proofs = Arc::new(Mutex::new(Vec::new()));
3150        let cbs = MockCallbacks {
3151            announces: Arc::new(Mutex::new(Vec::new())),
3152            paths: Arc::new(Mutex::new(Vec::new())),
3153            deliveries: Arc::new(Mutex::new(Vec::new())),
3154            iface_ups: Arc::new(Mutex::new(Vec::new())),
3155            iface_downs: Arc::new(Mutex::new(Vec::new())),
3156            link_established: Arc::new(Mutex::new(Vec::new())),
3157            link_closed: Arc::new(Mutex::new(Vec::new())),
3158            remote_identified: Arc::new(Mutex::new(Vec::new())),
3159            resources_received: Arc::new(Mutex::new(Vec::new())),
3160            resource_completed: Arc::new(Mutex::new(Vec::new())),
3161            resource_failed: Arc::new(Mutex::new(Vec::new())),
3162            channel_messages: Arc::new(Mutex::new(Vec::new())),
3163            link_data: Arc::new(Mutex::new(Vec::new())),
3164            responses: Arc::new(Mutex::new(Vec::new())),
3165            proofs: proofs.clone(),
3166            proof_requested: Arc::new(Mutex::new(Vec::new())),
3167        };
3168
3169        let mut driver = Driver::new(
3170            TransportConfig { transport_enabled: false, identity_hash: None },
3171            rx,
3172            Box::new(cbs),
3173        );
3174        let info = make_interface_info(1);
3175        driver.engine.register_interface(info);
3176        let (writer, _sent) = MockWriter::new();
3177        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
3178
3179        // Register a destination so proof packets can be delivered locally
3180        let dest = [0xEE; 16];
3181        driver.engine.register_destination(dest, constants::DESTINATION_SINGLE);
3182
3183        // Simulate a sent packet that we're tracking
3184        let tracked_hash = [0x42u8; 32];
3185        let sent_time = time::now() - 0.5; // 500ms ago
3186        driver.sent_packets.insert(tracked_hash, (dest, sent_time));
3187
3188        // Build a PROOF packet with the tracked hash + dummy signature
3189        let mut proof_data = Vec::new();
3190        proof_data.extend_from_slice(&tracked_hash);
3191        proof_data.extend_from_slice(&[0xAA; 64]); // dummy signature
3192
3193        let flags = PacketFlags {
3194            header_type: constants::HEADER_1,
3195            context_flag: constants::FLAG_UNSET,
3196            transport_type: constants::TRANSPORT_BROADCAST,
3197            destination_type: constants::DESTINATION_SINGLE,
3198            packet_type: constants::PACKET_TYPE_PROOF,
3199        };
3200        let packet = RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, &proof_data).unwrap();
3201
3202        tx.send(Event::Frame { interface_id: InterfaceId(1), data: packet.raw }).unwrap();
3203        tx.send(Event::Shutdown).unwrap();
3204        driver.run();
3205
3206        // on_proof callback should have been fired
3207        let proof_list = proofs.lock().unwrap();
3208        assert_eq!(proof_list.len(), 1);
3209        assert_eq!(proof_list[0].0, DestHash(dest));
3210        assert_eq!(proof_list[0].1, PacketHash(tracked_hash));
3211        assert!(proof_list[0].2 >= 0.4, "RTT should be approximately 0.5s, got {}", proof_list[0].2);
3212
3213        // Tracked packet should be removed
3214        assert!(!driver.sent_packets.contains_key(&tracked_hash));
3215    }
3216
3217    #[test]
3218    fn inbound_proof_for_unknown_packet_is_ignored() {
3219        let (tx, rx) = event::channel();
3220        let proofs = Arc::new(Mutex::new(Vec::new()));
3221        let cbs = MockCallbacks {
3222            announces: Arc::new(Mutex::new(Vec::new())),
3223            paths: Arc::new(Mutex::new(Vec::new())),
3224            deliveries: Arc::new(Mutex::new(Vec::new())),
3225            iface_ups: Arc::new(Mutex::new(Vec::new())),
3226            iface_downs: Arc::new(Mutex::new(Vec::new())),
3227            link_established: Arc::new(Mutex::new(Vec::new())),
3228            link_closed: Arc::new(Mutex::new(Vec::new())),
3229            remote_identified: Arc::new(Mutex::new(Vec::new())),
3230            resources_received: Arc::new(Mutex::new(Vec::new())),
3231            resource_completed: Arc::new(Mutex::new(Vec::new())),
3232            resource_failed: Arc::new(Mutex::new(Vec::new())),
3233            channel_messages: Arc::new(Mutex::new(Vec::new())),
3234            link_data: Arc::new(Mutex::new(Vec::new())),
3235            responses: Arc::new(Mutex::new(Vec::new())),
3236            proofs: proofs.clone(),
3237            proof_requested: Arc::new(Mutex::new(Vec::new())),
3238        };
3239
3240        let mut driver = Driver::new(
3241            TransportConfig { transport_enabled: false, identity_hash: None },
3242            rx,
3243            Box::new(cbs),
3244        );
3245        let info = make_interface_info(1);
3246        driver.engine.register_interface(info);
3247        let (writer, _sent) = MockWriter::new();
3248        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
3249
3250        let dest = [0xEE; 16];
3251        driver.engine.register_destination(dest, constants::DESTINATION_SINGLE);
3252
3253        // Build a PROOF packet for an untracked hash
3254        let unknown_hash = [0xFF; 32];
3255        let mut proof_data = Vec::new();
3256        proof_data.extend_from_slice(&unknown_hash);
3257        proof_data.extend_from_slice(&[0xAA; 64]);
3258
3259        let flags = PacketFlags {
3260            header_type: constants::HEADER_1,
3261            context_flag: constants::FLAG_UNSET,
3262            transport_type: constants::TRANSPORT_BROADCAST,
3263            destination_type: constants::DESTINATION_SINGLE,
3264            packet_type: constants::PACKET_TYPE_PROOF,
3265        };
3266        let packet = RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, &proof_data).unwrap();
3267
3268        tx.send(Event::Frame { interface_id: InterfaceId(1), data: packet.raw }).unwrap();
3269        tx.send(Event::Shutdown).unwrap();
3270        driver.run();
3271
3272        // on_proof should NOT have been called
3273        assert!(proofs.lock().unwrap().is_empty());
3274    }
3275
3276    #[test]
3277    fn inbound_proof_with_valid_signature_fires_callback() {
3278        // When the destination IS in known_destinations, the proof signature is verified
3279        let (tx, rx) = event::channel();
3280        let proofs = Arc::new(Mutex::new(Vec::new()));
3281        let cbs = MockCallbacks {
3282            announces: Arc::new(Mutex::new(Vec::new())),
3283            paths: Arc::new(Mutex::new(Vec::new())),
3284            deliveries: Arc::new(Mutex::new(Vec::new())),
3285            iface_ups: Arc::new(Mutex::new(Vec::new())),
3286            iface_downs: Arc::new(Mutex::new(Vec::new())),
3287            link_established: Arc::new(Mutex::new(Vec::new())),
3288            link_closed: Arc::new(Mutex::new(Vec::new())),
3289            remote_identified: Arc::new(Mutex::new(Vec::new())),
3290            resources_received: Arc::new(Mutex::new(Vec::new())),
3291            resource_completed: Arc::new(Mutex::new(Vec::new())),
3292            resource_failed: Arc::new(Mutex::new(Vec::new())),
3293            channel_messages: Arc::new(Mutex::new(Vec::new())),
3294            link_data: Arc::new(Mutex::new(Vec::new())),
3295            responses: Arc::new(Mutex::new(Vec::new())),
3296            proofs: proofs.clone(),
3297            proof_requested: Arc::new(Mutex::new(Vec::new())),
3298        };
3299
3300        let mut driver = Driver::new(
3301            TransportConfig { transport_enabled: false, identity_hash: None },
3302            rx,
3303            Box::new(cbs),
3304        );
3305        let info = make_interface_info(1);
3306        driver.engine.register_interface(info);
3307        let (writer, _sent) = MockWriter::new();
3308        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
3309
3310        let dest = [0xEE; 16];
3311        driver.engine.register_destination(dest, constants::DESTINATION_SINGLE);
3312
3313        // Create real identity and add to known_destinations
3314        let identity = Identity::new(&mut OsRng);
3315        let pub_key = identity.get_public_key();
3316        driver.known_destinations.insert(dest, crate::destination::AnnouncedIdentity {
3317            dest_hash: DestHash(dest),
3318            identity_hash: IdentityHash(*identity.hash()),
3319            public_key: pub_key.unwrap(),
3320            app_data: None,
3321            hops: 0,
3322            received_at: time::now(),
3323        });
3324
3325        // Sign a packet hash with the identity
3326        let tracked_hash = [0x42u8; 32];
3327        let sent_time = time::now() - 0.5;
3328        driver.sent_packets.insert(tracked_hash, (dest, sent_time));
3329
3330        let signature = identity.sign(&tracked_hash).unwrap();
3331        let mut proof_data = Vec::new();
3332        proof_data.extend_from_slice(&tracked_hash);
3333        proof_data.extend_from_slice(&signature);
3334
3335        let flags = PacketFlags {
3336            header_type: constants::HEADER_1,
3337            context_flag: constants::FLAG_UNSET,
3338            transport_type: constants::TRANSPORT_BROADCAST,
3339            destination_type: constants::DESTINATION_SINGLE,
3340            packet_type: constants::PACKET_TYPE_PROOF,
3341        };
3342        let packet = RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, &proof_data).unwrap();
3343
3344        tx.send(Event::Frame { interface_id: InterfaceId(1), data: packet.raw }).unwrap();
3345        tx.send(Event::Shutdown).unwrap();
3346        driver.run();
3347
3348        // Valid signature: on_proof should fire
3349        let proof_list = proofs.lock().unwrap();
3350        assert_eq!(proof_list.len(), 1);
3351        assert_eq!(proof_list[0].0, DestHash(dest));
3352        assert_eq!(proof_list[0].1, PacketHash(tracked_hash));
3353    }
3354
3355    #[test]
3356    fn inbound_proof_with_invalid_signature_rejected() {
3357        // When known_destinations has the public key, bad signatures are rejected
3358        let (tx, rx) = event::channel();
3359        let proofs = Arc::new(Mutex::new(Vec::new()));
3360        let cbs = MockCallbacks {
3361            announces: Arc::new(Mutex::new(Vec::new())),
3362            paths: Arc::new(Mutex::new(Vec::new())),
3363            deliveries: Arc::new(Mutex::new(Vec::new())),
3364            iface_ups: Arc::new(Mutex::new(Vec::new())),
3365            iface_downs: Arc::new(Mutex::new(Vec::new())),
3366            link_established: Arc::new(Mutex::new(Vec::new())),
3367            link_closed: Arc::new(Mutex::new(Vec::new())),
3368            remote_identified: Arc::new(Mutex::new(Vec::new())),
3369            resources_received: Arc::new(Mutex::new(Vec::new())),
3370            resource_completed: Arc::new(Mutex::new(Vec::new())),
3371            resource_failed: Arc::new(Mutex::new(Vec::new())),
3372            channel_messages: Arc::new(Mutex::new(Vec::new())),
3373            link_data: Arc::new(Mutex::new(Vec::new())),
3374            responses: Arc::new(Mutex::new(Vec::new())),
3375            proofs: proofs.clone(),
3376            proof_requested: Arc::new(Mutex::new(Vec::new())),
3377        };
3378
3379        let mut driver = Driver::new(
3380            TransportConfig { transport_enabled: false, identity_hash: None },
3381            rx,
3382            Box::new(cbs),
3383        );
3384        let info = make_interface_info(1);
3385        driver.engine.register_interface(info);
3386        let (writer, _sent) = MockWriter::new();
3387        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
3388
3389        let dest = [0xEE; 16];
3390        driver.engine.register_destination(dest, constants::DESTINATION_SINGLE);
3391
3392        // Create identity and add to known_destinations
3393        let identity = Identity::new(&mut OsRng);
3394        let pub_key = identity.get_public_key();
3395        driver.known_destinations.insert(dest, crate::destination::AnnouncedIdentity {
3396            dest_hash: DestHash(dest),
3397            identity_hash: IdentityHash(*identity.hash()),
3398            public_key: pub_key.unwrap(),
3399            app_data: None,
3400            hops: 0,
3401            received_at: time::now(),
3402        });
3403
3404        // Track a sent packet
3405        let tracked_hash = [0x42u8; 32];
3406        let sent_time = time::now() - 0.5;
3407        driver.sent_packets.insert(tracked_hash, (dest, sent_time));
3408
3409        // Use WRONG signature (all 0xAA — invalid for this identity)
3410        let mut proof_data = Vec::new();
3411        proof_data.extend_from_slice(&tracked_hash);
3412        proof_data.extend_from_slice(&[0xAA; 64]);
3413
3414        let flags = PacketFlags {
3415            header_type: constants::HEADER_1,
3416            context_flag: constants::FLAG_UNSET,
3417            transport_type: constants::TRANSPORT_BROADCAST,
3418            destination_type: constants::DESTINATION_SINGLE,
3419            packet_type: constants::PACKET_TYPE_PROOF,
3420        };
3421        let packet = RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, &proof_data).unwrap();
3422
3423        tx.send(Event::Frame { interface_id: InterfaceId(1), data: packet.raw }).unwrap();
3424        tx.send(Event::Shutdown).unwrap();
3425        driver.run();
3426
3427        // Invalid signature: on_proof should NOT fire
3428        assert!(proofs.lock().unwrap().is_empty());
3429    }
3430
3431    #[test]
3432    fn proof_data_is_valid_explicit_proof() {
3433        // Verify that the proof generated by ProveAll is a valid explicit proof
3434        let (tx, rx) = event::channel();
3435        let (cbs, _, _, _, _, _) = MockCallbacks::new();
3436        let mut driver = Driver::new(
3437            TransportConfig { transport_enabled: false, identity_hash: None },
3438            rx,
3439            Box::new(cbs),
3440        );
3441        let info = make_interface_info(1);
3442        driver.engine.register_interface(info);
3443        let (writer, sent) = MockWriter::new();
3444        driver.interfaces.insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
3445
3446        let dest = [0xDD; 16];
3447        let identity = Identity::new(&mut OsRng);
3448        let prv_key = identity.get_private_key().unwrap();
3449        driver.engine.register_destination(dest, constants::DESTINATION_SINGLE);
3450        driver.proof_strategies.insert(dest, (
3451            rns_core::types::ProofStrategy::ProveAll,
3452            Some(Identity::from_private_key(&prv_key)),
3453        ));
3454
3455        let flags = PacketFlags {
3456            header_type: constants::HEADER_1,
3457            context_flag: constants::FLAG_UNSET,
3458            transport_type: constants::TRANSPORT_BROADCAST,
3459            destination_type: constants::DESTINATION_SINGLE,
3460            packet_type: constants::PACKET_TYPE_DATA,
3461        };
3462        let data_packet = RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"verify me").unwrap();
3463        let data_packet_hash = data_packet.packet_hash;
3464
3465        tx.send(Event::Frame { interface_id: InterfaceId(1), data: data_packet.raw }).unwrap();
3466        tx.send(Event::Shutdown).unwrap();
3467        driver.run();
3468
3469        // Find the proof packet in sent
3470        let sent_packets = sent.lock().unwrap();
3471        let proof_raw = sent_packets.iter().find(|raw| {
3472            let f = PacketFlags::unpack(raw[0] & 0x7F);
3473            f.packet_type == constants::PACKET_TYPE_PROOF
3474        });
3475        assert!(proof_raw.is_some(), "Should have sent a proof");
3476
3477        let proof_packet = RawPacket::unpack(proof_raw.unwrap()).unwrap();
3478        // Proof data should be 96 bytes: packet_hash(32) + signature(64)
3479        assert_eq!(proof_packet.data.len(), 96, "Explicit proof should be 96 bytes");
3480
3481        // Validate using rns-core's receipt module
3482        let result = rns_core::receipt::validate_proof(
3483            &proof_packet.data,
3484            &data_packet_hash,
3485            &Identity::from_private_key(&prv_key), // same identity
3486        );
3487        assert_eq!(result, rns_core::receipt::ProofResult::Valid);
3488    }
3489
3490    #[test]
3491    fn query_local_destinations_empty() {
3492        let (tx, rx) = event::channel();
3493        let (cbs, _, _, _, _, _) = MockCallbacks::new();
3494        let driver_config = TransportConfig { transport_enabled: false, identity_hash: None };
3495        let mut driver = Driver::new(driver_config, rx, Box::new(cbs));
3496
3497        let (resp_tx, resp_rx) = mpsc::channel();
3498        tx.send(Event::Query(QueryRequest::LocalDestinations, resp_tx)).unwrap();
3499        tx.send(Event::Shutdown).unwrap();
3500        driver.run();
3501
3502        match resp_rx.recv().unwrap() {
3503            QueryResponse::LocalDestinations(entries) => {
3504                // Should contain the two internal destinations (tunnel_synth + path_request)
3505                assert_eq!(entries.len(), 2);
3506                for entry in &entries {
3507                    assert_eq!(entry.dest_type, rns_core::constants::DESTINATION_PLAIN);
3508                }
3509            }
3510            other => panic!("expected LocalDestinations, got {:?}", other),
3511        }
3512    }
3513
3514    #[test]
3515    fn query_local_destinations_with_registered() {
3516        let (tx, rx) = event::channel();
3517        let (cbs, _, _, _, _, _) = MockCallbacks::new();
3518        let driver_config = TransportConfig { transport_enabled: false, identity_hash: None };
3519        let mut driver = Driver::new(driver_config, rx, Box::new(cbs));
3520
3521        let dest_hash = [0xAA; 16];
3522        tx.send(Event::RegisterDestination {
3523            dest_hash,
3524            dest_type: rns_core::constants::DESTINATION_SINGLE,
3525        }).unwrap();
3526
3527        let (resp_tx, resp_rx) = mpsc::channel();
3528        tx.send(Event::Query(QueryRequest::LocalDestinations, resp_tx)).unwrap();
3529        tx.send(Event::Shutdown).unwrap();
3530        driver.run();
3531
3532        match resp_rx.recv().unwrap() {
3533            QueryResponse::LocalDestinations(entries) => {
3534                // 2 internal + 1 registered
3535                assert_eq!(entries.len(), 3);
3536                assert!(entries.iter().any(|e| e.hash == dest_hash
3537                    && e.dest_type == rns_core::constants::DESTINATION_SINGLE));
3538            }
3539            other => panic!("expected LocalDestinations, got {:?}", other),
3540        }
3541    }
3542
3543    #[test]
3544    fn query_local_destinations_tracks_link_dest() {
3545        let (tx, rx) = event::channel();
3546        let (cbs, _, _, _, _, _) = MockCallbacks::new();
3547        let driver_config = TransportConfig { transport_enabled: false, identity_hash: None };
3548        let mut driver = Driver::new(driver_config, rx, Box::new(cbs));
3549
3550        let dest_hash = [0xBB; 16];
3551        tx.send(Event::RegisterLinkDestination {
3552            dest_hash,
3553            sig_prv_bytes: [0x11; 32],
3554            sig_pub_bytes: [0x22; 32],
3555        }).unwrap();
3556
3557        let (resp_tx, resp_rx) = mpsc::channel();
3558        tx.send(Event::Query(QueryRequest::LocalDestinations, resp_tx)).unwrap();
3559        tx.send(Event::Shutdown).unwrap();
3560        driver.run();
3561
3562        match resp_rx.recv().unwrap() {
3563            QueryResponse::LocalDestinations(entries) => {
3564                // 2 internal + 1 link destination
3565                assert_eq!(entries.len(), 3);
3566                assert!(entries.iter().any(|e| e.hash == dest_hash
3567                    && e.dest_type == rns_core::constants::DESTINATION_SINGLE));
3568            }
3569            other => panic!("expected LocalDestinations, got {:?}", other),
3570        }
3571    }
3572
3573    #[test]
3574    fn query_links_empty() {
3575        let (tx, rx) = event::channel();
3576        let (cbs, _, _, _, _, _) = MockCallbacks::new();
3577        let driver_config = TransportConfig { transport_enabled: false, identity_hash: None };
3578        let mut driver = Driver::new(driver_config, rx, Box::new(cbs));
3579
3580        let (resp_tx, resp_rx) = mpsc::channel();
3581        tx.send(Event::Query(QueryRequest::Links, resp_tx)).unwrap();
3582        tx.send(Event::Shutdown).unwrap();
3583        driver.run();
3584
3585        match resp_rx.recv().unwrap() {
3586            QueryResponse::Links(entries) => {
3587                assert!(entries.is_empty());
3588            }
3589            other => panic!("expected Links, got {:?}", other),
3590        }
3591    }
3592
3593    #[test]
3594    fn query_resources_empty() {
3595        let (tx, rx) = event::channel();
3596        let (cbs, _, _, _, _, _) = MockCallbacks::new();
3597        let driver_config = TransportConfig { transport_enabled: false, identity_hash: None };
3598        let mut driver = Driver::new(driver_config, rx, Box::new(cbs));
3599
3600        let (resp_tx, resp_rx) = mpsc::channel();
3601        tx.send(Event::Query(QueryRequest::Resources, resp_tx)).unwrap();
3602        tx.send(Event::Shutdown).unwrap();
3603        driver.run();
3604
3605        match resp_rx.recv().unwrap() {
3606            QueryResponse::Resources(entries) => {
3607                assert!(entries.is_empty());
3608            }
3609            other => panic!("expected Resources, got {:?}", other),
3610        }
3611    }
3612
3613    #[test]
3614    fn infer_interface_type_from_name() {
3615        assert_eq!(
3616            super::infer_interface_type("TCPServerInterface/Client-1234"),
3617            "TCPServerClientInterface"
3618        );
3619        assert_eq!(
3620            super::infer_interface_type("BackboneInterface/5"),
3621            "BackboneInterface"
3622        );
3623        assert_eq!(
3624            super::infer_interface_type("LocalInterface"),
3625            "LocalServerClientInterface"
3626        );
3627        assert_eq!(
3628            super::infer_interface_type("MyAutoGroup:fe80::1"),
3629            "AutoInterface"
3630        );
3631    }
3632}