1use std::net::SocketAddr;
7
8use async_trait::async_trait;
9use serde::{Deserialize, Serialize};
10
11#[non_exhaustive]
13#[derive(Debug, thiserror::Error)]
14pub enum MeshError {
15 #[error("transport error: {0}")]
17 Transport(String),
18
19 #[error("handshake failed: {0}")]
21 Handshake(String),
22
23 #[error("peer not connected: {0}")]
25 PeerNotConnected(String),
26
27 #[error("message too large: {size} bytes (max {max})")]
29 MessageTooLarge {
30 size: usize,
32 max: usize,
34 },
35
36 #[error("connection closed")]
38 ConnectionClosed,
39
40 #[error("timeout: {0}")]
42 Timeout(String),
43
44 #[error("encryption error: {0}")]
46 Encryption(String),
47
48 #[error("genesis mismatch: local vs remote cluster")]
50 GenesisMismatch,
51
52 #[error("io error: {0}")]
54 Io(String),
55}
56
57pub const MAX_MESSAGE_SIZE: usize = 16 * 1024 * 1024;
59
60#[async_trait]
74pub trait MeshStream: Send + Sync + 'static {
75 async fn send(&mut self, data: &[u8]) -> Result<(), MeshError>;
77
78 async fn recv(&mut self) -> Result<Vec<u8>, MeshError>;
82
83 async fn close(&mut self) -> Result<(), MeshError>;
85
86 fn remote_addr(&self) -> Option<SocketAddr>;
88}
89
90#[async_trait]
92pub trait TransportListener: Send + Sync + 'static {
93 async fn accept(&mut self) -> Result<(Box<dyn MeshStream>, SocketAddr), MeshError>;
95
96 fn local_addr(&self) -> Result<SocketAddr, MeshError>;
98}
99
100#[async_trait]
105pub trait MeshTransport: Send + Sync + 'static {
106 fn name(&self) -> &str;
108
109 async fn listen(&self, addr: &str) -> Result<Box<dyn TransportListener>, MeshError>;
111
112 async fn connect(&self, addr: &str) -> Result<Box<dyn MeshStream>, MeshError>;
114
115 fn supports(&self, addr: &str) -> bool;
117}
118
119#[derive(Debug, Clone, Serialize, Deserialize)]
124pub struct WeftHandshake {
125 pub node_id: String,
127
128 pub governance_genesis_hash: [u8; 32],
130
131 pub governance_version: String,
133
134 pub capabilities: u32,
136
137 pub kem_supported: bool,
139
140 pub chain_seq: u64,
142
143 pub supported_sync_streams: Vec<u8>,
145}
146
147#[derive(Debug, Clone)]
149pub struct MeshPeer {
150 pub node_id: String,
152
153 pub handshake: WeftHandshake,
155
156 pub address: SocketAddr,
158}
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163
164 #[test]
165 fn mesh_error_display_transport() {
166 let err = MeshError::Transport("reset".into());
167 assert_eq!(err.to_string(), "transport error: reset");
168 }
169
170 #[test]
171 fn mesh_error_display_handshake() {
172 let err = MeshError::Handshake("bad key".into());
173 assert_eq!(err.to_string(), "handshake failed: bad key");
174 }
175
176 #[test]
177 fn mesh_error_display_peer_not_connected() {
178 let err = MeshError::PeerNotConnected("node-42".into());
179 assert_eq!(err.to_string(), "peer not connected: node-42");
180 }
181
182 #[test]
183 fn mesh_error_display_message_too_large() {
184 let err = MeshError::MessageTooLarge {
185 size: 20_000_000,
186 max: MAX_MESSAGE_SIZE,
187 };
188 let s = err.to_string();
189 assert!(s.contains("20000000"));
190 assert!(s.contains(&MAX_MESSAGE_SIZE.to_string()));
191 }
192
193 #[test]
194 fn mesh_error_display_connection_closed() {
195 let err = MeshError::ConnectionClosed;
196 assert_eq!(err.to_string(), "connection closed");
197 }
198
199 #[test]
200 fn mesh_error_display_timeout() {
201 let err = MeshError::Timeout("connect".into());
202 assert_eq!(err.to_string(), "timeout: connect");
203 }
204
205 #[test]
206 fn mesh_error_display_encryption() {
207 let err = MeshError::Encryption("decrypt failed".into());
208 assert_eq!(err.to_string(), "encryption error: decrypt failed");
209 }
210
211 #[test]
212 fn mesh_error_display_genesis_mismatch() {
213 let err = MeshError::GenesisMismatch;
214 assert!(err.to_string().contains("genesis mismatch"));
215 }
216
217 #[test]
218 fn mesh_error_display_io() {
219 let err = MeshError::Io("broken pipe".into());
220 assert_eq!(err.to_string(), "io error: broken pipe");
221 }
222
223 #[test]
224 fn max_message_size_is_16mib() {
225 assert_eq!(MAX_MESSAGE_SIZE, 16 * 1024 * 1024);
226 }
227
228 #[test]
229 fn weft_handshake_serde_roundtrip() {
230 let hs = WeftHandshake {
231 node_id: "abcdef1234567890".into(),
232 governance_genesis_hash: [0xAA; 32],
233 governance_version: "1.0.0".into(),
234 capabilities: 0b1011,
235 kem_supported: true,
236 chain_seq: 42,
237 supported_sync_streams: vec![0x01, 0x03, 0x07],
238 };
239 let json = serde_json::to_string(&hs).unwrap();
240 let restored: WeftHandshake = serde_json::from_str(&json).unwrap();
241 assert_eq!(restored.node_id, hs.node_id);
242 assert_eq!(restored.governance_genesis_hash, [0xAA; 32]);
243 assert_eq!(restored.governance_version, "1.0.0");
244 assert_eq!(restored.capabilities, 0b1011);
245 assert!(restored.kem_supported);
246 assert_eq!(restored.chain_seq, 42);
247 assert_eq!(restored.supported_sync_streams, vec![0x01, 0x03, 0x07]);
248 }
249
250 #[test]
251 fn mesh_peer_fields() {
252 let hs = WeftHandshake {
253 node_id: "peer-1".into(),
254 governance_genesis_hash: [0; 32],
255 governance_version: "0.1.0".into(),
256 capabilities: 0,
257 kem_supported: false,
258 chain_seq: 0,
259 supported_sync_streams: vec![],
260 };
261 let addr: SocketAddr = "127.0.0.1:9489".parse().unwrap();
262 let peer = MeshPeer {
263 node_id: "peer-1".into(),
264 handshake: hs,
265 address: addr,
266 };
267 assert_eq!(peer.node_id, "peer-1");
268 assert_eq!(peer.address.port(), 9489);
269 }
270}