use tokio::io::{AsyncRead, AsyncWrite, AsyncReadExt, AsyncWriteExt};
use tracing::{info, debug, warn};
use zks_wire::{Swarm, SwarmCircuit};
use crate::{
config::ConnectionConfig,
error::{Result, SdkError},
stream::EncryptedStream,
};
pub trait ZksStream: AsyncRead + AsyncWrite + Send + Unpin + 'static {}
impl<T: AsyncRead + AsyncWrite + Send + Unpin + 'static> ZksStream for T {}
pub struct ZksConnection {
stream: EncryptedStream<Box<dyn ZksStream>>,
config: ConnectionConfig,
peer_addr: String,
hop_count: u8,
circuit: Option<SwarmCircuit>,
swarm: Option<Swarm>,
}
impl ZksConnection {
pub async fn connect(
url: String,
config: ConnectionConfig,
min_hops: u8,
max_hops: u8
) -> Result<Self> {
let parsed_url = url::Url::parse(&url)
.map_err(|e| SdkError::InvalidUrl(format!("Invalid URL: {}", e)))?;
let host = parsed_url.host_str()
.ok_or_else(|| SdkError::InvalidUrl("Missing host in URL".to_string()))?;
let port = parsed_url.port().unwrap_or(8443); let addr = format!("{}:{}", host, port);
info!("Connecting to ZKS peer at {} with {}-{} hops", addr, min_hops, max_hops);
let swarm = Swarm::new("zks-network".to_string());
let stream = tokio::net::TcpStream::connect(&addr).await
.map_err(|e| SdkError::ConnectionFailed(format!("TCP connection failed: {}", e)))?;
let peer_addr = stream.peer_addr()
.map_err(|e| SdkError::ConnectionFailed(format!("Failed to get peer address: {}", e)))?
.to_string();
debug!("TCP connection established to {}", peer_addr);
let boxed_stream: Box<dyn ZksStream> = Box::new(stream);
let encrypted_stream = EncryptedStream::handshake(
boxed_stream,
&config,
true, zks_proto::HandshakeRole::Initiator,
"zks-onion".to_string(), None, ).await?;
info!("🔐 ZKS connection established with {} ({} hops)", peer_addr, min_hops);
Ok(Self {
stream: encrypted_stream,
config,
peer_addr,
hop_count: min_hops,
circuit: None, swarm: Some(swarm),
})
}
pub async fn send(&mut self, data: &[u8]) -> Result<()> {
debug!("Sending {} bytes to {} ({} hops)", data.len(), self.peer_addr, self.hop_count);
tokio::time::timeout(self.config.timeout, self.stream.write_all(data))
.await
.map_err(|_| SdkError::Timeout)?
.map_err(|e| SdkError::NetworkError(e.to_string()))?;
debug!("Sent {} bytes successfully", data.len());
Ok(())
}
pub async fn recv(&mut self, buf: &mut [u8]) -> Result<usize> {
debug!("Receiving data from {} ({} hops)", self.peer_addr, self.hop_count);
let n = tokio::time::timeout(self.config.timeout, self.stream.read(buf))
.await
.map_err(|_| SdkError::Timeout)?
.map_err(|e| SdkError::NetworkError(e.to_string()))?;
if n == 0 {
return Err(SdkError::ConnectionFailed("Connection closed by peer".to_string()));
}
debug!("Received {} bytes", n);
Ok(n)
}
pub async fn send_message(&mut self, message: &[u8]) -> Result<()> {
if message.len() > self.config.max_message_size {
return Err(SdkError::InvalidUrl(format!(
"Message too large: {} bytes (max: {})",
message.len(),
self.config.max_message_size
)));
}
let len = (message.len() as u32).to_be_bytes();
self.send(&len).await?;
self.send(message).await?;
Ok(())
}
pub async fn recv_message(&mut self) -> Result<Vec<u8>> {
let mut len_buf = [0u8; 4];
self.recv(&mut len_buf).await?;
let len = u32::from_be_bytes(len_buf) as usize;
if len > self.config.max_message_size {
return Err(SdkError::InvalidUrl(format!(
"Message too large: {} bytes (max: {})",
len,
self.config.max_message_size
)));
}
let mut message = vec![0u8; len];
self.recv(&mut message).await?;
Ok(message)
}
pub fn peer_addr(&self) -> &str {
&self.peer_addr
}
pub fn config(&self) -> &ConnectionConfig {
&self.config
}
pub fn hop_count(&self) -> u8 {
self.hop_count
}
pub fn is_connected(&self) -> bool {
true
}
pub async fn build_circuit(&mut self, min_hops: u8, max_hops: u8) -> Result<()> {
if let Some(swarm) = &self.swarm {
info!("Building circuit with {}-{} hops", min_hops, max_hops);
let circuit = swarm.build_circuit(min_hops, max_hops).await
.map_err(|e| SdkError::ConnectionFailed(format!("Failed to build circuit: {}", e)))?;
self.hop_count = circuit.hop_count() as u8;
self.circuit = Some(circuit);
info!("Successfully built circuit with {} hops", self.hop_count);
Ok(())
} else {
Err(SdkError::ConnectionFailed("No swarm available for circuit building".to_string()))
}
}
pub async fn send_onion(&mut self, data: &[u8]) -> Result<()> {
if let Some(circuit) = &self.circuit {
debug!("Onion routing {} bytes through {} hops", data.len(), circuit.hop_count());
let encrypted = circuit.onion_encrypt(data)
.map_err(|e| SdkError::NetworkError(format!("Onion encryption failed: {}", e)))?;
self.send(&encrypted).await
} else {
debug!("No circuit available, sending directly");
self.send(data).await
}
}
pub async fn recv_onion(&mut self, buf: &mut [u8]) -> Result<usize> {
let has_circuit = self.circuit.is_some();
if has_circuit {
debug!("Receiving onion routed data");
let mut encrypted_buf = vec![0u8; buf.len() * 2]; let n = self.recv(&mut encrypted_buf).await?;
if n == 0 {
return Ok(0);
}
if let Some(circuit) = &self.circuit {
debug!("Decrypting through {} hops", circuit.hop_count());
let decrypted = circuit.onion_decrypt(&encrypted_buf[..n])
.map_err(|e| SdkError::NetworkError(format!("Onion decryption failed: {}", e)))?;
let copy_len = std::cmp::min(decrypted.len(), buf.len());
buf[..copy_len].copy_from_slice(&decrypted[..copy_len]);
Ok(copy_len)
} else {
debug!("Circuit removed during recv, treating as regular data");
let copy_len = std::cmp::min(n, buf.len());
buf[..copy_len].copy_from_slice(&encrypted_buf[..copy_len]);
Ok(copy_len)
}
} else {
debug!("No circuit available, receiving directly");
self.recv(buf).await
}
}
pub fn circuit(&self) -> Option<&SwarmCircuit> {
self.circuit.as_ref()
}
pub fn swarm(&self) -> Option<&Swarm> {
self.swarm.as_ref()
}
pub async fn close(mut self) -> Result<()> {
info!("Closing ZKS connection to {} ({} hops)", self.peer_addr, self.hop_count);
let close_msg = b"CLOSE";
if let Err(e) = self.send(close_msg).await {
warn!("Failed to send close notification: {}", e);
}
self.stream.shutdown().await
.map_err(|e| SdkError::NetworkError(e.to_string()))?;
info!("ZKS connection to {} closed", self.peer_addr);
Ok(())
}
}