From 4a0fa7bad1b7dd9488f7de1d7e970b79d80040ea Mon Sep 17 00:00:00 2001 From: DanGould Date: Sat, 6 Jan 2024 14:05:47 -0500 Subject: [PATCH] Track pending payjoin psbt transactions Both sender original PSBT and payjoin proposal PSBT are tracked. --- mutiny-core/src/lib.rs | 4 +++- mutiny-core/src/nodemanager.rs | 19 ++++++++++++++++--- mutiny-core/src/onchain.rs | 21 ++++++++++++++++++--- 3 files changed, 37 insertions(+), 7 deletions(-) diff --git a/mutiny-core/src/lib.rs b/mutiny-core/src/lib.rs index 0c06e4588..984fb355e 100644 --- a/mutiny-core/src/lib.rs +++ b/mutiny-core/src/lib.rs @@ -208,6 +208,8 @@ pub enum ActivityItem { OnChain(TransactionDetails), Lightning(Box), ChannelClosed(ChannelClosure), + // /// A payjoin proposal is posted to the directory but not yet broadcast from the sender + // PendingPayjoin, } impl ActivityItem { @@ -1477,7 +1479,7 @@ impl MutinyWallet { let Ok(address) = self.node_manager.get_new_address(labels.clone()) else { return Err(MutinyError::WalletOperationFailed); }; - + log_info!(self.logger, "created new address: {address}"); let (pj, ohttp) = match self.node_manager.start_payjoin_session().await { Ok((enrolled, ohttp_keys)) => { let session = self diff --git a/mutiny-core/src/nodemanager.rs b/mutiny-core/src/nodemanager.rs index bf626b10b..80b6f5f79 100644 --- a/mutiny-core/src/nodemanager.rs +++ b/mutiny-core/src/nodemanager.rs @@ -768,7 +768,17 @@ impl NodeManager { .map_err(|_| MutinyError::IncorrectNetwork)?; let address = uri.address.clone(); let original_psbt = self.wallet.create_signed_psbt(address, amount, fee_rate)?; - // TODO ensure this creates a pending tx in the UI. Ensure locked UTXO. + // Track this transaction in the wallet so it shows as an ActivityItem in UI. + // We'll cancel it if and when this original_psbt fallback is replaced with a payjoin. + // TODO mark as a payjoin + self.wallet + .insert_tx( + original_psbt.clone().extract_tx(), + ConfirmationTime::unconfirmed(crate::utils::now().as_secs()), + None, + ) + .await?; + let fee_rate = if let Some(rate) = fee_rate { FeeRate::from_sat_per_vb(rate) } else { @@ -799,6 +809,7 @@ impl NodeManager { let proposal_psbt = match Self::poll_payjoin_sender(stop, req_ctx).await { Ok(psbt) => psbt, Err(e) => { + // self.wallet cancel_tx log_error!(logger, "Error polling payjoin sender: {e}"); return; } @@ -869,11 +880,13 @@ impl NodeManager { labels: Vec, ) -> Result { log_debug!(logger, "Sending payjoin.."); + let original_tx = original_psbt.clone().extract_tx(); let tx = wallet .send_payjoin(original_psbt, proposal_psbt, labels) .await?; let txid = tx.txid(); wallet.broadcast_transaction(tx).await?; + wallet.cancel_tx(&original_tx)?; log_info!(logger, "Payjoin broadcast! TXID: {txid}"); Ok(txid) } @@ -908,7 +921,7 @@ impl NodeManager { let (req, ohttp_ctx) = payjoin_proposal.extract_v2_req()?; let res = http_client .post(req.url) - .header("Content-Type", "message/ohttp-req") + .header("content-type", "message/ohttp-req") .body(req.body) .send() .await?; @@ -936,7 +949,7 @@ impl NodeManager { let (req, context) = session.enrolled.extract_req()?; let ohttp_response = client .post(req.url) - .header("Content-Type", "message/ohttp-req") + .header("content-type", "message/ohttp-req") .body(req.body) .send() .await?; diff --git a/mutiny-core/src/onchain.rs b/mutiny-core/src/onchain.rs index f39e3b32e..9522d244d 100644 --- a/mutiny-core/src/onchain.rs +++ b/mutiny-core/src/onchain.rs @@ -355,6 +355,12 @@ impl OnChainWallet { Ok(()) } + pub(crate) fn cancel_tx(&self, tx: &Transaction) -> Result<(), MutinyError> { + let mut wallet = self.wallet.try_write()?; + wallet.cancel_tx(tx); + Ok(()) + } + fn is_mine(&self, script: &Script) -> Result { Ok(self.wallet.try_read()?.is_mine(script)) } @@ -400,7 +406,10 @@ impl OnChainWallet { // Outputs may be substituted for e.g. batching at this stage // We're not doing this yet. - + let mut wallet = self + .wallet + .try_write() + .map_err(|_| Error::Server(MutinyError::WalletSigningFailed.into()))?; let payjoin_proposal = provisional_payjoin.finalize_proposal( |psbt| { let mut psbt = psbt.clone(); @@ -416,10 +425,16 @@ impl OnChainWallet { // TODO: check Mutiny's minfeerate is present here Some(payjoin::bitcoin::FeeRate::MIN), )?; - let payjoin_proposal_psbt = payjoin_proposal.psbt(); + let payjoin_psbt_tx = payjoin_proposal.psbt().clone().extract_tx(); + wallet + .insert_tx( + payjoin_psbt_tx, + ConfirmationTime::unconfirmed(crate::utils::now().as_secs()), + ) + .map_err(|_| Error::Server(MutinyError::WalletOperationFailed.into()))?; log::debug!( "Receiver's Payjoin proposal PSBT Rsponse: {:#?}", - payjoin_proposal_psbt + payjoin_proposal.psbt() ); Ok(payjoin_proposal) }