From 96210e8c117e08b1e29c4d6d523cb6771526d4b7 Mon Sep 17 00:00:00 2001 From: mojoX911 Date: Thu, 26 Dec 2024 21:50:12 +0530 Subject: [PATCH 1/4] move Dns messages to message --- src/protocol/messages.rs | 20 ++++++++++++++++++++ src/utill.rs | 20 -------------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/protocol/messages.rs b/src/protocol/messages.rs index b3182431..3999804a 100644 --- a/src/protocol/messages.rs +++ b/src/protocol/messages.rs @@ -315,3 +315,23 @@ impl Display for MakerToTakerMessage { } } } + +#[derive(Serialize, Deserialize, Debug)] +pub struct DnsMetadata { + pub url: String, + pub proof: FidelityProof, +} + +// Structured requests and responses using serde. +#[derive(Serialize, Deserialize, Debug)] +pub enum DnsRequest { + Post { + metadata: DnsMetadata, + }, + Get, + #[cfg(feature = "integration-test")] + Dummy { + url: String, + vout: u32, // represents a specific vout value of the OutPoint(deadbeefcafebabefeedc0ffee123456789abcdeffedcba9876543210ffeeddcc:vout) + }, +} diff --git a/src/utill.rs b/src/utill.rs index 057f1020..49339859 100644 --- a/src/utill.rs +++ b/src/utill.rs @@ -13,7 +13,6 @@ use log4rs::{ config::{Appender, Logger, Root}, Config, }; -use serde::{Deserialize, Serialize}; use std::{ env, fmt, io::{BufReader, BufWriter, ErrorKind, Read}, @@ -507,25 +506,6 @@ pub fn parse_proxy_auth(s: &str) -> Result<(String, String), NetError> { Ok((user, passwd)) } -#[derive(Serialize, Deserialize, Debug)] -pub struct DnsMetadata { - pub url: String, - pub proof: FidelityProof, -} - -// Structured requests and responses using serde. -#[derive(Serialize, Deserialize, Debug)] -pub enum DnsRequest { - Post { - metadata: Box, - }, - Get, - #[cfg(feature = "integration-test")] - Dummy { - url: String, - }, -} - pub fn verify_fidelity_checks( proof: &FidelityProof, addr: &str, From cf65d5933cd7b5895941463acf01154eff88a127 Mon Sep 17 00:00:00 2001 From: mojoX911 Date: Thu, 26 Dec 2024 21:50:58 +0530 Subject: [PATCH 2/4] update precommit hook Use nightly for fmt. Add cargo hack. --- git_hooks/pre-commit | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/git_hooks/pre-commit b/git_hooks/pre-commit index 772fef15..063094e8 100755 --- a/git_hooks/pre-commit +++ b/git_hooks/pre-commit @@ -2,6 +2,7 @@ # Check Rust formatting and automatically correct it echo "Auto-correcting code style with rustfmt..." +rustup override set nightly cargo fmt # Check Clippy lints @@ -12,4 +13,12 @@ if ! cargo clippy --all-targets -- -D warnings; then exit 1 fi +# Check for feature combinations +echo "Checking feature combinations with cargo-hack..." +if ! cargo hack --feature-powerset check; then + echo "Feature combination issues detected" + echo "Please fix the issues before committing." + exit 1 +fi + exit 0 \ No newline at end of file From 53d46350911bc4a7d9e814247f0a6c9ad76424f0 Mon Sep 17 00:00:00 2001 From: mojoX911 Date: Thu, 26 Dec 2024 21:52:19 +0530 Subject: [PATCH 3/4] make dns index entries by Outpoints --- src/maker/server.rs | 8 +-- src/market/directory.rs | 123 +++++++++++++++++++++++------------- src/market/rpc/messages.rs | 3 +- src/market/rpc/server.rs | 12 ++-- src/protocol/messages.rs | 1 + src/taker/offers.rs | 4 +- tests/dns.rs | 50 ++++++++++----- tests/test_framework/mod.rs | 3 +- 8 files changed, 132 insertions(+), 72 deletions(-) diff --git a/src/maker/server.rs b/src/maker/server.rs index 3cf7af5c..e01b69d0 100644 --- a/src/maker/server.rs +++ b/src/maker/server.rs @@ -39,10 +39,8 @@ use crate::{ handlers::handle_message, rpc::start_rpc_server, }, - protocol::messages::TakerToMakerMessage, - utill::{ - read_message, send_message, ConnectionType, DnsMetadata, DnsRequest, HEART_BEAT_INTERVAL, - }, + protocol::messages::{DnsMetadata, DnsRequest, TakerToMakerMessage}, + utill::{read_message, send_message, ConnectionType}, wallet::WalletError, }; @@ -148,7 +146,7 @@ fn network_bootstrap(maker: Arc) -> Result<(String, OptionalJoinHandle), }; let request = DnsRequest::Post { - metadata: Box::new(dns_metadata), + metadata: dns_metadata, }; // Keep trying until send is successful. diff --git a/src/market/directory.rs b/src/market/directory.rs index 7d951fd5..ad88f3a3 100644 --- a/src/market/directory.rs +++ b/src/market/directory.rs @@ -3,13 +3,16 @@ //! 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 bitcoin::{transaction::ParseOutPointError, OutPoint}; use bitcoind::bitcoincore_rpc::{self, Client, RpcApi}; +use std::collections::hash_map::Entry; use crate::{ market::rpc::start_rpc_server_thread, + protocol::messages::DnsRequest, utill::{ get_dns_dir, parse_field, parse_toml, read_message, send_message, verify_fidelity_checks, - ConnectionType, DnsRequest, HEART_BEAT_INTERVAL, + ConnectionType, }, wallet::{RPCConfig, WalletError}, }; @@ -18,13 +21,13 @@ use crate::{ use crate::utill::{get_tor_addrs, monitor_log_for_completion}; use std::{ - cmp::Ordering, - collections::BTreeSet, + collections::HashMap, convert::TryFrom, fs::{self, File}, io::{BufRead, BufReader, Write}, net::{Ipv4Addr, TcpListener, TcpStream}, path::{Path, PathBuf}, + str::FromStr, sync::{ atomic::{AtomicBool, Ordering::Relaxed}, Arc, PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard, @@ -42,6 +45,7 @@ pub enum DirectoryServerError { Net(NetError), MutexPossion, Wallet(WalletError), + AddressFileCorrupted(String), } impl From for DirectoryServerError { @@ -86,18 +90,9 @@ impl<'a, T> From>> for DirectoryServerError } } -#[derive(Debug, Eq, PartialEq)] -pub struct AddressEntry(pub u64, pub String); - -impl PartialOrd for AddressEntry { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for AddressEntry { - fn cmp(&self, other: &Self) -> Ordering { - other.0.cmp(&self.0).then_with(|| self.1.cmp(&other.1)) +impl From for DirectoryServerError { + fn from(value: ParseOutPointError) -> Self { + Self::AddressFileCorrupted(value.to_string()) } } @@ -110,7 +105,7 @@ pub struct DirectoryServer { pub connection_type: ConnectionType, pub data_dir: PathBuf, pub shutdown: AtomicBool, - pub addresses: Arc>>, + pub addresses: Arc>>, } impl Default for DirectoryServer { @@ -131,7 +126,7 @@ impl Default for DirectoryServer { }, data_dir: get_dns_dir(), shutdown: AtomicBool::new(false), - addresses: Arc::new(RwLock::new(BTreeSet::new())), + addresses: Arc::new(RwLock::new(HashMap::new())), } } } @@ -178,7 +173,7 @@ impl DirectoryServer { *value = conn_type_string; // Update the file on disk - let mut file = File::create(config_path)?; + let mut config_file = File::create(config_path)?; let mut content = String::new(); for (i, (key, value)) in config_map.iter().enumerate() { @@ -189,23 +184,12 @@ impl DirectoryServer { } } - file.write_all(content.as_bytes())?; + config_file.write_all(content.as_bytes())?; } - let addresses = Arc::new(RwLock::new(BTreeSet::new())); + // Load all addresses from address.dat file let address_file = data_dir.join("addresses.dat"); - if let Ok(file) = File::open(&address_file) { - let reader = BufReader::new(file); - for address in reader.lines().map_while(Result::ok) { - if let Some((key, value)) = address.split_once(',') { - if let Ok(key) = key.trim().parse::() { - addresses - .write()? - .insert(AddressEntry(key, value.to_string())); - } - } - } - } + let addresses = Arc::new(RwLock::new(read_addresses_from_file(&address_file)?)); let default_dns = Self::default(); Ok(DirectoryServer { @@ -221,6 +205,29 @@ impl DirectoryServer { addresses, }) } + + /// Updates the in-memory address map. If entry already exists, updates the value. If new entry, inserts the value. + pub fn updated_address_map( + &self, + metadata: (String, OutPoint), + ) -> Result<(), DirectoryServerError> { + match self.addresses.write()?.entry(metadata.1) { + Entry::Occupied(mut value) => { + log::info!("Maker Address Got Updated | Existing Address {} | New Address {} | Fidelity Outpoint {}", value.get(), metadata.0, metadata.1); + *value.get_mut() = metadata.0.clone(); + Ok(()) + } + Entry::Vacant(value) => { + log::info!( + "New Maker Address Added {} | Fidelity Outpoint {}", + metadata.0, + metadata.1 + ); + value.insert(metadata.0.clone()); + Ok(()) + } + } + } } fn write_default_directory_config(config_path: &Path) -> Result<(), DirectoryServerError> { @@ -258,6 +265,7 @@ pub fn start_address_writer_thread( } } +/// Write in-memory address data to address file pub fn write_addresses_to_file( directory: &Arc, address_file: &Path, @@ -266,7 +274,7 @@ pub fn write_addresses_to_file( .addresses .read()? .iter() - .map(|AddressEntry(addr, amount)| format!("{},{}\n", addr, amount)) + .map(|(op, addr)| format!("{},{}\n", op, addr)) .collect::>() .join(""); @@ -275,6 +283,31 @@ pub fn write_addresses_to_file( file.flush()?; Ok(()) } + +/// Read address data from file and return the HashMap +pub fn read_addresses_from_file( + path: &Path, +) -> Result, DirectoryServerError> { + if !path.exists() { + return Ok(HashMap::new()); + } + let reader = BufReader::new(File::open(path)?); + + reader + .lines() + .map(|line| { + let line = line?; + let (outpoint, addr) = + line.split_once(',') + .ok_or(DirectoryServerError::AddressFileCorrupted( + "deliminator missing in address.dat file".to_string(), + ))?; + let op = OutPoint::from_str(outpoint)?; + Ok((op, addr.to_string())) + }) + .collect::, DirectoryServerError>>() +} + pub fn start_directory_server( directory: Arc, rpc_config: Option, @@ -408,11 +441,7 @@ fn handle_client( current_height, ) { Ok(_) => { - log::info!("Maker verified successfully."); - directory.addresses.write()?.insert(AddressEntry( - metadata.proof.bond.amount.to_sat(), - metadata.url.clone(), - )); + directory.updated_address_map((metadata.url, metadata.proof.bond.outpoint))?; } Err(e) => { log::error!( @@ -428,16 +457,24 @@ fn handle_client( let addresses = directory.addresses.read()?; let response = addresses .iter() - .fold(String::new(), |acc, AddressEntry(_, addr)| { - acc + addr + "\n" - }); + .fold(String::new(), |acc, (_, addr)| acc + addr + "\n"); log::debug!("Sending Addresses: {}", response); send_message(stream, &response)?; } #[cfg(feature = "integration-test")] - DnsRequest::Dummy { url } => { + // Used for IT, only checks the updated_address_map() function. + DnsRequest::Dummy { url, vout } => { log::info!("Got new maker address: {}", &url); - directory.addresses.write()?.insert(AddressEntry(0, url)); + + // Create a constant txid for tests + // Its okay to unwrap as this is test-only + let txid = bitcoin::Txid::from_str( + "c3a04e4bdf3c8684c5cf5c8b2f3c43009670bc194ac6c856b3ec9d3a7a6e2602", + ) + .unwrap(); + let fidelity_op = OutPoint::new(txid, vout); + + directory.updated_address_map((url, fidelity_op))?; } } Ok(()) diff --git a/src/market/rpc/messages.rs b/src/market/rpc/messages.rs index 8a81e1ef..a3552acb 100644 --- a/src/market/rpc/messages.rs +++ b/src/market/rpc/messages.rs @@ -1,3 +1,4 @@ +use bitcoin::OutPoint; use serde::{Deserialize, Serialize}; use std::collections::BTreeSet; @@ -8,5 +9,5 @@ pub enum RpcMsgReq { #[derive(Serialize, Deserialize, Debug)] pub enum RpcMsgResp { - ListAddressesResp(BTreeSet), + ListAddressesResp(BTreeSet<(OutPoint, String)>), } diff --git a/src/market/rpc/server.rs b/src/market/rpc/server.rs index 4850a183..d66db0de 100644 --- a/src/market/rpc/server.rs +++ b/src/market/rpc/server.rs @@ -1,11 +1,13 @@ +use bitcoin::OutPoint; + use super::{RpcMsgReq, RpcMsgResp}; use crate::{ error::NetError, - market::directory::{AddressEntry, DirectoryServer, DirectoryServerError}, - utill::{read_message, send_message, HEART_BEAT_INTERVAL}, + market::directory::{DirectoryServer, DirectoryServerError}, + utill::{read_message, send_message}, }; use std::{ - collections::BTreeSet, + collections::{BTreeSet, HashMap}, io::ErrorKind, net::{TcpListener, TcpStream}, sync::{atomic::Ordering::Relaxed, Arc, RwLock}, @@ -14,7 +16,7 @@ use std::{ }; fn handle_request( socket: &mut TcpStream, - address: Arc>>, + address: Arc>>, ) -> Result<(), DirectoryServerError> { let req_bytes = read_message(socket)?; let rpc_request: RpcMsgReq = serde_cbor::from_slice(&req_bytes).map_err(NetError::Cbor)?; @@ -26,7 +28,7 @@ fn handle_request( address .read()? .iter() - .map(|AddressEntry(_, address)| address.clone()) + .map(|(op, address)| (*op, address.clone())) .collect::>(), ); if let Err(e) = send_message(socket, &resp) { diff --git a/src/protocol/messages.rs b/src/protocol/messages.rs index 3999804a..295158b5 100644 --- a/src/protocol/messages.rs +++ b/src/protocol/messages.rs @@ -324,6 +324,7 @@ pub struct DnsMetadata { // Structured requests and responses using serde. #[derive(Serialize, Deserialize, Debug)] +#[allow(clippy::large_enum_variant)] pub enum DnsRequest { Post { metadata: DnsMetadata, diff --git a/src/taker/offers.rs b/src/taker/offers.rs index beaa6b77..15fd784a 100644 --- a/src/taker/offers.rs +++ b/src/taker/offers.rs @@ -21,8 +21,8 @@ use socks::Socks5Stream; use crate::{ error::NetError, - protocol::messages::Offer, - utill::{read_message, send_message, ConnectionType, DnsRequest, GLOBAL_PAUSE, NET_TIMEOUT}, + protocol::messages::{DnsRequest, Offer}, + utill::{read_message, send_message, ConnectionType, GLOBAL_PAUSE, NET_TIMEOUT}, }; use super::{config::TakerConfig, error::TakerError, routines::download_maker_offer}; diff --git a/tests/dns.rs b/tests/dns.rs index fc76fa29..f5666091 100644 --- a/tests/dns.rs +++ b/tests/dns.rs @@ -3,14 +3,15 @@ use std::{io::Write, net::TcpStream, process::Command, thread, time::Duration}; mod test_framework; -use coinswap::utill::DnsRequest; +use coinswap::{protocol::messages::DnsRequest, utill::ConnectionType}; use test_framework::{init_bitcoind, start_dns}; -fn send_addresses(addresses: &[&str]) { +fn send_addresses(addresses: &[(&str, u32)]) { for address in addresses { let mut stream = TcpStream::connect(("127.0.0.1", 8080)).unwrap(); let request = DnsRequest::Dummy { - url: address.to_string(), + url: address.0.to_string(), + vout: address.1, }; let buffer = serde_cbor::ser::to_vec(&request).unwrap(); let length = buffer.len() as u32; @@ -20,23 +21,35 @@ fn send_addresses(addresses: &[&str]) { } } -fn verify_addresses(addresses: &[&str]) { +fn verify_addresses(addresses: &[(&str, u32)]) { let output = Command::new("./target/debug/directory-cli") .arg("list-addresses") .output() .unwrap(); let addresses_output = String::from_utf8(output.stdout).unwrap(); + println!("{}", addresses_output); + assert!( output.stderr.is_empty(), "Error: {:?}", String::from_utf8(output.stderr).unwrap() ); - for address in addresses { - assert!( - addresses_output.contains(&address.to_string()), - "Address {} not found", + // TODO add more through script checking + for (address, index) in addresses { + assert_eq!( + addresses_output.match_indices(&address.to_string()).count(), + 1, + "Address {} not found or duplicate entries found", + address + ); + assert_eq!( + addresses_output + .match_indices(&format!("vout: {}", index.to_string())) + .count(), + 1, + "OP index {} not found", address ); } @@ -58,7 +71,13 @@ fn test_dns() { let mut process = start_dns(&data_dir, &bitcoind); - let initial_addresses = vec!["127.0.0.1:8080", "127.0.0.1:8081", "127.0.0.1:8082"]; + // The indexes denotes vout of an `OutPoint(deadbeefcafebabefeedc0ffee123456789abcdeffedcba9876543210ffeeddcc:vout)`` + // So using the same index for different address, will replace the address. + let initial_addresses = vec![ + ("127.0.0.1:8080", 0), + ("127.0.0.1:8081", 1), + ("127.0.0.1:8082", 2), + ]; send_addresses(&initial_addresses); thread::sleep(Duration::from_secs(10)); verify_addresses(&initial_addresses); @@ -69,7 +88,9 @@ fn test_dns() { let mut process = start_dns(&data_dir, &bitcoind); - let additional_addresses = vec!["127.0.0.1:8083", "127.0.0.1:8084"]; + // Replace address 8082 to 8083 registered for Bond index 2. + // Add a new entry with a new bond index + let additional_addresses = vec![("127.0.0.1:8083", 2), ("127.0.0.1:8084", 3)]; send_addresses(&additional_addresses); thread::sleep(Duration::from_secs(10)); @@ -78,11 +99,10 @@ fn test_dns() { let mut process = start_dns(&data_dir, &bitcoind); let all_addresses = vec![ - "127.0.0.1:8080", - "127.0.0.1:8081", - "127.0.0.1:8082", - "127.0.0.1:8083", - "127.0.0.1:8084", + ("127.0.0.1:8080", 0), + ("127.0.0.1:8081", 1), + ("127.0.0.1:8083", 2), + ("127.0.0.1:8084", 3), ]; verify_addresses(&all_addresses); diff --git a/tests/test_framework/mod.rs b/tests/test_framework/mod.rs index 0041c819..b4413389 100644 --- a/tests/test_framework/mod.rs +++ b/tests/test_framework/mod.rs @@ -150,6 +150,7 @@ pub fn start_dns(data_dir: &std::path::Path, bitcoind: &BitcoinD) -> process::Ch thread::spawn(move || { let reader = BufReader::new(stderr); if let Some(line) = reader.lines().map_while(Result::ok).next() { + println!("{}", line); let _ = stderr_sender.send(line); } }); @@ -159,7 +160,7 @@ pub fn start_dns(data_dir: &std::path::Path, bitcoind: &BitcoinD) -> process::Ch let reader = BufReader::new(stdout); for line in reader.lines().map_while(Result::ok) { - log::info!("{line}"); + println!("{}", line); if stdout_sender.send(line).is_err() { break; } From 0c42b563ac44cdc2d3396f2f36911c4d5269462a Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Thu, 2 Jan 2025 18:55:09 +0530 Subject: [PATCH 4/4] git rebase fix --- src/maker/server.rs | 2 +- src/market/directory.rs | 2 +- src/market/rpc/server.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/maker/server.rs b/src/maker/server.rs index e01b69d0..eec3a947 100644 --- a/src/maker/server.rs +++ b/src/maker/server.rs @@ -40,7 +40,7 @@ use crate::{ rpc::start_rpc_server, }, protocol::messages::{DnsMetadata, DnsRequest, TakerToMakerMessage}, - utill::{read_message, send_message, ConnectionType}, + utill::{read_message, send_message, ConnectionType, HEART_BEAT_INTERVAL}, wallet::WalletError, }; diff --git a/src/market/directory.rs b/src/market/directory.rs index ad88f3a3..e5b56464 100644 --- a/src/market/directory.rs +++ b/src/market/directory.rs @@ -12,7 +12,7 @@ use crate::{ protocol::messages::DnsRequest, utill::{ get_dns_dir, parse_field, parse_toml, read_message, send_message, verify_fidelity_checks, - ConnectionType, + ConnectionType, HEART_BEAT_INTERVAL, }, wallet::{RPCConfig, WalletError}, }; diff --git a/src/market/rpc/server.rs b/src/market/rpc/server.rs index d66db0de..1bc3e11d 100644 --- a/src/market/rpc/server.rs +++ b/src/market/rpc/server.rs @@ -4,7 +4,7 @@ use super::{RpcMsgReq, RpcMsgResp}; use crate::{ error::NetError, market::directory::{DirectoryServer, DirectoryServerError}, - utill::{read_message, send_message}, + utill::{read_message, send_message, HEART_BEAT_INTERVAL}, }; use std::{ collections::{BTreeSet, HashMap},