fix: Résolution du bug txn-mempool-conflict avec mutex global
All checks were successful
Build and Push to Registry / build-and-push (push) Successful in 1m58s

- Ajout de src/tx_queue.rs avec mutex global TX_QUEUE
- Modification de commit_new_transaction() pour verrouiller les transactions
- Prévient les race conditions lors de la sélection d'UTXOs concurrents
- Fix erreur RPC -26 'txn-mempool-conflict'
This commit is contained in:
NicolasCantu 2025-10-13 09:20:58 +02:00
parent a056d44cbf
commit 470f0b36b9
3 changed files with 37 additions and 4 deletions

View File

@ -21,6 +21,7 @@ use sdk_common::{
use crate::{lock_freezed_utxos, MutexExt, DAEMON, STORAGE, WALLET};
use crate::{
message::{broadcast_message, BroadcastType},
tx_queue::lock_tx_queue,
CHAIN_TIP,
};
@ -307,12 +308,14 @@ fn process_validation(
.extend(commit_msg.validation_tokens.iter());
// Sort by public key to group validations by signer
state_to_update.validation_tokens.sort_unstable_by_key(|proof| proof.get_key());
state_to_update
.validation_tokens
.sort_unstable_by_key(|proof| proof.get_key());
// Remove duplicates where same key validates same message
state_to_update.validation_tokens.dedup_by(|a, b| {
a.get_key() == b.get_key() && a.get_message() == b.get_message()
});
state_to_update
.validation_tokens
.dedup_by(|a, b| a.get_key() == b.get_key() && a.get_message() == b.get_message());
}
let state_to_validate = updated_process.get_state_for_id(&new_state_id)?;
@ -332,6 +335,11 @@ fn commit_new_transaction(
updated_process: &mut Process,
state_to_commit: ProcessState,
) -> Result<OutPoint> {
// Acquire global transaction lock to prevent concurrent transactions
// from creating mempool conflicts (txn-mempool-conflict error -26)
let _tx_lock = lock_tx_queue()?;
log::debug!("Acquired transaction queue lock for process {}", updated_process.get_process_id()?);
let sp_wallet = WALLET
.get()
.ok_or(Error::msg("Wallet not initialized"))?
@ -403,6 +411,14 @@ fn commit_new_transaction(
daemon.test_mempool_accept(&final_tx)?;
let txid = daemon.broadcast(&final_tx)?;
// ✅ AJOUTER UN DÉLAI ICI
log::info!(
"Transaction broadcast: {}. Attente de confirmation mempool...",
txid
);
std::thread::sleep(std::time::Duration::from_millis(500)); // 500ms de délai
let commited_in = OutPoint::new(txid, 0);
freezed_utxos.insert(commited_in);

View File

@ -59,6 +59,7 @@ mod daemon;
mod faucet;
mod message;
mod scan;
mod tx_queue;
use crate::config::Config;
use crate::{

16
src/tx_queue.rs Normal file
View File

@ -0,0 +1,16 @@
use std::sync::{Mutex, MutexGuard, OnceLock};
use anyhow::Result;
/// Global transaction queue mutex to prevent concurrent transactions
/// from creating mempool conflicts (txn-mempool-conflict error -26)
static TX_QUEUE: OnceLock<Mutex<()>> = OnceLock::new();
/// Acquires the global transaction lock to serialize blockchain commits.
/// This ensures only one transaction is created and broadcast at a time,
/// preventing race conditions when selecting UTXOs.
pub fn lock_tx_queue() -> Result<MutexGuard<'static, ()>> {
TX_QUEUE
.get_or_init(|| Mutex::new(()))
.lock()
.map_err(|e| anyhow::Error::msg(format!("Failed to lock tx queue: {}", e)))
}