New mesh_node.rs providing a production-ready node: - MeshNodeBuilder for fluent configuration - MeshConfig integration for all settings - MeshMetrics tracking for all operations - Rate limiting on incoming messages - Backpressure controller - Graceful shutdown via ShutdownCoordinator - Optional FappRouter based on capabilities - MeshRouter for envelope routing - TransportManager for multi-transport support Key APIs: - MeshNodeBuilder::new().fapp_relay().build() - node.process_incoming() with rate limiting + metrics - node.gc() for store/routing table cleanup - node.shutdown() for graceful termination 222 tests passing (203 lib + 3 fapp_flow + 16 multi_node)
290 lines
8.5 KiB
Rust
290 lines
8.5 KiB
Rust
//! Service router dispatches messages to service-specific handlers.
|
|
|
|
use std::collections::HashMap;
|
|
|
|
use crate::error::ServiceError;
|
|
use crate::message::{MessageType, ServiceMessage};
|
|
use crate::store::{ServiceStore, StoredMessage};
|
|
use crate::verification::TrustedVerifiers;
|
|
|
|
/// Action returned by a service handler.
|
|
#[derive(Debug)]
|
|
pub enum ServiceAction {
|
|
/// Message handled, do nothing more.
|
|
Handled,
|
|
/// Store the message locally.
|
|
Store,
|
|
/// Store and forward to peers.
|
|
StoreAndForward,
|
|
/// Forward without storing (pass-through relay).
|
|
ForwardOnly,
|
|
/// Drop the message silently.
|
|
Drop,
|
|
/// Send a response back.
|
|
Respond(ServiceMessage),
|
|
/// Reject with error.
|
|
Reject(ServiceError),
|
|
}
|
|
|
|
/// Trait for service-specific handlers.
|
|
pub trait ServiceHandler: Send + Sync {
|
|
/// The service ID this handler manages.
|
|
fn service_id(&self) -> u32;
|
|
|
|
/// Human-readable service name.
|
|
fn name(&self) -> &str;
|
|
|
|
/// Handle an incoming message.
|
|
fn handle(
|
|
&self,
|
|
message: &ServiceMessage,
|
|
context: &HandlerContext,
|
|
) -> Result<ServiceAction, ServiceError>;
|
|
|
|
/// Validate a message payload (service-specific logic).
|
|
fn validate(&self, message: &ServiceMessage) -> Result<(), ServiceError> {
|
|
// Default: accept all
|
|
let _ = message;
|
|
Ok(())
|
|
}
|
|
|
|
/// Check if a message matches a query.
|
|
fn matches_query(&self, announce: &StoredMessage, query: &ServiceMessage) -> bool;
|
|
}
|
|
|
|
/// Context passed to handlers.
|
|
pub struct HandlerContext<'a> {
|
|
/// Current node's capabilities.
|
|
pub capabilities: u16,
|
|
/// The store (for lookups during handle).
|
|
pub store: &'a ServiceStore,
|
|
/// Trusted verifiers for checking.
|
|
pub trusted_verifiers: &'a TrustedVerifiers,
|
|
/// Sender's public key (if known).
|
|
pub sender_public_key: Option<[u8; 32]>,
|
|
}
|
|
|
|
/// Routes messages to appropriate service handlers.
|
|
pub struct ServiceRouter {
|
|
/// Service ID -> Handler.
|
|
handlers: HashMap<u32, Box<dyn ServiceHandler>>,
|
|
/// Shared message store.
|
|
store: ServiceStore,
|
|
/// Node capabilities.
|
|
capabilities: u16,
|
|
/// Trusted verifiers.
|
|
trusted_verifiers: TrustedVerifiers,
|
|
/// Minimum verification level to accept announces (0 = any).
|
|
min_verification_level: u8,
|
|
}
|
|
|
|
impl ServiceRouter {
|
|
/// Create a new router.
|
|
pub fn new(capabilities: u16) -> Self {
|
|
Self {
|
|
handlers: HashMap::new(),
|
|
store: ServiceStore::new(),
|
|
capabilities,
|
|
trusted_verifiers: TrustedVerifiers::new(),
|
|
min_verification_level: 0,
|
|
}
|
|
}
|
|
|
|
/// Register a service handler.
|
|
pub fn register(&mut self, handler: Box<dyn ServiceHandler>) {
|
|
let id = handler.service_id();
|
|
self.handlers.insert(id, handler);
|
|
}
|
|
|
|
/// Set trusted verifiers.
|
|
pub fn set_trusted_verifiers(&mut self, verifiers: TrustedVerifiers) {
|
|
self.trusted_verifiers = verifiers;
|
|
}
|
|
|
|
/// Set minimum verification level for announces.
|
|
pub fn set_min_verification_level(&mut self, level: u8) {
|
|
self.min_verification_level = level;
|
|
}
|
|
|
|
/// Access the store.
|
|
pub fn store(&self) -> &ServiceStore {
|
|
&self.store
|
|
}
|
|
|
|
/// Mutable access to store.
|
|
pub fn store_mut(&mut self) -> &mut ServiceStore {
|
|
&mut self.store
|
|
}
|
|
|
|
/// Check if a service is registered.
|
|
pub fn has_service(&self, service_id: u32) -> bool {
|
|
self.handlers.contains_key(&service_id)
|
|
}
|
|
|
|
/// Handle an incoming message.
|
|
pub fn handle(
|
|
&mut self,
|
|
message: ServiceMessage,
|
|
sender_public_key: Option<[u8; 32]>,
|
|
) -> Result<ServiceAction, ServiceError> {
|
|
// Basic validation
|
|
if message.is_expired() {
|
|
return Err(ServiceError::Expired);
|
|
}
|
|
|
|
if message.hop_count > message.max_hops {
|
|
return Err(ServiceError::MaxHopsExceeded);
|
|
}
|
|
|
|
// Get handler
|
|
let handler = self
|
|
.handlers
|
|
.get(&message.service_id)
|
|
.ok_or(ServiceError::UnknownService(message.service_id))?;
|
|
|
|
// Validate message with handler
|
|
handler.validate(&message)?;
|
|
|
|
// Verify signature if we have public key
|
|
if let Some(pk) = &sender_public_key {
|
|
if !message.verify(pk) {
|
|
return Err(ServiceError::SignatureInvalid);
|
|
}
|
|
}
|
|
|
|
// Check verification level for announces
|
|
if message.message_type == MessageType::Announce && self.min_verification_level > 0 {
|
|
let level = self
|
|
.trusted_verifiers
|
|
.highest_level(&message.verifications, &message.sender_address);
|
|
if (level as u8) < self.min_verification_level {
|
|
return Err(ServiceError::VerificationRequired(self.min_verification_level));
|
|
}
|
|
}
|
|
|
|
// Build context
|
|
let context = HandlerContext {
|
|
capabilities: self.capabilities,
|
|
store: &self.store,
|
|
trusted_verifiers: &self.trusted_verifiers,
|
|
sender_public_key,
|
|
};
|
|
|
|
// Dispatch to handler
|
|
let action = handler.handle(&message, &context)?;
|
|
|
|
// Process action
|
|
match &action {
|
|
ServiceAction::Store | ServiceAction::StoreAndForward => {
|
|
if let Some(pk) = sender_public_key {
|
|
self.store.store(message, pk);
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
|
|
Ok(action)
|
|
}
|
|
|
|
/// Query the store for matching announces.
|
|
pub fn query(&self, query: &ServiceMessage) -> Vec<&StoredMessage> {
|
|
let Some(handler) = self.handlers.get(&query.service_id) else {
|
|
return Vec::new();
|
|
};
|
|
|
|
self.store.query(query.service_id, |stored| {
|
|
stored.message.message_type == MessageType::Announce
|
|
&& handler.matches_query(stored, query)
|
|
})
|
|
}
|
|
|
|
/// Get handler name for a service.
|
|
pub fn service_name(&self, service_id: u32) -> Option<&str> {
|
|
self.handlers.get(&service_id).map(|h| h.name())
|
|
}
|
|
|
|
/// List registered services.
|
|
pub fn services(&self) -> Vec<(u32, &str)> {
|
|
self.handlers
|
|
.iter()
|
|
.map(|(&id, h)| (id, h.name()))
|
|
.collect()
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::{identity::ServiceIdentity, service_ids::FAPP};
|
|
|
|
struct TestHandler;
|
|
|
|
impl ServiceHandler for TestHandler {
|
|
fn service_id(&self) -> u32 {
|
|
FAPP
|
|
}
|
|
|
|
fn name(&self) -> &str {
|
|
"Test"
|
|
}
|
|
|
|
fn handle(
|
|
&self,
|
|
message: &ServiceMessage,
|
|
_context: &HandlerContext,
|
|
) -> Result<ServiceAction, ServiceError> {
|
|
match message.message_type {
|
|
MessageType::Announce => Ok(ServiceAction::StoreAndForward),
|
|
MessageType::Query => Ok(ServiceAction::Handled),
|
|
_ => Ok(ServiceAction::Drop),
|
|
}
|
|
}
|
|
|
|
fn matches_query(&self, _announce: &StoredMessage, _query: &ServiceMessage) -> bool {
|
|
true // Match all for test
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn register_and_handle() {
|
|
let mut router = ServiceRouter::new(crate::capabilities::RELAY);
|
|
router.register(Box::new(TestHandler));
|
|
|
|
assert!(router.has_service(FAPP));
|
|
assert_eq!(router.service_name(FAPP), Some("Test"));
|
|
|
|
let id = ServiceIdentity::generate();
|
|
let msg = ServiceMessage::announce(&id, FAPP, vec![], 1);
|
|
|
|
let action = router.handle(msg.clone(), Some(id.public_key())).unwrap();
|
|
assert!(matches!(action, ServiceAction::StoreAndForward));
|
|
|
|
// Message should be stored
|
|
assert_eq!(router.store().len(), 1);
|
|
}
|
|
|
|
#[test]
|
|
fn unknown_service_rejected() {
|
|
let mut router = ServiceRouter::new(0);
|
|
let id = ServiceIdentity::generate();
|
|
let msg = ServiceMessage::announce(&id, 9999, vec![], 1);
|
|
|
|
let result = router.handle(msg, Some(id.public_key()));
|
|
assert!(matches!(result, Err(ServiceError::UnknownService(9999))));
|
|
}
|
|
|
|
#[test]
|
|
fn invalid_signature_rejected() {
|
|
let mut router = ServiceRouter::new(0);
|
|
router.register(Box::new(TestHandler));
|
|
|
|
let id1 = ServiceIdentity::generate();
|
|
let id2 = ServiceIdentity::generate();
|
|
let msg = ServiceMessage::announce(&id1, FAPP, vec![], 1);
|
|
|
|
// Pass wrong public key
|
|
let result = router.handle(msg, Some(id2.public_key()));
|
|
assert!(matches!(result, Err(ServiceError::SignatureInvalid)));
|
|
}
|
|
}
|