Skip to content

feat(bob): concurrent swaps#971

Open
binarybaron wants to merge 13 commits into
masterfrom
feat/bob-concurrent-swaps
Open

feat(bob): concurrent swaps#971
binarybaron wants to merge 13 commits into
masterfrom
feat/bob-concurrent-swaps

Conversation

@binarybaron
Copy link
Copy Markdown

@binarybaron binarybaron commented Apr 28, 2026

No description provided.

@binarybaron
Copy link
Copy Markdown
Author

We should display the final state of the swap until the user acknowledges it.

@binarybaron
Copy link
Copy Markdown
Author

We should move the "cancel timeline" from the "history" page to the "swaps" page and hide it somewhat (make it expandable).

@binarybaron
Copy link
Copy Markdown
Author

Issue:

run_exclusive_initiation releases the initiation lock at swap_manager.rs:642 before the lock-tx is broadcast. UTXO selection happens inside the spawned task (TxLock::new → wallet.send_to_address); the wallet mutex is method-level, so two concurrent buy_xmr calls can build PSBTs from the same UTXOs.

@Einliterflasche
Copy link
Copy Markdown

Einliterflasche commented May 18, 2026

The interaction with the Watcher needs to be updated. The watcher still does in-line cancel_and_refund but should use the swap manager now. At least prevent the swap manager from spawning another state machine while the watcher is refunding

@binarybaron
Copy link
Copy Markdown
Author

The interaction with the Watcher needs to be updated. The watcher still does in-line cancel_and_refund but should use the swap manager now. At least prevent the swap manager from spawning another state machine while the watcher is refunding

Yes you are right but the whole watcher should become redundant anyway... It should not be required if the state machine is properly working. The watcher should be abolished at some point.

@Einliterflasche
Copy link
Copy Markdown

Einliterflasche commented May 18, 2026

I have 2 swaps where I had to do the XMR redeem manually which are now always automatically resumed - and keep failing. There needs to be a solution to this. Some kind of manually marking swaps as "do not resume" which is persisted across restarts.

Apart from that, I think we should change "Suspend" in the GUI to something like "Stop" or "Halt".

@binarybaron
Copy link
Copy Markdown
Author

I have 2 swaps where I had to do the XMR redeem manually which are now always automatically resumed - and keep failing. There needs to be a solution to this. Some kind of manully marking swaps as "do not resume" which is persisted across restarts.

Apart from that, I think we should change "Suspend" in the GUI to something like "Stop" or "Halt".

"Stop" or "Halt" seems like this actually stop the timelocks from counting down or does something on a protocol level. I dont see them being clearer than "suspend".

@Einliterflasche
Copy link
Copy Markdown

"Stop" or "Halt" seems like this actually stop the timelocks from counting down or does something on a protocol level. I dont see them being clearer than "suspend".

Suspend has the same issue, except I have a much harder time imagining what "suspend" means. It's technical jargon.
We need to warn anyway that this doesn't stop the swap / refund window from progressing.

@Einliterflasche
Copy link
Copy Markdown

I'll remove the watcher.

What do you propose for the manually-finished-swap problem? We could just add a new db table with columns like swap_id, should_not_resume.

@binarybaron
Copy link
Copy Markdown
Author

I'll remove the watcher.

What do you propose for the manually-finished-swap problem? We could just add a new db table with columns like swap_id, should_not_resume.

We could also bundle it with #932

@binarybaron
Copy link
Copy Markdown
Author

Not sure if removing the watcher is the move yet... its a reasonable safety fallback mechanism.

@Einliterflasche
Copy link
Copy Markdown

Einliterflasche commented May 18, 2026

It also turns out we use the watcher for balance and timelock updates. I'll keep the watcher and make it interact with the swap manager.

We could also bundle it with #932

Yes. Although there still should be an option to stop a swap from resuming without fully deleting it. It needs to be done as part of this PR though. Otherwise it gets really annoying for people in this situation of which there are a few.

@binarybaron binarybaron marked this pull request as ready for review May 19, 2026 10:01
@binarybaron
Copy link
Copy Markdown
Author

@Einliterflasche I will merge this today.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 4 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit b04ebf0. Configure here.

.await?;

Ok(())
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing database migration for auto_resume table

High Severity

The new get_auto_resume and set_auto_resume methods query an auto_resume table, but no migration file exists in swap/migrations/ to create it. The database uses sqlx::migrate!("./migrations") to manage schema. Without a migration, any call to these methods will fail with a "table not found" error at runtime, breaking both resume_all_swaps (which reads auto_resume) and suspend_swap with disable_auto_resume: true.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit b04ebf0. Configure here.

.await
.context("Failed to release initiation lock")?;
result
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initiation lock released before UTXO selection completes

High Severity

run_exclusive_initiation releases the initiation lock as soon as manager.start() returns, but start() only spawns the state-machine task — it doesn't wait for TxLock creation (UTXO selection). Two concurrent buy_xmr calls can therefore both pass determine_btc_to_swap sequentially, spawn their tasks, and then both tasks select UTXOs from the same unspent set concurrently, risking double-spend attempts or transaction failures.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit b04ebf0. Configure here.

let seller_addresses = db.get_addresses(seller_peer_id).await?;

let mut event_loop_handle = context.try_get_event_loop_handle().await?;
db.set_auto_resume(swap_id, true).await?;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Manual resume silently re-enables auto-resume preference

Medium Severity

resume_swap unconditionally calls db.set_auto_resume(swap_id, true), overwriting a user's explicit "Don't auto resume on startup" preference. A user who disabled auto-resume for a problematic swap and later manually resumes it to check progress will unknowingly re-enable auto-resume, causing the swap to resume on every restart — the exact problem the PR discussion identifies users are experiencing.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit b04ebf0. Configure here.

const swap = state.swap.swaps[swapId];
return swap != null && swap.curr.type !== "Released";
});
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useIsSpecificSwapRunning ignores retry-backoff state inconsistently

Low Severity

useIsSpecificSwapRunning treats any Released event as "not running," but the codebase's isSwapInFlight helper (used by useIsSwapRunning, useRunningSwapsCount, badge counts) correctly treats a Released with next_auto_resume_at_unix_ms as still in flight. This inconsistency means a swap in retry-backoff is counted as running for badges but not for the per-swap check, which could cause subtle UI contradictions.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit b04ebf0. Configure here.

@Einliterflasche
Copy link
Copy Markdown

We definitely need a docker test which spawns multiple swaps. Also get the ui mock system to work again, it's pretty broken now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants