From 4ce52a3c9f73acc4e86c9a9048e32f1e73c48102 Mon Sep 17 00:00:00 2001 From: KnowWhoami Date: Fri, 14 Feb 2025 09:48:11 +0530 Subject: [PATCH] WIP --- src/bin/maker-cli.rs | 8 - src/bin/taker.rs | 2 +- src/maker/api.rs | 80 +++++-- src/maker/rpc/messages.rs | 2 - src/maker/rpc/server.rs | 7 +- src/maker/server.rs | 445 +++++++++++++++++------------------- src/taker/api.rs | 3 +- src/utill.rs | 2 +- src/wallet/api.rs | 17 +- src/wallet/fidelity.rs | 119 +++++++--- src/wallet/storage.rs | 3 +- tests/abort1.rs | 4 +- tests/abort2_case1.rs | 5 +- tests/abort2_case2.rs | 3 +- tests/abort2_case3.rs | 3 +- tests/abort3_case1.rs | 3 +- tests/abort3_case2.rs | 3 +- tests/abort3_case3.rs | 3 +- tests/fidelity.rs | 4 +- tests/malice1.rs | 3 +- tests/malice2.rs | 3 +- tests/standard_swap.rs | 5 +- tests/test_framework/mod.rs | 16 +- 23 files changed, 392 insertions(+), 351 deletions(-) diff --git a/src/bin/maker-cli.rs b/src/bin/maker-cli.rs index 20d74dec..d8b93efe 100644 --- a/src/bin/maker-cli.rs +++ b/src/bin/maker-cli.rs @@ -64,11 +64,6 @@ enum Commands { ShowDataDir, /// Shutdown the makerd server Stop, - /// Redeems the fidelity bond if timelock is matured. Returns the txid of the spending transaction. - RedeemFidelity { - #[clap(long, short = 'i', default_value = "0")] - index: u32, - }, /// Show all the fidelity bonds, current and previous, with an (index, {bond_proof, is_spent}) tupple. ShowFidelity, /// Sync the maker wallet with current blockchain state. @@ -126,9 +121,6 @@ fn main() -> Result<(), MakerError> { Commands::Stop => { send_rpc_req(stream, RpcMsgReq::Stop)?; } - Commands::RedeemFidelity { index } => { - send_rpc_req(stream, RpcMsgReq::RedeemFidelity(index))?; - } Commands::ShowFidelity => { send_rpc_req(stream, RpcMsgReq::ListFidelity)?; } diff --git a/src/bin/taker.rs b/src/bin/taker.rs index d354d61a..33b69371 100644 --- a/src/bin/taker.rs +++ b/src/bin/taker.rs @@ -174,7 +174,7 @@ fn main() -> Result<(), TakerError> { } } Commands::GetBalances => { - let balances = taker.get_wallet().get_balances(None)?; + let balances = taker.get_wallet().get_balances()?; println!( "{}", to_string_pretty(&json!({ diff --git a/src/maker/api.rs b/src/maker/api.rs index ac6b9d54..c434cd03 100644 --- a/src/maker/api.rs +++ b/src/maker/api.rs @@ -31,7 +31,7 @@ use std::{ atomic::{AtomicBool, Ordering::Relaxed}, Arc, Mutex, RwLock, }, - thread::JoinHandle, + thread::{self, JoinHandle}, time::{Duration, Instant}, }; @@ -49,14 +49,12 @@ use crate::{ use super::{config::MakerConfig, error::MakerError}; /// Interval for health checks on a stable RPC connection with bitcoind. -pub const RPC_PING_INTERVAL: Duration = Duration::from_secs(10); - -// Currently we don't refresh address at DNS. The Maker only post it once at startup. -// If the address record gets deleted, or the DNS gets blasted, the Maker won't know. -// TODO: Make the maker repost their address to DNS once a day in spawned thread. -// pub const DIRECTORY_SERVERS_REFRESH_INTERVAL_SECS: u64 = Duartion::from_days(1); // Once a day. +pub const RPC_PING_INTERVAL: u32 = 9; /// Maker triggers the recovery mechanism, if Taker is idle for more than 15 mins during a swap. +#[cfg(feature = "integration-test")] +pub const IDLE_CONNECTION_TIMEOUT: Duration = Duration::from_secs(60); +#[cfg(not(feature = "integration-test"))] pub const IDLE_CONNECTION_TIMEOUT: Duration = Duration::from_secs(60 * 15); /// The minimum difference in locktime (in blocks) between the incoming and outgoing swaps. @@ -69,6 +67,21 @@ pub const IDLE_CONNECTION_TIMEOUT: Duration = Duration::from_secs(60 * 15); /// To enhance safety, the default value is set to 20 blocks. pub const MIN_CONTRACT_REACTION_TIME: u16 = 20; +/// Interval for redeeming expired bonds, creating new ones if needed, +/// and updating the DNS server with the latest bond proof and maker address. +/// TODO:Is 10 min interval too less? Can't we just increase it to 1 day? +#[cfg(feature = "integration-test")] +pub(crate) const FIDELITY_BOND_DNS_UPDATE_INTERVAL: u32 = 30; +#[cfg(not(feature = "integration-test"))] +pub(crate) const FIDELITY_BOND_DNS_UPDATE_INTERVAL: u32 = 600; // 1 Block Interval + +/// Interval to check if there is enough liquidity for swaps. +/// If the available balance is below the minimum, maker server won't listen for any swap requests until funds are added. +#[cfg(feature = "integration-test")] +pub(crate) const SWAP_LIQUIDITY_CHECK_INTERVAL: u32 = 5; +#[cfg(not(feature = "integration-test"))] +pub(crate) const SWAP_LIQUIDITY_CHECK_INTERVAL: u32 = 30; + /// # Fee Parameters for Coinswap /// /// These parameters define the fees charged by Makers in a coinswap transaction. @@ -111,8 +124,6 @@ pub const TIME_RELATIVE_FEE_PCT: f64 = 0.005; /// Minimum Coinswap amount; makers will not accept amounts below this. pub const MIN_SWAP_AMOUNT: u64 = 10_000; -// What's the use of RefundLocktimeStep? - /// Used to configure the maker for testing purposes. /// /// This enum defines various behaviors that can be assigned to the maker during testing @@ -238,6 +249,7 @@ pub struct Maker { #[allow(clippy::too_many_arguments)] impl Maker { + // TOD0: Update the doc comment here? /// Initializes a Maker structure. /// /// This function sets up a Maker instance with configurable parameters. @@ -285,6 +297,48 @@ impl Maker { wallet }; + // Check if all wallet's fidelity transaction are confirmed ,if not wait until it get's confirmed. + let sleep_increment = 10; + let mut sleep_duration = 0; + + let bond_conf_heights= wallet + .get_fidelity_bonds() + .iter() + .filter_map(|(i, (bond, _,_))|{ + if bond.conf_height.is_none() && bond.cert_expiry.is_none(){ + let txid= bond.outpoint.txid; + + let conf_height= loop{ + if let Some(ht) = wallet.rpc.get_transaction(&txid, None).map_err(WalletError::Rpc).unwrap().info.blockheight{ + log::info!("Fidelity Transaction {} confirmed at blockheight: {}",txid, ht); + break ht; + } + else{ + log::info!("Fidelity Transaction {} seen in mempool, waiting for confirmation.",txid); + sleep_duration= (sleep_duration + sleep_increment).min(60*10); // Capped at 1 Block interval i.e 10 mins + log::info!("Next sync in {:?} secs", sleep_duration); + thread::sleep(Duration::from_secs(sleep_duration)); + } + + }; + Some((*i, conf_height)) + } + else{ + None + } + }) + .collect::>(); + + // update the confirmation height & bond expiry after the bond confirmation. + let cert_expiry = wallet.get_fidelity_expiry()?; + let fidelity_bonds = wallet.get_fidelity_bonds_mut(); + bond_conf_heights.into_iter().for_each(|(i, ht)| { + let (bond, _, _) = fidelity_bonds.get_mut(&i).unwrap(); + + bond.conf_height = Some(ht); + bond.cert_expiry = Some(cert_expiry); + }); + // If config file doesn't exist, default config will be loaded. let mut config = MakerConfig::new(Some(&data_dir.join("config.toml")))?; @@ -648,12 +702,6 @@ pub(crate) fn restore_broadcasted_contracts_on_reboot(maker: Arc) -> Resu pub(crate) fn check_for_idle_states(maker: Arc) -> Result<(), MakerError> { let mut bad_ip = Vec::new(); - let conn_timeout = if cfg!(feature = "integration-test") { - Duration::from_secs(60) - } else { - IDLE_CONNECTION_TIMEOUT - }; - loop { if maker.shutdown.load(Relaxed) { break; @@ -670,7 +718,7 @@ pub(crate) fn check_for_idle_states(maker: Arc) -> Result<(), MakerError> let no_response_since = current_time.saturating_duration_since(*last_connected_time); - if no_response_since > conn_timeout { + if no_response_since > IDLE_CONNECTION_TIMEOUT { log::error!( "[{}] Potential Dropped Connection from taker. No response since : {} secs. Recovering from swap", maker.config.network_port, diff --git a/src/maker/rpc/messages.rs b/src/maker/rpc/messages.rs index 5b3d13e9..b46a3a60 100644 --- a/src/maker/rpc/messages.rs +++ b/src/maker/rpc/messages.rs @@ -43,8 +43,6 @@ pub enum RpcMsgReq { GetDataDir, /// Request to stop the Maker server. Stop, - /// Request to reddem a fidelity bond for a given index. - RedeemFidelity(u32), /// Request to list all active and past fidelity bonds. ListFidelity, /// Request to sync the internal wallet with blockchain. diff --git a/src/maker/rpc/server.rs b/src/maker/rpc/server.rs index d5bc9cc7..d35eb3c1 100644 --- a/src/maker/rpc/server.rs +++ b/src/maker/rpc/server.rs @@ -64,7 +64,7 @@ fn handle_request(maker: &Arc, socket: &mut TcpStream) -> Result<(), Make RpcMsgResp::SwapUtxoResp { utxos } } RpcMsgReq::Balances => { - let balances = maker.get_wallet().read()?.get_balances(None)?; + let balances = maker.get_wallet().read()?.get_balances()?; RpcMsgResp::TotalBalanceResp(balances) } RpcMsgReq::NewAddress => { @@ -116,11 +116,6 @@ fn handle_request(maker: &Arc, socket: &mut TcpStream) -> Result<(), Make maker.shutdown.store(true, Relaxed); RpcMsgResp::Shutdown } - - RpcMsgReq::RedeemFidelity(index) => { - let txid = maker.get_wallet().write()?.redeem_fidelity(index)?; - RpcMsgResp::FidelitySpend(txid) - } RpcMsgReq::ListFidelity => { let list = maker .get_wallet() diff --git a/src/maker/server.rs b/src/maker/server.rs index 02e2901e..62f2aeec 100644 --- a/src/maker/server.rs +++ b/src/maker/server.rs @@ -9,10 +9,7 @@ use std::{ net::{Ipv4Addr, TcpListener, TcpStream}, path::Path, process::Child, - sync::{ - atomic::{AtomicBool, Ordering::Relaxed}, - Arc, - }, + sync::{atomic::Ordering::Relaxed, Arc}, thread::{self, sleep}, time::Duration, }; @@ -31,11 +28,12 @@ use crate::{ api::{ check_for_broadcasted_contracts, check_for_idle_states, restore_broadcasted_contracts_on_reboot, ConnectionState, + FIDELITY_BOND_DNS_UPDATE_INTERVAL, SWAP_LIQUIDITY_CHECK_INTERVAL, }, handlers::handle_message, rpc::start_rpc_server, }, - protocol::messages::{DnsMetadata, DnsRequest, TakerToMakerMessage}, + protocol::messages::{DnsMetadata, DnsRequest, FidelityProof, TakerToMakerMessage}, utill::{get_tor_hostname, read_message, send_message, ConnectionType, HEART_BEAT_INTERVAL}, wallet::WalletError, }; @@ -45,16 +43,13 @@ use crate::utill::monitor_log_for_completion; use crate::maker::error::MakerError; -// Default values for Maker configurations -pub(crate) const DIRECTORY_SERVERS_REFRESH_INTERVAL_SECS: u64 = 60 * 15; // 15 minutes - /// Fetches the Maker and DNS address, and sends maker address to the DNS server. /// Depending upon ConnectionType and test/prod environment, different maker address and DNS addresses are returned. /// Return the Maker address and an optional tor thread handle. /// /// Tor thread is spawned only if ConnectionType=TOR and --feature=tor is enabled. /// Errors if ConncetionType=TOR but, the tor feature is not enabled. -fn network_bootstrap(maker: Arc) -> Result, MakerError> { +fn network_bootstrap(maker: Arc) -> Result<(Option, String, String), MakerError> { let maker_port = maker.config.network_port; let (maker_address, dns_address, tor_handle) = match maker.config.connection_type { ConnectionType::CLEARNET => { @@ -129,27 +124,33 @@ fn network_bootstrap(maker: Arc) -> Result, MakerError> { } }; - log::info!( - "[{}] Server is listening at {}", - maker.config.network_port, - maker_address - ); - - setup_fidelity_bond(&maker, &maker_address)?; - log::info!( - "Max offer size : {} sats", - maker.get_wallet().read()?.store.offer_maxsize - ); - - let proof = maker - .highest_fidelity_proof - .read()? - .as_ref() - .unwrap() - .clone(); + // setup fidelity bonds if doesn't exists and make post request to DNS. + manage_fidelity_bonds_and_update_dns(maker.as_ref(), &maker_address, &dns_address)?; + + Ok((tor_handle, maker_address, dns_address)) +} + +/// Manages the maker's fidelity bonds and ensures the DNS server is updated with the latest bond proof and maker address. +/// +/// It performs the following operations: +/// 1. Redeems all expired fidelity bonds in the maker's wallet, if any are found. +/// 2. Creates a new fidelity bond if no valid bonds remain after redemption. +/// 3. Sends a POST request to the DNS server containing the maker's address and the proof of the fidelity bond +/// with the highest value. +fn manage_fidelity_bonds_and_update_dns( + maker: &Maker, + maker_addr: &str, + dns_addr: &str, +) -> Result<(), MakerError> { + maker.wallet.write()?.redeem_expired_fidelity_bonds()?; + + let proof = setup_fidelity_bond(maker, maker_addr)?; + + // Check for swap liquidity + check_swap_liquidity(maker)?; let dns_metadata = DnsMetadata { - url: maker_address.clone(), + url: maker_addr.to_string(), proof, }; @@ -157,70 +158,59 @@ fn network_bootstrap(maker: Arc) -> Result, MakerError> { metadata: dns_metadata, }; - thread::spawn(move || { - let trigger_count = DIRECTORY_SERVERS_REFRESH_INTERVAL_SECS / HEART_BEAT_INTERVAL.as_secs(); - let mut i = 0; + let port = maker.config.network_port; - while !maker.shutdown.load(Relaxed) { - if i >= trigger_count || i == 0 { - let stream = match maker.config.connection_type { - ConnectionType::CLEARNET => TcpStream::connect(&dns_address), - #[cfg(feature = "tor")] - ConnectionType::TOR => Socks5Stream::connect( - format!("127.0.0.1:{}", maker.config.socks_port), - dns_address.as_str(), - ) - .map(|stream| stream.into_inner()), - }; + log::info!("[{}] Connecting to DNS: {}", port, dns_addr); - log::info!( - "[{}] Connecting to DNS: {}", - maker.config.network_port, - dns_address - ); - - let mut stream = match stream { - Ok(s) => s, - Err(e) => { - log::warn!( - "[{}] TCP connection error with directory, reattempting: {}", - maker_port, - e - ); - thread::sleep(HEART_BEAT_INTERVAL); - continue; - } - }; + while !maker.shutdown.load(Relaxed) { + let stream = match maker.config.connection_type { + ConnectionType::CLEARNET => TcpStream::connect(dns_addr), + #[cfg(feature = "tor")] + ConnectionType::TOR => { + Socks5Stream::connect(format!("127.0.0.1:{}", maker.config.socks_port), dns_addr) + .map(|s| s.into_inner()) + } + }; - if let Err(e) = send_message(&mut stream, &request) { - log::warn!( - "[{}] Failed to send our address to directory, reattempting: {}", - maker_port, - e + match stream { + Ok(mut stream) => match send_message(&mut stream, &request) { + Ok(_) => { + log::info!( + "[{}] Successfully sent our address to DNS at {}", + port, + dns_addr ); - thread::sleep(HEART_BEAT_INTERVAL); - continue; + break; } - - log::info!( - "[{}] Successfully sent our address to DNS at {}", - maker_port, - dns_address - ); - // Reset counter when success - i = 0; - } - i += 1; - thread::sleep(HEART_BEAT_INTERVAL); + Err(e) => log::warn!( + "[{}] Failed to send our address to DNS server, retrying: {}", + port, + e + ), + }, + + Err(e) => log::warn!( + "[{}] Failed to establish TCP connection with DNS server, retrying: {}", + port, + e + ), } - }); - Ok(tor_handle) + thread::sleep(HEART_BEAT_INTERVAL); + } + + Ok(()) } -/// Checks if the wallet already has fidelity bonds. if not, create the first fidelity bond. -fn setup_fidelity_bond(maker: &Arc, maker_address: &str) -> Result<(), MakerError> { +/// Checks if the wallet already has fidelity valid bonds. if not, create the first valid bond. +/// +/// ### NOTE ON VALID BOND: +/// +/// `Valid Fidelity Bond` means that the bond is not yet expired and redeemed or spent. +fn setup_fidelity_bond(maker: &Maker, maker_address: &str) -> Result { let highest_index = maker.get_wallet().read()?.get_highest_fidelity_index()?; + let mut proof = maker.highest_fidelity_proof.write()?; + if let Some(i) = highest_index { let wallet_read = maker.get_wallet().read()?; let (bond, _, _) = wallet_read.get_fidelity_bonds().get(&i).unwrap(); @@ -236,21 +226,16 @@ fn setup_fidelity_bond(maker: &Arc, maker_address: &str) -> Result<(), Ma .generate_fidelity_proof(i, maker_address)?; log::info!( - "Highest bond at outpoint {} | index {} | Amount {:?} sats | Remaining Timelock for expiry : {:?} Blocks | Current Bond Value : {:?} sats", + "Highest bond at outpoint {} | index {} | Amount {:?} sats | Remaining Timelock for expiry : {:?} Blocks | Current Bond Value : {:?} sats", highest_proof.bond.outpoint, i, bond.amount.to_sat(), bond.lock_time.to_consensus_u32() - current_height, wallet_read.calculate_bond_value(i)?.to_sat() ); - log::info!("Bond amount : {:?}", bond.amount.to_sat()); - // TODO: work remainig - // log::info!("") - let mut proof = maker.highest_fidelity_proof.write()?; *proof = Some(highest_proof); } else { - // xxxxx // No bond in the wallet. Lets attempt to create one. let amount = Amount::from_sat(maker.config.fidelity_amount); let current_height = maker @@ -274,10 +259,9 @@ fn setup_fidelity_bond(maker: &Arc, maker_address: &str) -> Result<(), Ma log::info!("Fidelity value chosen = {:?} sats", amount.to_sat()); log::info!("Fidelity Tx fee = 300 sats"); log::info!( - "Fidelity timelock {} blocks", - maker.config.fidelity_timelock + "Fidelity timelock {:?} blocks", + locktime.to_consensus_u32() - current_height ); - while !maker.shutdown.load(Relaxed) { sleep_multiplier += 1; // sync the wallet @@ -324,7 +308,7 @@ fn setup_fidelity_bond(maker: &Arc, maker_address: &str) -> Result<(), Ma .get_wallet() .read()? .generate_fidelity_proof(i, maker_address)?; - let mut proof = maker.highest_fidelity_proof.write()?; + *proof = Some(highest_proof); // sync and save the wallet data to disk @@ -334,48 +318,66 @@ fn setup_fidelity_bond(maker: &Arc, maker_address: &str) -> Result<(), Ma } } } + }; + + Ok(proof + .clone() + .expect("Fidelity Proof must exist after creating a bond")) +} + +/// Checks if the maker has enough liquidity for swaps. +/// If funds are below the minimum required, it repeatedly prompts the user to add more +/// until the liquidity is sufficient. +fn check_swap_liquidity(maker: &Maker) -> Result<(), MakerError> { + let sleep_incremental = 10; + let mut sleep_duration = 0; + let addr = maker.get_wallet().write()?.get_next_external_address()?; + while !maker.shutdown.load(Relaxed) { + maker.get_wallet().write()?.sync_no_fail(); + let offer_max_size = maker.get_wallet().read()?.store.offer_maxsize; + + let min_required = maker.config.min_swap_amount; + if offer_max_size < min_required { + log::warn!( + "Low Swap Liquidity | Min: {} sats | Available: {} sats. Add funds to {:?}", + min_required, + offer_max_size, + addr + ); + + sleep_duration = (sleep_duration + sleep_incremental).min(10 * 60); // Capped at 1 Block interval + log::info!("Next sync in {:?} secs", sleep_duration); + thread::sleep(Duration::from_secs(sleep_duration)); + } else { + log::info!( + "Swap Liquidity: {} sats | Min: {} sats | Listening for requests.", + offer_max_size, + min_required + ); + break; + } } + Ok(()) } -/// Keep checking if the Bitcoin Core RPC connection is live. Sets the global `accepting_client` flag as per RPC connection status. -/// -/// This will not block. Once Core RPC connection is live, accepting_client will set as `true` again. -fn check_connection_with_core( - maker: Arc, - accepting_clients: Arc, -) -> Result<(), MakerError> { - let mut rpc_ping_success = false; - let mut i = 0; +/// Continuously checks if the Bitcoin Core RPC connection is live. +fn check_connection_with_core(maker: &Maker) -> Result<(), MakerError> { while !maker.shutdown.load(Relaxed) { - // If connection is disrupted keep trying at heart_beat_interval (3 sec). - // If connection is live, keep tring at rpc_ping_interval (60 sec). - let trigger_count = match rpc_ping_success { - true => RPC_PING_INTERVAL.as_secs() / HEART_BEAT_INTERVAL.as_secs(), - false => 1, - }; - - if i >= trigger_count || i == 0 { - if let Err(e) = maker.wallet.read()?.rpc.get_blockchain_info() { - log::error!( - "[{}] RPC Connection failed. Reattempting {}", - maker.config.network_port, - e - ); - rpc_ping_success = false; - } else { - if !rpc_ping_success { - log::info!( - "[{}] Bitcoin Core RPC connection is back online.", - maker.config.network_port - ); - } - rpc_ping_success = true; - } - accepting_clients.store(rpc_ping_success, Relaxed); - i = 0; + if let Err(e) = maker.wallet.read()?.rpc.get_blockchain_info() { + log::error!( + "[{}] RPC Connection failed. Reattempting {}", + maker.config.network_port, + e + ); + } else { + log::info!( + "[{}] Bitcoin Core RPC connection is live.", + maker.config.network_port + ); + break; } - i += 1; + thread::sleep(HEART_BEAT_INTERVAL); } @@ -383,7 +385,7 @@ fn check_connection_with_core( } /// Handle a single client connection. -fn handle_client(maker: Arc, stream: &mut TcpStream) -> Result<(), MakerError> { +fn handle_client(maker: &Arc, stream: &mut TcpStream) -> Result<(), MakerError> { stream.set_nonblocking(false)?; // Block this thread until message is read. let mut connection_state = ConnectionState::default(); @@ -409,7 +411,7 @@ fn handle_client(maker: Arc, stream: &mut TcpStream) -> Result<(), MakerE let taker_msg: TakerToMakerMessage = serde_cbor::from_slice(&taker_msg_bytes)?; log::info!("[{}] <=== {}", maker.config.network_port, taker_msg); - let reply = handle_message(&maker, &mut connection_state, taker_msg); + let reply = handle_message(maker, &mut connection_state, taker_msg); match reply { Ok(reply) => { @@ -450,71 +452,58 @@ fn handle_client(maker: Arc, stream: &mut TcpStream) -> Result<(), MakerE Ok(()) } -/// Starts the main Maker Server process. +/// Starts the Maker server and manages its core operations. /// -/// This function initializes the Maker server by setting up network connections, -/// configuring the wallet with fidelity bond, and spawning necessary threads for: -/// - Checking Bitcoin Core connections. -/// - Monitoring idle client connections. -/// - Watching for broadcasted contract transactions. -/// - Running an RPC server for interacting with `maker-cli`. +/// This function initializes network connections, sets up the wallet with fidelity bonds, +/// and spawns essential threads for: +/// - Checking for idle client connections. +/// - Detecting and handling broadcasted contract transactions. +/// - Running an RPC server for communication with `maker-cli`. /// -/// It also handles incoming peer-to-peer (P2P) client connections in a loop, where -/// each connection spawns a dedicated handler thread. +/// The server continuously listens for incoming P2P client connections. +/// Periodic checks ensure liquidity availability, fidelity bond updates and backend connectivity +/// while avoiding disruptions during ongoing swaps. /// -/// The server continues to run until a shutdown signal is detected, at which point -/// it performs cleanup tasks, such as saving wallet data and terminating active Tor sessions. +/// The server runs until a shutdown signal is received, at which point it safely terminates +/// active processes, saves wallet data, and closes Tor sessions if enabled. pub fn start_maker_server(maker: Arc) -> Result<(), MakerError> { log::info!("Starting Maker Server"); - // Initialize network connections. - // Setup the wallet with fidelity bond. - let _tor_thread = network_bootstrap(maker.clone())?; + // Tracks the elapsed time in heartbeat intervals to schedule periodic checks and avoid redundant executions. + let mut interval_tracker = 0; + + let (_tor_thread, maker_addr, dns_addr) = network_bootstrap(maker.clone())?; + + // HEART_BEAT_INTERVAL secs are added to prevent redundant checks for fidelity bonds, DNS updates, + // and swap liquidity immediately after the Maker server starts. This ensures these functions + // are not executed twice in quick succession. + interval_tracker += HEART_BEAT_INTERVAL.as_secs() as u32; let port = maker.config.network_port; - let network = maker.get_wallet().read()?.store.network; - let offer_max_size = maker.get_wallet().read()?.store.offer_maxsize; - let utxos = maker.get_wallet().read()?.get_all_utxo()?; - let balances = maker.get_wallet().read()?.get_balances(Some(&utxos))?; - log::info!("[{}] Bitcoin Network: {}", port, network); - log::info!( - "[{}] Spendable Wallet Balance: {}", - port, - balances.spendable - ); - log::info!("[{}] Fidelity Bond Amount : {}", port, balances.fidelity); - log::info!( - "[{}] Minimum Swap Size {} SATS", - port, - maker.config.min_swap_amount - ); - log::info!("[{}] Maximum Swap Size {} SATS", port, offer_max_size); + + { + let wallet = maker.get_wallet().read()?; + log::info!("[{}] Bitcoin Network: {}", port, wallet.store.network); + log::info!( + "[{}] Spendable Wallet Balance: {}", + port, + wallet.get_balances()?.spendable + ); + + log::info!( + "[{}] Minimum Swap Size {} SATS | Maximum Swap Size {} SATS", + port, + maker.config.min_swap_amount, + wallet.store.offer_maxsize + ); + } let listener = TcpListener::bind((Ipv4Addr::LOCALHOST, maker.config.network_port)) .map_err(NetError::IO)?; listener.set_nonblocking(true)?; // Needed to not block a thread waiting for incoming connection. - // Global server Mutex, to switch on/off p2p network. - let accepting_clients = Arc::new(AtomicBool::new(false)); - if !maker.shutdown.load(Relaxed) { - // 1. Bitcoin Core Connection checker thread. - // Ensures that Bitcoin Core connection is live. - // If not, it will block p2p connections until Core works again. - let maker_clone = maker.clone(); - let acc_client_clone = accepting_clients.clone(); - let conn_check_thread = thread::Builder::new() - .name("Bitcoin Core Connection Checker Thread".to_string()) - .spawn(move || { - log::info!("[{}] Spawning Bitcoin Core connection checker thread", port); - if let Err(e) = check_connection_with_core(maker_clone.clone(), acc_client_clone) { - log::error!("[{}] Bitcoin Core connection check failed: {:?}", port, e); - maker_clone.shutdown.store(true, Relaxed); - } - })?; - maker.thread_pool.add_thread(conn_check_thread); - - // 2. Idle Client connection checker thread. + // 1. Idle Client connection checker thread. // This threads check idelness of peer in live swaps. // And takes recovery measure if the peer seems to have disappeared in middlle of a swap. let maker_clone = maker.clone(); @@ -532,7 +521,7 @@ pub fn start_maker_server(maker: Arc) -> Result<(), MakerError> { })?; maker.thread_pool.add_thread(idle_conn_check_thread); - // 3. Watchtower thread. + // 2. Watchtower thread. // This thread checks for broadcasted contract transactions, which usually means violation of the protocol. // When contract transaction detected in mempool it will attempt recovery. // This can get triggered even when contracts of adjacent hops are published. Implying the whole swap route is disrupted. @@ -548,7 +537,7 @@ pub fn start_maker_server(maker: Arc) -> Result<(), MakerError> { })?; maker.thread_pool.add_thread(contract_watcher_thread); - // 4: The RPC server thread. + // 3: The RPC server thread. // User for responding back to `maker-cli` apps. let maker_clone = maker.clone(); let rpc_thread = thread::Builder::new() @@ -567,77 +556,63 @@ pub fn start_maker_server(maker: Arc) -> Result<(), MakerError> { maker.thread_pool.add_thread(rpc_thread); sleep(HEART_BEAT_INTERVAL); // wait for 1 beat, to complete spawns of all the threads. + + // Check if recovery is needed. + let (inc, out) = maker.wallet.read()?.find_unfinished_swapcoins(); + if !inc.is_empty() || !out.is_empty() { + log::info!("Incomplete swaps detected in the wallet. Starting recovery"); + let maker_clone = maker.clone(); + restore_broadcasted_contracts_on_reboot(maker_clone.clone())?; + } + maker.is_setup_complete.store(true, Relaxed); log::info!("[{}] Server Setup completed!! Use maker-cli to operate the server and the internal wallet.", maker.config.network_port); } - // Check if recovery is needed. - let (inc, out) = maker.wallet.read()?.find_unfinished_swapcoins(); - if !inc.is_empty() || !out.is_empty() { - log::info!("Incomplete swaps detected in the wallet. Starting recovery"); - let maker_clone = maker.clone(); - restore_broadcasted_contracts_on_reboot(maker_clone.clone())?; - } - - let mut sync_counter = 0; - // The P2P Client connection loop. - // Each client connection will spawn a new handler thread, which is added back in the global thread_pool. - // This loop beats at `maker.config.heart_beat_interval_secs` while !maker.shutdown.load(Relaxed) { - // Check every 30 secs that we have enough swap liquidity. - // Raise warning otherwise and don't listen for swap requests. - if sync_counter >= 10 || sync_counter == 0 { - maker.get_wallet().write()?.sync_no_fail(); - let offer_max_size = maker.get_wallet().read()?.store.offer_maxsize; - if offer_max_size <= maker.config.min_swap_amount { - log::warn!("[WARN!] Swaps are disabled due to low balance, Please put more funds in the wallet | Min required {} sats | Available {} sats", maker.config.min_swap_amount, offer_max_size); - } else { - log::info!( - "Total available balance for swaps: {} sats | Listening for incoming swap requests", - offer_max_size - ); - } - sync_counter = 0; + // check for connection with bitcoin core backend after every RPC_PING_INTERVAL. + if interval_tracker % RPC_PING_INTERVAL == 0 { + check_connection_with_core(maker.as_ref())?; } - sync_counter += 1; - let maker = maker.clone(); // This clone is needed to avoid moving the Arc in each iterations. + // Check fidelity bonds and swap liquidity only when no coinswap is in progress. + // This prevents issues where an expired fidelity bond could block new bond creation, + // or insufficient liquidity could delay swaps while waiting for additional funds. + // If these checks were performed during an active coinswap, the maker might stop + // responding to requests, potentially aborting an ongoing swap. + if maker.ongoing_swap_state.lock()?.is_empty() { + if interval_tracker % FIDELITY_BOND_DNS_UPDATE_INTERVAL == 0 { + manage_fidelity_bonds_and_update_dns(maker.as_ref(), &maker_addr, &dns_addr)?; + interval_tracker = 0; + } - // Block client connections if accepting_client=false - if !accepting_clients.load(Relaxed) { - log::warn!( - "[{}] Temporary failure in Bitcoin Core RPC.", - maker.config.network_port - ); - sleep(HEART_BEAT_INTERVAL); - continue; + if interval_tracker % SWAP_LIQUIDITY_CHECK_INTERVAL == 0 { + check_swap_liquidity(maker.as_ref())?; + } } match listener.accept() { Ok((mut stream, _)) => { - log::info!( - "[{}] Received incoming connection", - maker.config.network_port - ); - - if let Err(e) = handle_client(maker, &mut stream) { + log::info!("[{}] Received incoming connection", port); + if let Err(e) = handle_client(&maker, &mut stream) { log::error!("[{}] Error Handling client request {:?}", port, e); } } - Err(e) => { - if e.kind() == ErrorKind::WouldBlock { - // Do nothing - } else { - log::error!( - "[{}] Error accepting incoming connection: {:?}", - maker.config.network_port, - e - ); + if e.kind() != ErrorKind::WouldBlock { + log::error!("[{}] Error accepting incoming connection: {:?}", port, e); } } - }; + } + // Increment `interval_tracker` only if no coinswap is in progress or if no pending + // swap liquidity and fidelity bond checks are due. This ensures these checks are + // not skipped due to an ongoing coinswap and are performed once it completes. + if maker.ongoing_swap_state.lock()?.is_empty() + || interval_tracker % SWAP_LIQUIDITY_CHECK_INTERVAL != 0 + { + interval_tracker += HEART_BEAT_INTERVAL.as_secs() as u32; + } sleep(HEART_BEAT_INTERVAL); } diff --git a/src/taker/api.rs b/src/taker/api.rs index 73610292..9d9fac06 100644 --- a/src/taker/api.rs +++ b/src/taker/api.rs @@ -362,7 +362,7 @@ impl Taker { /// If that fails too. Open an issue at [our github](https://github.com/citadel-tech/coinswap/issues) pub(crate) fn send_coinswap(&mut self, swap_params: SwapParams) -> Result<(), TakerError> { // Check if we have enough balance. - let available = self.wallet.get_balances(None)?.spendable; + let available = self.wallet.get_balances()?.spendable; // TODO: Make more exact estimate of swap cost and ensure balance. // For now ensure at least swap_amount + 1000 sats is available. @@ -2079,7 +2079,6 @@ impl Taker { let tor_dir = Path::new("/tmp/coinswap/dns/tor"); let hostname = get_tor_hostname(tor_dir)?; - log::info!("---------------hostname : {:?}", hostname); format!("{}:{}", hostname, 8080) } else { self.config.directory_server_address.clone() diff --git a/src/utill.rs b/src/utill.rs index c6ae406a..6d4e1f7c 100644 --- a/src/utill.rs +++ b/src/utill.rs @@ -625,7 +625,7 @@ pub(crate) fn verify_fidelity_checks( // Verify certificate hash let expected_cert_hash = proof.bond.generate_cert_hash(addr); - if proof.cert_hash != expected_cert_hash { + if proof.cert_hash != expected_cert_hash? { return Err(FidelityError::InvalidCertHash.into()); } diff --git a/src/wallet/api.rs b/src/wallet/api.rs index 5bbe24d0..eb26b25a 100644 --- a/src/wallet/api.rs +++ b/src/wallet/api.rs @@ -322,24 +322,23 @@ impl Wallet { /// Calculates the total balances of different categories in the wallet. /// Includes regular, swap, contract, fidelitly and spendable (regular + swap) utxos. /// Optionally takes in a list of UTXOs to reduce rpc call. If None is provided, the full list is fetched from core rpc. - pub fn get_balances( - &self, - all_utxos: Option<&Vec>, - ) -> Result { + pub fn get_balances(&self) -> Result { + // TODO: Can we remove `all_utxos` parameter below apis? as It seems redundant. + let regular = self - .list_descriptor_utxo_spend_info(all_utxos)? + .list_descriptor_utxo_spend_info(None)? .iter() .fold(Amount::ZERO, |sum, (utxo, _)| sum + utxo.amount); let contract = self - .list_live_timelock_contract_spend_info(all_utxos)? + .list_live_timelock_contract_spend_info(None)? .iter() .fold(Amount::ZERO, |sum, (utxo, _)| sum + utxo.amount); let swap = self - .list_incoming_swap_coin_utxo_spend_info(all_utxos)? + .list_incoming_swap_coin_utxo_spend_info(None)? .iter() .fold(Amount::ZERO, |sum, (utxo, _)| sum + utxo.amount); let fidelity = self - .list_fidelity_spend_info(all_utxos)? + .list_fidelity_spend_info(None)? .iter() .fold(Amount::ZERO, |sum, (utxo, _)| sum + utxo.amount); let spendable = regular + swap; @@ -863,7 +862,7 @@ impl Wallet { /// Refreshes the offer maximum size cache based on the current wallet's unspent transaction outputs (UTXOs). pub(crate) fn refresh_offer_maxsize_cache(&mut self) -> Result<(), WalletError> { - let balance = self.get_balances(None)?.spendable; + let balance = self.get_balances()?.spendable; self.store.offer_maxsize = balance.to_sat(); Ok(()) } diff --git a/src/wallet/fidelity.rs b/src/wallet/fidelity.rs index 18d55e06..cbfcb29e 100644 --- a/src/wallet/fidelity.rs +++ b/src/wallet/fidelity.rs @@ -126,12 +126,13 @@ pub struct FidelityBond { /// Fidelity Amount pub amount: Amount, /// Fidelity Locktime + /// TODO: Should we rename it `expires-at`? pub lock_time: LockTime, pub(crate) pubkey: PublicKey, // Height at which the bond was confirmed. - pub(crate) conf_height: u32, + pub(crate) conf_height: Option, // Cert expiry denoted in multiple of difficulty adjustment period (2016 blocks) - pub(crate) cert_expiry: u64, + pub(crate) cert_expiry: Option, } impl FidelityBond { @@ -146,17 +147,23 @@ impl FidelityBond { } /// Generate the bond's certificate hash. - pub(crate) fn generate_cert_hash(&self, addr: &str) -> sha256d::Hash { + pub(crate) fn generate_cert_hash(&self, addr: &str) -> Result { let cert_msg_str = format!( "fidelity-bond-cert|{}|{}|{}|{}|{}|{}", - self.outpoint, self.pubkey, self.cert_expiry, self.lock_time, self.amount, addr + self.outpoint, + self.pubkey, + self.cert_expiry.ok_or(FidelityError::BondDoesNotExist)?, // TODO: Should We panic or propagate the error here? + self.lock_time, + self.amount, + addr ); let cert_msg = cert_msg_str.as_bytes(); let mut btc_signed_msg = Vec::::new(); btc_signed_msg.extend("\x18Bitcoin Signed Message:\n".as_bytes()); btc_signed_msg.push(cert_msg.len() as u8); btc_signed_msg.extend(cert_msg); - sha256d::Hash::hash(&btc_signed_msg) + + Ok(sha256d::Hash::hash(&btc_signed_msg)) } } @@ -167,6 +174,12 @@ impl Wallet { &self.store.fidelity_bond } + /// Get a mutable reference to the fidelity bond store. + /// TODO: Should we increase the public visibility of `fidelity_bonds` field of `WalletStore` to pub(crate) -> this would help in preventing these api's otherwise? + pub fn get_fidelity_bonds_mut(&mut self) -> &mut HashMap { + &mut self.store.fidelity_bond + } + /// Get the highest value fidelity bond. Returns None, if no bond exists. pub fn get_highest_fidelity_index(&self) -> Result, WalletError> { Ok(self @@ -177,19 +190,11 @@ impl Wallet { if !is_spent { match self.calculate_bond_value(*i) { Ok(v) => { - log::info!("Fidelity Bond found | Index: {}, Value : {}", i, v); + log::info!("Fidelity Bond found | Index: {}, Bond Value : {}", i, v); Some((i, v)) } Err(e) => { log::error!("Fidelity valuation failed for index {}: {:?} ", i, e); - if matches!( - e, - WalletError::Fidelity(FidelityError::BondLocktimeExpired) - ) { - log::info!( - "Use `maker-cli redeem-fildeity ` to redeem the bond" - ); - } None } } @@ -271,7 +276,9 @@ impl Wallet { .expect("This can't error") .as_secs(); - let hash = self.rpc.get_block_hash(bond.conf_height as u64)?; + let hash = self + .rpc + .get_block_hash(bond.conf_height.ok_or(FidelityError::BondDoesNotExist)? as u64)?; let confirmation_time = self.rpc.get_block_header_info(&hash)?.time as u64; @@ -307,7 +314,8 @@ impl Wallet { pub fn create_fidelity( &mut self, amount: Amount, - locktime: LockTime, // The final locktime in blockheight or timestamp + locktime: LockTime, // The absolute locktime in blockheight or timestamp + // THINK: Why are we considering locktime to be UNIX timestamp based? ) -> Result { let (index, fidelity_addr, fidelity_pubkey) = self.get_next_fidelity_address(locktime)?; @@ -398,6 +406,26 @@ impl Wallet { let txid = self.send_tx(&tx)?; + // Register this bond even it is in mempool and not yet confirmed to avoid the edge case when the maker server + // unexpectedly shutdown while it was waiting for the fidelity transaction confirmation. + // Otherwise the wallet wouldn't know about this bond in this case and would attempt to create a new bond again. + { + let bond = FidelityBond { + outpoint: OutPoint::new(txid, 0), + amount, + lock_time: locktime, + pubkey: fidelity_pubkey, + // `Conf_height` & `cert_expiry` are considered None as they can't be known before the confirmation. + conf_height: None, + cert_expiry: None, + }; + let bond_spk = bond.script_pub_key(); + self.store + .fidelity_bond + .insert(index, (bond, bond_spk, false)); + self.save_to_disk()?; + } + let sleep_increment = 10; let mut sleep_multiplier = 0; @@ -417,7 +445,6 @@ impl Wallet { "Fidelity Transaction {} seen in mempool, waiting for confirmation.", txid ); - log::warn!("ATTENTION ! DO NOT SHUTDOWN THE MAKER UNTIL CONFIRMATION"); let total_sleep = sleep_increment * sleep_multiplier.min(10 * 60); // Caps at 1 Block interval i.e 10 mins log::info!("Next sync in {:?} secs", total_sleep); @@ -425,22 +452,16 @@ impl Wallet { } }; + // Update bond's confirmation height and certificate expiry. let cert_expiry = self.get_fidelity_expiry()?; - - let bond = FidelityBond { - outpoint: OutPoint::new(txid, 0), - amount, - lock_time: locktime, - pubkey: fidelity_pubkey, - conf_height, - cert_expiry, - }; - - let bond_spk = bond.script_pub_key(); - - self.store + let (bond, _, _) = self + .store .fidelity_bond - .insert(index, (bond, bond_spk, false)); + .get_mut(&index) + .ok_or(FidelityError::BondDoesNotExist)?; + + bond.cert_expiry = Some(cert_expiry); + bond.conf_height = Some(conf_height); self.sync()?; @@ -495,7 +516,11 @@ impl Wallet { let txid = self.send_tx(&tx)?; - log::info!("Fidelity redeem transaction broadcasted. txid: {}", txid); + log::info!( + "Redeem transaction for Fidelity bond broadcasted. Index: {}, TxID: {}", + index, + txid + ); // No need to wait for confirmation as that will delay the rpc call. Just send back the txid. @@ -513,6 +538,34 @@ impl Wallet { Ok(txid) } + /// Redeems all expired fidelity bonds in the wallet ,if found any. + pub fn redeem_expired_fidelity_bonds(&mut self) -> Result<(), WalletError> { + let curr_height = self.rpc.get_block_count()? as u32; + + let expired_bond_indices = self + .store + .fidelity_bond + .iter() + .filter_map(|(&i, (bond, _, is_spent))| { + if !is_spent && curr_height > bond.lock_time.to_consensus_u32() { + println!( + "curr_height : {:?} | expiry_height: {:?}", + curr_height, + bond.conf_height.unwrap() + bond.lock_time.to_consensus_u32() + ); + Some(i) + } else { + None + } + }) + .collect::>(); + + expired_bond_indices.into_iter().try_for_each(|i| { + log::info!("Fidelity Bond at index: {:?} expired | Redeeming it.", i); + self.redeem_fidelity(i).map(|_| ()) + }) + } + /// Generate a [FidelityProof] for bond at a given index and a specific onion address. pub(crate) fn generate_fidelity_proof( &self, @@ -532,7 +585,7 @@ impl Wallet { let fidelity_privkey = self.get_fidelity_keypair(index)?.secret_key(); - let cert_hash = bond.generate_cert_hash(maker_addr); + let cert_hash = bond.generate_cert_hash(maker_addr)?; let secp = Secp256k1::new(); let cert_sig = secp.sign_ecdsa( diff --git a/src/wallet/storage.rs b/src/wallet/storage.rs index 606ecfa6..3d14983c 100644 --- a/src/wallet/storage.rs +++ b/src/wallet/storage.rs @@ -35,8 +35,9 @@ pub(crate) struct WalletStore { /// Map of prevout to contract redeemscript. pub(super) prevout_to_contract_map: HashMap, /// Map for all the fidelity bond information. (index, (Bond, script_pubkey, is_spent)). + /// TODO: The hashmap doesn't require to script-pubkey as a additional parameter -> we can directly use `script_pub_key` api wherever required. + /// TODO: Can we also include the notion of `is_spent` inside the fidelity bond struct? pub(super) fidelity_bond: HashMap, - //TODO: Add last synced height and Wallet birthday. pub(super) last_synced_height: Option, pub(super) wallet_birthday: Option, diff --git a/tests/abort1.rs b/tests/abort1.rs index 72917d2f..76fa69d9 100644 --- a/tests/abort1.rs +++ b/tests/abort1.rs @@ -87,9 +87,7 @@ fn test_stop_taker_after_setup() { // Check balance after setting up maker server. let wallet = maker.wallet.read().unwrap(); - let all_utxos = wallet.get_all_utxo().unwrap(); - - let balances = wallet.get_balances(Some(&all_utxos)).unwrap(); + let balances = wallet.get_balances().unwrap(); assert_eq!(balances.regular, Amount::from_btc(0.14999).unwrap()); assert_eq!(balances.fidelity, Amount::from_btc(0.05).unwrap()); diff --git a/tests/abort2_case1.rs b/tests/abort2_case1.rs index 2fd5533f..a10dde21 100644 --- a/tests/abort2_case1.rs +++ b/tests/abort2_case1.rs @@ -84,9 +84,8 @@ fn test_abort_case_2_move_on_with_other_makers() { // Check balance after setting up maker server. let wallet = maker.wallet.read().unwrap(); - let all_utxos = wallet.get_all_utxo().unwrap(); - let balances = wallet.get_balances(Some(&all_utxos)).unwrap(); + let balances = wallet.get_balances().unwrap(); assert_eq!(balances.regular, Amount::from_btc(0.14999).unwrap()); assert_eq!(balances.fidelity, Amount::from_btc(0.05).unwrap()); @@ -215,7 +214,7 @@ fn test_abort_case_2_move_on_with_other_makers() { taker_wallet_mut.sync().unwrap(); - let balances = taker_wallet_mut.get_balances(None).unwrap(); + let balances = taker_wallet_mut.get_balances().unwrap(); assert_eq!(balances.swap, Amount::ZERO); assert_eq!(balances.regular, Amount::from_btc(0.14934642).unwrap()); diff --git a/tests/abort2_case2.rs b/tests/abort2_case2.rs index a5a2bdb2..793615d9 100644 --- a/tests/abort2_case2.rs +++ b/tests/abort2_case2.rs @@ -90,9 +90,8 @@ fn test_abort_case_2_recover_if_no_makers_found() { // Check balance after setting up maker server. let wallet = maker.wallet.read().unwrap(); - let all_utxos = wallet.get_all_utxo().unwrap(); - let balances = wallet.get_balances(Some(&all_utxos)).unwrap(); + let balances = wallet.get_balances().unwrap(); assert_eq!(balances.regular, Amount::from_btc(0.14999).unwrap()); assert_eq!(balances.fidelity, Amount::from_btc(0.05).unwrap()); diff --git a/tests/abort2_case3.rs b/tests/abort2_case3.rs index 6dfc1f0d..fe0d8d12 100644 --- a/tests/abort2_case3.rs +++ b/tests/abort2_case3.rs @@ -87,9 +87,8 @@ fn maker_drops_after_sending_senders_sigs() { // Check balance after setting up maker server. let wallet = maker.wallet.read().unwrap(); - let all_utxos = wallet.get_all_utxo().unwrap(); - let balances = wallet.get_balances(Some(&all_utxos)).unwrap(); + let balances = wallet.get_balances().unwrap(); assert_eq!(balances.regular, Amount::from_btc(0.14999).unwrap()); assert_eq!(balances.fidelity, Amount::from_btc(0.05).unwrap()); diff --git a/tests/abort3_case1.rs b/tests/abort3_case1.rs index 18ffd4ea..0c759a07 100644 --- a/tests/abort3_case1.rs +++ b/tests/abort3_case1.rs @@ -91,9 +91,8 @@ fn abort3_case1_close_at_contract_sigs_for_recvr_and_sender() { // Check balance after setting up maker server. let wallet = maker.wallet.read().unwrap(); - let all_utxos = wallet.get_all_utxo().unwrap(); - let balances = wallet.get_balances(Some(&all_utxos)).unwrap(); + let balances = wallet.get_balances().unwrap(); assert_eq!(balances.regular, Amount::from_btc(0.14999).unwrap()); assert_eq!(balances.fidelity, Amount::from_btc(0.05).unwrap()); diff --git a/tests/abort3_case2.rs b/tests/abort3_case2.rs index 3602a297..7e42a67a 100644 --- a/tests/abort3_case2.rs +++ b/tests/abort3_case2.rs @@ -85,9 +85,8 @@ fn abort3_case2_close_at_contract_sigs_for_recvr() { // Check balance after setting up maker server. let wallet = maker.wallet.read().unwrap(); - let all_utxos = wallet.get_all_utxo().unwrap(); - let balances = wallet.get_balances(Some(&all_utxos)).unwrap(); + let balances = wallet.get_balances().unwrap(); assert_eq!(balances.regular, Amount::from_btc(0.14999).unwrap()); assert_eq!(balances.fidelity, Amount::from_btc(0.05).unwrap()); diff --git a/tests/abort3_case3.rs b/tests/abort3_case3.rs index d44b211d..841cb696 100644 --- a/tests/abort3_case3.rs +++ b/tests/abort3_case3.rs @@ -83,9 +83,8 @@ fn abort3_case3_close_at_hash_preimage_handover() { // Check balance after setting up maker server. let wallet = maker.wallet.read().unwrap(); - let all_utxos = wallet.get_all_utxo().unwrap(); - let balances = wallet.get_balances(Some(&all_utxos)).unwrap(); + let balances = wallet.get_balances().unwrap(); assert_eq!(balances.regular, Amount::from_btc(0.14999).unwrap()); assert_eq!(balances.fidelity, Amount::from_btc(0.05).unwrap()); diff --git a/tests/fidelity.rs b/tests/fidelity.rs index 0971137c..a9992be6 100644 --- a/tests/fidelity.rs +++ b/tests/fidelity.rs @@ -129,7 +129,7 @@ fn test_fidelity() { { let wallet_read = maker.get_wallet().read().unwrap(); - let balances = wallet_read.get_balances(None).unwrap(); + let balances = wallet_read.get_balances().unwrap(); assert_eq!(balances.fidelity.to_sat(), 13000000); assert_eq!(balances.regular.to_sat(), 90998000); @@ -184,7 +184,7 @@ fn test_fidelity() { // Verify the balances again after all bonds are redeemed. { let wallet_read = maker.get_wallet().read().unwrap(); - let balances = wallet_read.get_balances(None).unwrap(); + let balances = wallet_read.get_balances().unwrap(); assert_eq!(balances.fidelity.to_sat(), 0); assert_eq!(balances.regular.to_sat(), 103996000); diff --git a/tests/malice1.rs b/tests/malice1.rs index 0ed77d7e..e7a0835d 100644 --- a/tests/malice1.rs +++ b/tests/malice1.rs @@ -80,9 +80,8 @@ fn malice1_taker_broadcast_contract_prematurely() { // Check balance after setting up maker server. let wallet = maker.wallet.read().unwrap(); - let all_utxos = wallet.get_all_utxo().unwrap(); - let balances = wallet.get_balances(Some(&all_utxos)).unwrap(); + let balances = wallet.get_balances().unwrap(); assert_eq!(balances.regular, Amount::from_btc(0.14999).unwrap()); assert_eq!(balances.fidelity, Amount::from_btc(0.05).unwrap()); diff --git a/tests/malice2.rs b/tests/malice2.rs index b8dfeadc..206598c4 100644 --- a/tests/malice2.rs +++ b/tests/malice2.rs @@ -79,9 +79,8 @@ fn malice2_maker_broadcast_contract_prematurely() { // Check balance after setting up maker server. let wallet = maker.wallet.read().unwrap(); - let all_utxos = wallet.get_all_utxo().unwrap(); - let balances = wallet.get_balances(Some(&all_utxos)).unwrap(); + let balances = wallet.get_balances().unwrap(); assert_eq!(balances.regular, Amount::from_btc(0.14999).unwrap()); assert_eq!(balances.fidelity, Amount::from_btc(0.05).unwrap()); diff --git a/tests/standard_swap.rs b/tests/standard_swap.rs index 1725417a..14257813 100644 --- a/tests/standard_swap.rs +++ b/tests/standard_swap.rs @@ -75,9 +75,8 @@ fn test_standard_coinswap() { // Check balance after setting up maker server. let wallet = maker.wallet.read().unwrap(); - let all_utxos = wallet.get_all_utxo().unwrap(); - let balances = wallet.get_balances(Some(&all_utxos)).unwrap(); + let balances = wallet.get_balances().unwrap(); assert_eq!(balances.regular, Amount::from_btc(0.14999).unwrap()); assert_eq!(balances.fidelity, Amount::from_btc(0.05).unwrap()); @@ -173,7 +172,7 @@ fn test_standard_coinswap() { bitcoind.client.send_raw_transaction(&tx).unwrap(); generate_blocks(bitcoind, 1); - let balances = taker_wallet_mut.get_balances(None).unwrap(); + let balances = taker_wallet_mut.get_balances().unwrap(); assert_eq!(balances.swap, Amount::ZERO); assert_eq!(balances.regular, Amount::from_btc(0.14934642).unwrap()); diff --git a/tests/test_framework/mod.rs b/tests/test_framework/mod.rs index 9d99be8a..d5374b89 100644 --- a/tests/test_framework/mod.rs +++ b/tests/test_framework/mod.rs @@ -312,11 +312,8 @@ pub fn fund_and_verify_taker( // Check if utxo list looks good. // TODO: Assert other interesting things from the utxo list. - let all_utxos = wallet.get_all_utxo().unwrap(); + let balances = wallet.get_balances().unwrap(); - let balances = wallet.get_balances(Some(&all_utxos)).unwrap(); - - // TODO: Think about this: utxo_count*utxo_amt. assert_eq!(balances.regular, Amount::from_btc(0.15).unwrap()); assert_eq!(balances.fidelity, Amount::ZERO); assert_eq!(balances.swap, Amount::ZERO); @@ -355,11 +352,8 @@ pub fn fund_and_verify_maker( // Assert external address index reached to 4. assert_eq!(wallet.get_external_index(), &utxo_count); - let all_utxos = wallet.get_all_utxo().unwrap(); - - let balances = wallet.get_balances(Some(&all_utxos)).unwrap(); + let balances = wallet.get_balances().unwrap(); - // TODO: Think about this: utxo_count*utxo_amt. assert_eq!(balances.regular, Amount::from_btc(0.20).unwrap()); assert_eq!(balances.fidelity, Amount::ZERO); assert_eq!(balances.swap, Amount::ZERO); @@ -378,8 +372,7 @@ pub fn verify_swap_results( // Check Taker balances { let wallet = taker.get_wallet(); - let all_utxos = wallet.get_all_utxo().unwrap(); - let balances = wallet.get_balances(Some(&all_utxos)).unwrap(); + let balances = wallet.get_balances().unwrap(); assert!( balances.regular == Amount::from_btc(0.14497).unwrap() // Successful coinswap @@ -416,8 +409,7 @@ pub fn verify_swap_results( .zip(org_maker_spend_balances.iter()) .for_each(|(maker, org_spend_balance)| { let wallet = maker.get_wallet().read().unwrap(); - let all_utxos = wallet.get_all_utxo().unwrap(); - let balances = wallet.get_balances(Some(&all_utxos)).unwrap(); + let balances = wallet.get_balances().unwrap(); assert!( balances.regular == Amount::from_btc(0.14557358).unwrap() // First maker on successful coinswap