use std::net::SocketAddr;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
#[non_exhaustive]
#[derive(Debug, thiserror::Error)]
pub enum MeshError {
#[error("transport error: {0}")]
Transport(String),
#[error("handshake failed: {0}")]
Handshake(String),
#[error("peer not connected: {0}")]
PeerNotConnected(String),
#[error("message too large: {size} bytes (max {max})")]
MessageTooLarge {
size: usize,
max: usize,
},
#[error("connection closed")]
ConnectionClosed,
#[error("timeout: {0}")]
Timeout(String),
#[error("encryption error: {0}")]
Encryption(String),
#[error("genesis mismatch: local vs remote cluster")]
GenesisMismatch,
#[error("io error: {0}")]
Io(String),
}
pub const MAX_MESSAGE_SIZE: usize = 16 * 1024 * 1024;
#[async_trait]
pub trait MeshStream: Send + Sync + 'static {
async fn send(&mut self, data: &[u8]) -> Result<(), MeshError>;
async fn recv(&mut self) -> Result<Vec<u8>, MeshError>;
async fn close(&mut self) -> Result<(), MeshError>;
fn remote_addr(&self) -> Option<SocketAddr>;
}
#[async_trait]
pub trait TransportListener: Send + Sync + 'static {
async fn accept(&mut self) -> Result<(Box<dyn MeshStream>, SocketAddr), MeshError>;
fn local_addr(&self) -> Result<SocketAddr, MeshError>;
}
#[async_trait]
pub trait MeshTransport: Send + Sync + 'static {
fn name(&self) -> &str;
async fn listen(&self, addr: &str) -> Result<Box<dyn TransportListener>, MeshError>;
async fn connect(&self, addr: &str) -> Result<Box<dyn MeshStream>, MeshError>;
fn supports(&self, addr: &str) -> bool;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WeftHandshake {
pub node_id: String,
pub governance_genesis_hash: [u8; 32],
pub governance_version: String,
pub capabilities: u32,
pub kem_supported: bool,
pub chain_seq: u64,
pub supported_sync_streams: Vec<u8>,
}
#[derive(Debug, Clone)]
pub struct MeshPeer {
pub node_id: String,
pub handshake: WeftHandshake,
pub address: SocketAddr,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn mesh_error_display_transport() {
let err = MeshError::Transport("reset".into());
assert_eq!(err.to_string(), "transport error: reset");
}
#[test]
fn mesh_error_display_handshake() {
let err = MeshError::Handshake("bad key".into());
assert_eq!(err.to_string(), "handshake failed: bad key");
}
#[test]
fn mesh_error_display_peer_not_connected() {
let err = MeshError::PeerNotConnected("node-42".into());
assert_eq!(err.to_string(), "peer not connected: node-42");
}
#[test]
fn mesh_error_display_message_too_large() {
let err = MeshError::MessageTooLarge {
size: 20_000_000,
max: MAX_MESSAGE_SIZE,
};
let s = err.to_string();
assert!(s.contains("20000000"));
assert!(s.contains(&MAX_MESSAGE_SIZE.to_string()));
}
#[test]
fn mesh_error_display_connection_closed() {
let err = MeshError::ConnectionClosed;
assert_eq!(err.to_string(), "connection closed");
}
#[test]
fn mesh_error_display_timeout() {
let err = MeshError::Timeout("connect".into());
assert_eq!(err.to_string(), "timeout: connect");
}
#[test]
fn mesh_error_display_encryption() {
let err = MeshError::Encryption("decrypt failed".into());
assert_eq!(err.to_string(), "encryption error: decrypt failed");
}
#[test]
fn mesh_error_display_genesis_mismatch() {
let err = MeshError::GenesisMismatch;
assert!(err.to_string().contains("genesis mismatch"));
}
#[test]
fn mesh_error_display_io() {
let err = MeshError::Io("broken pipe".into());
assert_eq!(err.to_string(), "io error: broken pipe");
}
#[test]
fn max_message_size_is_16mib() {
assert_eq!(MAX_MESSAGE_SIZE, 16 * 1024 * 1024);
}
#[test]
fn weft_handshake_serde_roundtrip() {
let hs = WeftHandshake {
node_id: "abcdef1234567890".into(),
governance_genesis_hash: [0xAA; 32],
governance_version: "1.0.0".into(),
capabilities: 0b1011,
kem_supported: true,
chain_seq: 42,
supported_sync_streams: vec![0x01, 0x03, 0x07],
};
let json = serde_json::to_string(&hs).unwrap();
let restored: WeftHandshake = serde_json::from_str(&json).unwrap();
assert_eq!(restored.node_id, hs.node_id);
assert_eq!(restored.governance_genesis_hash, [0xAA; 32]);
assert_eq!(restored.governance_version, "1.0.0");
assert_eq!(restored.capabilities, 0b1011);
assert!(restored.kem_supported);
assert_eq!(restored.chain_seq, 42);
assert_eq!(restored.supported_sync_streams, vec![0x01, 0x03, 0x07]);
}
#[test]
fn mesh_peer_fields() {
let hs = WeftHandshake {
node_id: "peer-1".into(),
governance_genesis_hash: [0; 32],
governance_version: "0.1.0".into(),
capabilities: 0,
kem_supported: false,
chain_seq: 0,
supported_sync_streams: vec![],
};
let addr: SocketAddr = "127.0.0.1:9470".parse().unwrap();
let peer = MeshPeer {
node_id: "peer-1".into(),
handshake: hs,
address: addr,
};
assert_eq!(peer.node_id, "peer-1");
assert_eq!(peer.address.port(), 9470);
}
}