diff --git a/swap/src/api/request.rs b/swap/src/api/request.rs index 1f0e229f..be4b03fe 100644 --- a/swap/src/api/request.rs +++ b/swap/src/api/request.rs @@ -4,9 +4,8 @@ use crate::cli::{list_sellers, EventLoop, SellerStatus}; use crate::libp2p_ext::MultiAddrExt; use crate::network::quote::{BidQuote, ZeroQuoteReceived}; use crate::network::swarm; -use crate::protocol::bob; -use crate::protocol::bob::swap::is_complete; use crate::protocol::bob::{BobState, Swap}; +use crate::protocol::{bob, State}; use crate::{bitcoin, cli, monero, rpc}; use anyhow::{bail, Context as AnyContext, Result}; use libp2p::core::Multiaddr; @@ -195,8 +194,6 @@ impl Request { .as_ref() .context("Could not get Bitcoin wallet")?; - let swap_state: BobState = context.db.get_state(swap_id).await?.try_into()?; - let peerId = context .db .get_peer_id(swap_id) @@ -209,12 +206,53 @@ impl Request { .await .with_context(|| "Could not get addressess")?; - let is_completed = is_complete(&swap_state); + let state = context.db.get_state(swap_id).await?; + let is_completed = state.swap_finished(); let start_date = context.db.get_swap_start_date(swap_id).await?; + let swap_state: BobState = state.try_into()?; let state_name = format!("{}", swap_state); + let ( + xmr_amount, + btc_amount, + tx_lock_id, + tx_cancel_fee, + tx_refund_fee, + btc_refund_address, + cancel_timelock, + punish_timelock, + ) = context + .db + .get_states(swap_id) + .await? + .iter() + .find_map(|state| { + if let State::Bob(BobState::SwapSetupCompleted(state2)) = state { + let xmr_amount = state2.xmr; + let btc_amount = state2.tx_lock.lock_amount().to_sat(); + let tx_cancel_fee = state2.tx_cancel_fee.to_sat(); + let tx_refund_fee = state2.tx_refund_fee.to_sat(); + let tx_lock_id = state2.tx_lock.txid(); + let btc_refund_address = state2.refund_address.to_string(); + + return Some(( + xmr_amount, + btc_amount, + tx_lock_id, + tx_cancel_fee, + tx_refund_fee, + btc_refund_address, + state2.cancel_timelock, + state2.punish_timelock, + )); + } else { + None + } + }) + .with_context(|| "Did not find SwapSetupCompleted state for swap")?; + let timelock = match swap_state { BobState::Started { .. } | BobState::SafelyAborted @@ -230,7 +268,6 @@ impl Request { Some(state.expired_timelock(bitcoin_wallet).await) } BobState::BtcPunished { .. } => Some(Ok(ExpiredTimelocks::Punish)), - // swap is already finished BobState::BtcRefunded(_) | BobState::BtcRedeemed(_) | BobState::XmrRedeemed { .. } => None, @@ -245,10 +282,18 @@ impl Request { }, "completed": is_completed, "startDate": start_date, - // If none return null, if some unwrap and return as json - "timelock": timelock.map(|tl| tl.map(|tl| json!(tl)).unwrap_or(json!(null))).unwrap_or(json!(null)), - // Use display to get the string representation of the state "stateName": state_name, + "xmrAmount": xmr_amount, + "btcAmount": btc_amount, + "txLockId": tx_lock_id, + "txCancelFee": tx_cancel_fee, + "txRefundFee": tx_refund_fee, + "btcRefundAddress": btc_refund_address.to_string(), + "cancelTimelock": cancel_timelock, + "punishTimelock": punish_timelock, + // If the timelock is None, it means that the swap is in a state where the timelock is not accessible to us. + // If that is the case, we return null. Otherwise, we return the timelock. + "timelock": timelock.map(|tl| tl.map(|tl| json!(tl)).unwrap_or(json!(null))).unwrap_or(json!(null)), })) } Method::BuyXmr { @@ -263,7 +308,7 @@ impl Request { tokio::select! { biased; _ = context.swap_lock.listen_for_swap_force_suspension() => { - tracing::info!("Shutdown signal received, exiting"); + tracing::debug!("Shutdown signal received, exiting"); () }, _ = async { @@ -477,7 +522,7 @@ impl Request { () }, _ = context.swap_lock.listen_for_swap_force_suspension() => { - tracing::info!("Shutdown signal received, exiting"); + tracing::debug!("Shutdown signal received, exiting"); () } } diff --git a/swap/src/bitcoin/wallet.rs b/swap/src/bitcoin/wallet.rs index 7cd334fa..b133bb8c 100644 --- a/swap/src/bitcoin/wallet.rs +++ b/swap/src/bitcoin/wallet.rs @@ -762,9 +762,10 @@ impl Client { self.blockchain.get_tx(txid) } - fn update_state(&mut self) -> Result<()> { + fn update_state(&mut self, force_sync: bool) -> Result<()> { let now = Instant::now(); - if now < self.last_sync + self.sync_interval { + + if !force_sync && now < self.last_sync + self.sync_interval { return Ok(()); } @@ -784,10 +785,11 @@ impl Client { if !self.script_history.contains_key(&script) { self.script_history.insert(script.clone(), vec![]); + self.update_state(true)?; + }else { + self.update_state(false)?; } - - self.update_state()?; - + let history = self.script_history.entry(script).or_default(); let history_of_tx = history diff --git a/swap/src/database/sqlite.rs b/swap/src/database/sqlite.rs index 087a9163..30a7b2b7 100644 --- a/swap/src/database/sqlite.rs +++ b/swap/src/database/sqlite.rs @@ -5,7 +5,7 @@ use anyhow::{anyhow, Context, Result}; use async_trait::async_trait; use libp2p::{Multiaddr, PeerId}; use sqlx::sqlite::Sqlite; -use sqlx::{Pool, SqlitePool}; +use sqlx::{Pool, Row, SqlitePool}; use std::collections::HashMap; use std::path::Path; use std::str::FromStr; @@ -270,6 +270,39 @@ impl Database for SqliteDatabase { result } + async fn get_states(&self, swap_id: Uuid) -> Result> { + let mut conn = self.pool.acquire().await?; + let swap_id = swap_id.to_string(); + + // TODO: We should use query! instead of query here to allow for at-compile-time validation + // I didn't manage to generate the mappings for the query! macro because of problems with sqlx-cli + let rows = sqlx::query( + r#" + SELECT state + FROM swap_states + WHERE swap_id = ? + "#, + ) + .bind(swap_id) + .fetch_all(&mut conn) + .await?; + + let result = rows + .iter() + .map(|row| { + let state_str: &str = row.try_get("state")?; + + let state = match serde_json::from_str::(state_str) { + Ok(a) => Ok(State::from(a)), + Err(e) => Err(e), + }?; + Ok(state) + }) + .collect::>>(); + + result + } + async fn raw_all(&self) -> Result>> { let mut conn = self.pool.acquire().await?; let rows = sqlx::query!( diff --git a/swap/src/protocol.rs b/swap/src/protocol.rs index 794f27a9..0e15f89a 100644 --- a/swap/src/protocol.rs +++ b/swap/src/protocol.rs @@ -143,6 +143,7 @@ pub trait Database { async fn get_swap_start_date(&self, swap_id: Uuid) -> Result; async fn insert_latest_state(&self, swap_id: Uuid, state: State) -> Result<()>; async fn get_state(&self, swap_id: Uuid) -> Result; + async fn get_states(&self, swap_id: Uuid) -> Result>; async fn all(&self) -> Result>; async fn raw_all(&self) -> Result>>; } diff --git a/swap/src/protocol/bob/state.rs b/swap/src/protocol/bob/state.rs index e390ec41..edf902b6 100644 --- a/swap/src/protocol/bob/state.rs +++ b/swap/src/protocol/bob/state.rs @@ -288,13 +288,13 @@ pub struct State2 { S_a_monero: monero::PublicKey, S_a_bitcoin: bitcoin::PublicKey, v: monero::PrivateViewKey, - xmr: monero::Amount, - cancel_timelock: CancelTimelock, - punish_timelock: PunishTimelock, - refund_address: bitcoin::Address, + pub xmr: monero::Amount, + pub cancel_timelock: CancelTimelock, + pub punish_timelock: PunishTimelock, + pub refund_address: bitcoin::Address, redeem_address: bitcoin::Address, punish_address: bitcoin::Address, - tx_lock: bitcoin::TxLock, + pub tx_lock: bitcoin::TxLock, tx_cancel_sig_a: Signature, tx_refund_encsig: bitcoin::EncryptedSignature, min_monero_confirmations: u64, @@ -303,9 +303,9 @@ pub struct State2 { #[serde(with = "::bitcoin::util::amount::serde::as_sat")] tx_punish_fee: bitcoin::Amount, #[serde(with = "::bitcoin::util::amount::serde::as_sat")] - tx_refund_fee: bitcoin::Amount, + pub tx_refund_fee: bitcoin::Amount, #[serde(with = "::bitcoin::util::amount::serde::as_sat")] - tx_cancel_fee: bitcoin::Amount, + pub tx_cancel_fee: bitcoin::Amount, } impl State2 { diff --git a/swap/src/rpc/methods.rs b/swap/src/rpc/methods.rs index b7dc39cc..c5fa461c 100644 --- a/swap/src/rpc/methods.rs +++ b/swap/src/rpc/methods.rs @@ -51,7 +51,7 @@ pub fn register_modules(context: Arc) -> RpcModule> { .unwrap(); module - .register_async_method("get_raw_history", |params, context| async move { + .register_async_method("get_raw_states", |params, context| async move { execute_request(params, Method::GetRawStates, &context).await }) .unwrap();