Skip to content
This repository was archived by the owner on Feb 3, 2025. It is now read-only.

Commit bb75f79

Browse files
committed
Track pending payjoin transactions
Both sender original PSBT and payjoin proposal PSBT are tracked as pending. Only the one that confirms proceeds to live in the wallet forever.
1 parent 66124c2 commit bb75f79

File tree

2 files changed

+33
-10
lines changed

2 files changed

+33
-10
lines changed

mutiny-core/src/nodemanager.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -767,7 +767,16 @@ impl<S: MutinyStorage> NodeManager<S> {
767767
.map_err(|_| MutinyError::IncorrectNetwork)?;
768768
let address = uri.address.clone();
769769
let original_psbt = self.wallet.create_signed_psbt(address, amount, fee_rate)?;
770-
// TODO ensure this creates a pending tx in the UI. Ensure locked UTXO.
770+
// Track this transaction in the wallet so it shows as an ActivityItem in UI.
771+
// We'll cancel it if and when this original_psbt fallback is replaced with a received payjoin.
772+
self.wallet
773+
.insert_tx(
774+
original_psbt.clone().extract_tx(),
775+
ConfirmationTime::unconfirmed(crate::utils::now().as_secs()),
776+
None,
777+
)
778+
.await?;
779+
771780
let fee_rate = if let Some(rate) = fee_rate {
772781
FeeRate::from_sat_per_vb(rate)
773782
} else {
@@ -798,6 +807,7 @@ impl<S: MutinyStorage> NodeManager<S> {
798807
let proposal_psbt = match Self::poll_payjoin_sender(stop, req_ctx).await {
799808
Ok(psbt) => psbt,
800809
Err(e) => {
810+
// self.wallet cancel_tx
801811
log_error!(logger, "Error polling payjoin sender: {e}");
802812
return;
803813
}
@@ -867,11 +877,13 @@ impl<S: MutinyStorage> NodeManager<S> {
867877
labels: Vec<String>,
868878
) -> Result<Txid, MutinyError> {
869879
log_debug!(logger, "Sending payjoin..");
880+
let original_tx = original_psbt.clone().extract_tx();
870881
let tx = wallet
871882
.send_payjoin(original_psbt, proposal_psbt, labels)
872883
.await?;
873884
let txid = tx.txid();
874885
wallet.broadcast_transaction(tx).await?;
886+
wallet.cancel_tx(&original_tx)?;
875887
log_info!(logger, "Payjoin broadcast! TXID: {txid}");
876888
Ok(txid)
877889
}
@@ -905,6 +917,7 @@ impl<S: MutinyStorage> NodeManager<S> {
905917
.map_err(|e| PayjoinError::ReceiverStateMachine(e.to_string()))?;
906918
let mut payjoin_proposal = wallet
907919
.process_payjoin_proposal(proposal)
920+
.await
908921
.map_err(|e| PayjoinError::ReceiverStateMachine(e.to_string()))?;
909922

910923
let (req, ohttp_ctx) = payjoin_proposal

mutiny-core/src/onchain.rs

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,12 @@ impl<S: MutinyStorage> OnChainWallet<S> {
355355
Ok(())
356356
}
357357

358+
pub(crate) fn cancel_tx(&self, tx: &Transaction) -> Result<(), MutinyError> {
359+
let mut wallet = self.wallet.try_write()?;
360+
wallet.cancel_tx(tx);
361+
Ok(())
362+
}
363+
358364
fn is_mine(&self, script: &Script) -> Result<bool, MutinyError> {
359365
Ok(self.wallet.try_read()?.is_mine(script))
360366
}
@@ -363,7 +369,7 @@ impl<S: MutinyStorage> OnChainWallet<S> {
363369
Ok(self.wallet.try_read()?.list_unspent().collect())
364370
}
365371

366-
pub fn process_payjoin_proposal(
372+
pub async fn process_payjoin_proposal(
367373
&self,
368374
proposal: payjoin::receive::v2::UncheckedProposal,
369375
) -> Result<payjoin::receive::v2::PayjoinProposal, payjoin::Error> {
@@ -399,26 +405,30 @@ impl<S: MutinyStorage> OnChainWallet<S> {
399405

400406
// Outputs may be substituted for e.g. batching at this stage
401407
// We're not doing this yet.
402-
403408
let payjoin_proposal = provisional_payjoin.finalize_proposal(
404409
|psbt| {
405410
let mut psbt = psbt.clone();
406-
let wallet = self
407-
.wallet
408-
.try_read()
409-
.map_err(|_| Error::Server(MutinyError::WalletSigningFailed.into()))?;
410-
wallet
411+
self.wallet
412+
.try_write()
413+
.map_err(|_| Error::Server(MutinyError::WalletSigningFailed.into()))?
411414
.sign(&mut psbt, SignOptions::default())
412415
.map_err(|_| Error::Server(MutinyError::WalletSigningFailed.into()))?;
413416
Ok(psbt)
414417
},
415418
// TODO: check Mutiny's minfeerate is present here
416419
Some(payjoin::bitcoin::FeeRate::MIN),
417420
)?;
418-
let payjoin_proposal_psbt = payjoin_proposal.psbt();
421+
let payjoin_psbt_tx = payjoin_proposal.psbt().clone().extract_tx();
422+
self.insert_tx(
423+
payjoin_psbt_tx,
424+
ConfirmationTime::unconfirmed(crate::utils::now().as_secs()),
425+
None,
426+
)
427+
.await
428+
.map_err(|_| Error::Server(MutinyError::WalletOperationFailed.into()))?;
419429
log::debug!(
420430
"Receiver's Payjoin proposal PSBT Rsponse: {:#?}",
421-
payjoin_proposal_psbt
431+
payjoin_proposal.psbt()
422432
);
423433
Ok(payjoin_proposal)
424434
}

0 commit comments

Comments
 (0)