diff --git a/src/market/directory.rs b/src/market/directory.rs index 95d41d66..51513fbe 100644 --- a/src/market/directory.rs +++ b/src/market/directory.rs @@ -3,25 +3,24 @@ //! Handles market-related logic where Makers post their offers. Also provides functions to synchronize //! maker addresses from directory servers, post maker addresses to directory servers, +use crate::{ + market::rpc::start_rpc_server_thread, + utill::{ + get_dns_dir, get_tor_addrs, monitor_log_for_completion, parse_field, parse_toml, + write_default_config, ConnectionType, + }, +}; use std::{ collections::HashSet, - fs::{self, OpenOptions}, + fs::{self, File, OpenOptions}, io::{self, BufRead, BufReader, Write}, net::{Ipv4Addr, TcpListener, TcpStream}, - path::Path, + path::{Path, PathBuf}, sync::{Arc, RwLock}, thread::{self, sleep}, time::Duration, }; -use crate::market::rpc::start_rpc_server_thread; -use std::path::PathBuf; - -use crate::utill::{ - get_dns_dir, get_tor_addrs, monitor_log_for_completion, parse_field, parse_toml, - write_default_config, ConnectionType, -}; - /// Represents errors that can occur during directory server operations. #[derive(Debug)] pub enum DirectoryServerError { @@ -37,6 +36,7 @@ pub struct DirectoryServer { pub connection_type: ConnectionType, pub data_dir: PathBuf, pub shutdown: RwLock, + pub addresses: Arc>>, } impl Default for DirectoryServer { @@ -48,6 +48,7 @@ impl Default for DirectoryServer { connection_type: ConnectionType::TOR, data_dir: get_dns_dir(), shutdown: RwLock::new(false), + addresses: Arc::new(RwLock::new(HashSet::new())), } } } @@ -63,13 +64,11 @@ impl DirectoryServer { /// /// Default data-dir for linux: `~/.coinswap/` /// Default config locations: `~/.coinswap/dns/config.toml`. - pub fn new( config_path: Option, connection_type: Option, ) -> io::Result { let default_config = Self::default(); - let default_config_path = get_dns_dir().join("config.toml"); let config_path = config_path.unwrap_or(default_config_path); @@ -95,6 +94,16 @@ impl DirectoryServer { let connection_type_value = connection_type.unwrap_or(ConnectionType::TOR); + let addresses = Arc::new(RwLock::new(HashSet::new())); + let address_file = data_dir.join("addresses.dat"); + if address_file.exists() { + let file = File::open(&address_file)?; + let reader = BufReader::new(file); + for address in reader.lines().map_while(Result::ok) { + addresses.write().unwrap().insert(address); + } + } + Ok(DirectoryServer { rpc_port: 4321, port: parse_field(directory_config_section.get("port"), default_config.port) @@ -111,6 +120,7 @@ impl DirectoryServer { connection_type_value, ) .unwrap_or(connection_type_value), + addresses, }) } @@ -196,14 +206,13 @@ pub fn start_directory_server(directory: Arc) { match listener.accept() { Ok((mut stream, addrs)) => { log::debug!("Incoming connection from : {}", addrs); - let address_arc = addresses.clone(); stream .set_read_timeout(Some(Duration::from_secs(20))) .unwrap(); stream .set_write_timeout(Some(Duration::from_secs(20))) .unwrap(); - handle_client(&mut stream, address_arc); + handle_client(&mut stream, &directory); } // If no connection received, check for shutdown or save addresses to disk @@ -242,7 +251,7 @@ pub fn start_directory_server(directory: Arc) { // The stream should have read and write timeout set. // TODO: Use serde encoded data instead of string. -fn handle_client(stream: &mut TcpStream, addresses: Arc>>) { +fn handle_client(stream: &mut TcpStream, directory: &Arc) { let reader_stream = stream.try_clone().unwrap(); let mut reader = BufReader::new(reader_stream); let mut request_line = String::new(); @@ -251,11 +260,21 @@ fn handle_client(stream: &mut TcpStream, addresses: Arc>> if request_line.starts_with("POST") { let addr: String = request_line.replace("POST ", "").trim().to_string(); - addresses.write().unwrap().insert(addr.clone()); + directory.addresses.write().unwrap().insert(addr.clone()); log::info!("Got new maker address: {}", addr); + + let address_file = directory.data_dir.join("addresses.dat"); + // Expensive i/o operations below; Implement drop trait for directory server objects + let mut file = OpenOptions::new() + .create(true) + .append(true) + .open(address_file) + .unwrap(); + writeln!(file, "{}", addr).unwrap(); } else if request_line.starts_with("GET") { log::info!("Taker pinged the directory server"); - let response = addresses + let response = directory + .addresses .read() .unwrap() .iter()