From 89b2cff1267548655bebcf08fc3e4a55e987f78d Mon Sep 17 00:00:00 2001 From: binarybaron Date: Wed, 10 Jun 2026 12:57:51 +0200 Subject: [PATCH 1/4] feat(swap): check if Monero redeem is already present on chain before publishing --- monero-wallet/src/wallets.rs | 13 +++++++++++++ swap/src/monero.rs | 2 +- swap/src/protocol/bob/swap.rs | 11 +++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/monero-wallet/src/wallets.rs b/monero-wallet/src/wallets.rs index 46306b85e2..d36e3be09a 100644 --- a/monero-wallet/src/wallets.rs +++ b/monero-wallet/src/wallets.rs @@ -5,6 +5,7 @@ //! - wait for transactions to be confirmed //! - send money from one wallet to another. pub use monero_sys::{Daemon, WalletHandle as Wallet, WalletHandleListener}; +pub use monero_wallet_ng::rpc::TransactionStatus; use anyhow::{Context, Result}; use monero_address::Network; @@ -292,6 +293,18 @@ impl Wallets { rpc_client } + pub async fn transaction_status(&self, tx_hash: &TxHash) -> Result { + use monero_wallet_ng::rpc::ProvidesTransactionStatus; + + let rpc_client = self.rpc_client().await; + let tx_id = tx_hash_to_bytes(tx_hash)?; + + rpc_client + .transaction_status(tx_id) + .await + .context("Failed to query Monero transaction status") + } + pub async fn direct_rpc_block_height(&self) -> Result { use monero_daemon_rpc::prelude::ProvidesBlockchainMeta; let rpc_client = self.rpc_client().await; diff --git a/swap/src/monero.rs b/swap/src/monero.rs index 143438eef9..479f5260ab 100644 --- a/swap/src/monero.rs +++ b/swap/src/monero.rs @@ -6,4 +6,4 @@ pub use ::monero_address::Network; pub use ::monero_oxide_ext::{PrivateKey, PublicKey}; pub use curve25519_dalek::scalar::Scalar; pub use swap_core::monero::primitives::*; -pub use wallet::{Daemon, Wallet, Wallets}; +pub use wallet::{Daemon, TransactionStatus, Wallet, Wallets}; diff --git a/swap/src/protocol/bob/swap.rs b/swap/src/protocol/bob/swap.rs index 4b73740a4e..743fbed024 100644 --- a/swap/src/protocol/bob/swap.rs +++ b/swap/src/protocol/bob/swap.rs @@ -793,6 +793,17 @@ async fn next_state( retry( "Publishing Monero redeem transaction", || async { + let status = monero_wallet + .transaction_status(&xmr_redeem_tx_hash) + .await + .context("Failed to check whether Monero redeem transaction is already present on chain") + .map_err(backoff::Error::transient)?; + + if !matches!(status, monero::TransactionStatus::Unknown) { + tracing::info!(%swap_id, %xmr_redeem_tx_hash, "Monero redeem transaction is already present on chain, skipping publish"); + return Ok(()); + } + monero_wallet .rpc_client() .await From 02aca8d4d833d42d90c53c15afb480abc3acf5ca Mon Sep 17 00:00:00 2001 From: binarybaron Date: Wed, 10 Jun 2026 13:03:22 +0200 Subject: [PATCH 2/4] feat(asb): check if Monero refund is already present on chain before publishing --- swap/src/protocol/alice/swap.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/swap/src/protocol/alice/swap.rs b/swap/src/protocol/alice/swap.rs index a5120940fa..351222f28a 100644 --- a/swap/src/protocol/alice/swap.rs +++ b/swap/src/protocol/alice/swap.rs @@ -753,6 +753,17 @@ where retry( "Publishing Monero refund transaction", || async { + let status = monero_wallet + .transaction_status(&xmr_refund_tx_hash) + .await + .context("Failed to check whether Monero refund transaction is already present on chain") + .map_err(backoff::Error::transient)?; + + if !matches!(status, monero::TransactionStatus::Unknown) { + tracing::info!(%swap_id, %xmr_refund_tx_hash, "Monero refund transaction is already present on chain, skipping publish"); + return Ok(()); + } + monero_wallet .rpc_client() .await From 222283d18600a9d2600437b8fb0c3bb075bdd294 Mon Sep 17 00:00:00 2001 From: binarybaron Date: Wed, 10 Jun 2026 13:08:31 +0200 Subject: [PATCH 3/4] refactor(monero-wallet): wrap transaction status query in Wallets::is_transaction_present --- monero-wallet/src/wallets.rs | 11 ++++++----- swap/src/monero.rs | 2 +- swap/src/protocol/alice/swap.rs | 6 +++--- swap/src/protocol/bob/swap.rs | 6 +++--- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/monero-wallet/src/wallets.rs b/monero-wallet/src/wallets.rs index d36e3be09a..ee053168d6 100644 --- a/monero-wallet/src/wallets.rs +++ b/monero-wallet/src/wallets.rs @@ -5,7 +5,6 @@ //! - wait for transactions to be confirmed //! - send money from one wallet to another. pub use monero_sys::{Daemon, WalletHandle as Wallet, WalletHandleListener}; -pub use monero_wallet_ng::rpc::TransactionStatus; use anyhow::{Context, Result}; use monero_address::Network; @@ -293,16 +292,18 @@ impl Wallets { rpc_client } - pub async fn transaction_status(&self, tx_hash: &TxHash) -> Result { - use monero_wallet_ng::rpc::ProvidesTransactionStatus; + pub async fn is_transaction_present(&self, tx_hash: &TxHash) -> Result { + use monero_wallet_ng::rpc::{ProvidesTransactionStatus, TransactionStatus}; let rpc_client = self.rpc_client().await; let tx_id = tx_hash_to_bytes(tx_hash)?; - rpc_client + let status = rpc_client .transaction_status(tx_id) .await - .context("Failed to query Monero transaction status") + .context("Failed to query Monero transaction status")?; + + Ok(!matches!(status, TransactionStatus::Unknown)) } pub async fn direct_rpc_block_height(&self) -> Result { diff --git a/swap/src/monero.rs b/swap/src/monero.rs index 479f5260ab..143438eef9 100644 --- a/swap/src/monero.rs +++ b/swap/src/monero.rs @@ -6,4 +6,4 @@ pub use ::monero_address::Network; pub use ::monero_oxide_ext::{PrivateKey, PublicKey}; pub use curve25519_dalek::scalar::Scalar; pub use swap_core::monero::primitives::*; -pub use wallet::{Daemon, TransactionStatus, Wallet, Wallets}; +pub use wallet::{Daemon, Wallet, Wallets}; diff --git a/swap/src/protocol/alice/swap.rs b/swap/src/protocol/alice/swap.rs index 351222f28a..cada39cb4c 100644 --- a/swap/src/protocol/alice/swap.rs +++ b/swap/src/protocol/alice/swap.rs @@ -753,13 +753,13 @@ where retry( "Publishing Monero refund transaction", || async { - let status = monero_wallet - .transaction_status(&xmr_refund_tx_hash) + let is_present = monero_wallet + .is_transaction_present(&xmr_refund_tx_hash) .await .context("Failed to check whether Monero refund transaction is already present on chain") .map_err(backoff::Error::transient)?; - if !matches!(status, monero::TransactionStatus::Unknown) { + if is_present { tracing::info!(%swap_id, %xmr_refund_tx_hash, "Monero refund transaction is already present on chain, skipping publish"); return Ok(()); } diff --git a/swap/src/protocol/bob/swap.rs b/swap/src/protocol/bob/swap.rs index 743fbed024..9119e76e4a 100644 --- a/swap/src/protocol/bob/swap.rs +++ b/swap/src/protocol/bob/swap.rs @@ -793,13 +793,13 @@ async fn next_state( retry( "Publishing Monero redeem transaction", || async { - let status = monero_wallet - .transaction_status(&xmr_redeem_tx_hash) + let is_present = monero_wallet + .is_transaction_present(&xmr_redeem_tx_hash) .await .context("Failed to check whether Monero redeem transaction is already present on chain") .map_err(backoff::Error::transient)?; - if !matches!(status, monero::TransactionStatus::Unknown) { + if is_present { tracing::info!(%swap_id, %xmr_redeem_tx_hash, "Monero redeem transaction is already present on chain, skipping publish"); return Ok(()); } From 4fd033f4d544d5ee7b1325d9558e42de3e17f1e2 Mon Sep 17 00:00:00 2001 From: binarybaron Date: Wed, 10 Jun 2026 13:10:50 +0200 Subject: [PATCH 4/4] docs: add changelog entry for Monero redeem/refund on-chain check --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a7b6c79e70..08bb87715a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +- ASB+GUI: Skip publishing the Monero redeem/refund transaction if it is already present on chain (e.g. after a restart) + ## [4.8.4] - 2026-06-09 ## [4.8.3] - 2026-06-08