mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2025-08-08 14:32:24 -04:00
feat(gui): Refund swap in the background (#154)
Swaps will now be refunded as soon as the cancel timelock expires if the GUI is running but the swap dialog is not open.
This commit is contained in:
parent
4cf5cf719a
commit
e46be4a9ff
12 changed files with 210 additions and 27 deletions
|
@ -301,6 +301,8 @@ impl ContextBuilder {
|
|||
let seed = Seed::from_file_or_generate(data_dir.as_path())
|
||||
.context("Failed to read seed in file")?;
|
||||
|
||||
let swap_lock = Arc::new(SwapLock::new());
|
||||
|
||||
// We initialize the Bitcoin wallet below
|
||||
// To display the progress to the user, we emit events to the Tauri frontend
|
||||
self.tauri_handle
|
||||
|
@ -355,7 +357,12 @@ impl ContextBuilder {
|
|||
// we start a background task to watch for timelock changes.
|
||||
if let Some(wallet) = bitcoin_wallet.clone() {
|
||||
if self.tauri_handle.is_some() {
|
||||
let watcher = Watcher::new(wallet, db.clone(), self.tauri_handle.clone());
|
||||
let watcher = Watcher::new(
|
||||
wallet,
|
||||
db.clone(),
|
||||
self.tauri_handle.clone(),
|
||||
swap_lock.clone(),
|
||||
);
|
||||
tokio::spawn(watcher.run());
|
||||
}
|
||||
}
|
||||
|
@ -375,7 +382,7 @@ impl ContextBuilder {
|
|||
is_testnet: self.is_testnet,
|
||||
data_dir,
|
||||
},
|
||||
swap_lock: Arc::new(SwapLock::new()),
|
||||
swap_lock,
|
||||
tasks: Arc::new(PendingTaskList::default()),
|
||||
tauri_handle: self.tauri_handle,
|
||||
};
|
||||
|
|
|
@ -15,6 +15,8 @@ const SWAP_STATE_CHANGE_EVENT_NAME: &str = "swap-database-state-update";
|
|||
const TIMELOCK_CHANGE_EVENT_NAME: &str = "timelock-change";
|
||||
const CONTEXT_INIT_PROGRESS_EVENT_NAME: &str = "context-init-progress-update";
|
||||
const BALANCE_CHANGE_EVENT_NAME: &str = "balance-change";
|
||||
const BACKGROUND_REFUND_EVENT_NAME: &str = "background-refund";
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TauriHandle(
|
||||
#[cfg(feature = "tauri")]
|
||||
|
@ -82,6 +84,13 @@ pub trait TauriEmitter {
|
|||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn emit_background_refund_event(&self, swap_id: Uuid, state: BackgroundRefundState) {
|
||||
let _ = self.emit_tauri_event(
|
||||
BACKGROUND_REFUND_EVENT_NAME,
|
||||
TauriBackgroundRefundEvent { swap_id, state },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl TauriEmitter for TauriHandle {
|
||||
|
@ -222,6 +231,23 @@ pub struct TauriTimelockChangeEvent {
|
|||
timelock: Option<ExpiredTimelocks>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
#[typeshare]
|
||||
#[serde(tag = "type", content = "content")]
|
||||
pub enum BackgroundRefundState {
|
||||
Started,
|
||||
Failed { error: String },
|
||||
Completed,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
#[typeshare]
|
||||
pub struct TauriBackgroundRefundEvent {
|
||||
#[typeshare(serialized_as = "string")]
|
||||
swap_id: Uuid,
|
||||
state: BackgroundRefundState,
|
||||
}
|
||||
|
||||
/// This struct contains the settings for the Context
|
||||
#[typeshare]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
use super::api::tauri_bindings::TauriEmitter;
|
||||
use super::api::tauri_bindings::{BackgroundRefundState, TauriEmitter};
|
||||
use super::api::SwapLock;
|
||||
use super::cancel_and_refund;
|
||||
use crate::bitcoin::{ExpiredTimelocks, Wallet};
|
||||
use crate::cli::api::tauri_bindings::TauriHandle;
|
||||
use crate::protocol::bob::BobState;
|
||||
|
@ -15,6 +17,7 @@ pub struct Watcher {
|
|||
wallet: Arc<Wallet>,
|
||||
database: Arc<dyn Database + Send + Sync>,
|
||||
tauri: Option<TauriHandle>,
|
||||
swap_lock: Arc<SwapLock>,
|
||||
/// This saves for every running swap the last known timelock status
|
||||
cached_timelocks: HashMap<Uuid, Option<ExpiredTimelocks>>,
|
||||
}
|
||||
|
@ -28,12 +31,14 @@ impl Watcher {
|
|||
wallet: Arc<Wallet>,
|
||||
database: Arc<dyn Database + Send + Sync>,
|
||||
tauri: Option<TauriHandle>,
|
||||
swap_lock: Arc<SwapLock>,
|
||||
) -> Self {
|
||||
Self {
|
||||
wallet,
|
||||
database,
|
||||
cached_timelocks: HashMap::new(),
|
||||
tauri,
|
||||
swap_lock,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,6 +68,7 @@ impl Watcher {
|
|||
.balance()
|
||||
.await
|
||||
.context("Failed to fetch Bitcoin balance, retrying later")?;
|
||||
|
||||
// Emit a balance update event
|
||||
self.tauri.emit_balance_update_event(new_balance);
|
||||
|
||||
|
@ -98,6 +104,49 @@ impl Watcher {
|
|||
|
||||
// Insert new status
|
||||
self.cached_timelocks.insert(swap_id, new_timelock_status);
|
||||
|
||||
// If the swap has to be refunded, do it in the background
|
||||
if let Some(ExpiredTimelocks::Cancel { .. }) = new_timelock_status {
|
||||
// If the swap is already running, we can skip the refund
|
||||
// The refund will be handled by the state machine
|
||||
if let Some(current_swap_id) = self.swap_lock.get_current_swap_id().await {
|
||||
if current_swap_id == swap_id {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(e) = self.swap_lock.acquire_swap_lock(swap_id).await {
|
||||
tracing::error!(%e, %swap_id, "Watcher failed to refund a swap in the background because another swap is already running");
|
||||
continue;
|
||||
}
|
||||
|
||||
self.tauri
|
||||
.emit_background_refund_event(swap_id, BackgroundRefundState::Started);
|
||||
|
||||
match cancel_and_refund(swap_id, self.wallet.clone(), self.database.clone()).await {
|
||||
Err(e) => {
|
||||
tracing::error!(%e, %swap_id, "Watcher failed to refund a swap in the background");
|
||||
|
||||
self.tauri.emit_background_refund_event(
|
||||
swap_id,
|
||||
BackgroundRefundState::Failed {
|
||||
error: format!("{:?}", e),
|
||||
},
|
||||
);
|
||||
}
|
||||
Ok(_) => {
|
||||
tracing::info!(%swap_id, "Watcher has refunded a swap in the background");
|
||||
|
||||
self.tauri.emit_background_refund_event(
|
||||
swap_id,
|
||||
BackgroundRefundState::Completed,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// We have to release the swap lock when we are done
|
||||
self.swap_lock.release_swap_lock().await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue