Skip to content

Commit

Permalink
Track pending payjoin psbt transactions
Browse files Browse the repository at this point in the history
Both sender original PSBT and payjoin proposal PSBT are tracked.
  • Loading branch information
DanGould committed Mar 26, 2024
1 parent e203a83 commit 4a0fa7b
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 7 deletions.
4 changes: 3 additions & 1 deletion mutiny-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ pub enum ActivityItem {
OnChain(TransactionDetails),
Lightning(Box<MutinyInvoice>),
ChannelClosed(ChannelClosure),
// /// A payjoin proposal is posted to the directory but not yet broadcast from the sender
// PendingPayjoin,
}

impl ActivityItem {
Expand Down Expand Up @@ -1477,7 +1479,7 @@ impl<S: MutinyStorage> MutinyWallet<S> {
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
Expand Down
19 changes: 16 additions & 3 deletions mutiny-core/src/nodemanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -768,7 +768,17 @@ impl<S: MutinyStorage> NodeManager<S> {
.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 {
Expand Down Expand Up @@ -799,6 +809,7 @@ impl<S: MutinyStorage> NodeManager<S> {
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;
}
Expand Down Expand Up @@ -869,11 +880,13 @@ impl<S: MutinyStorage> NodeManager<S> {
labels: Vec<String>,
) -> Result<Txid, MutinyError> {
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)
}
Expand Down Expand Up @@ -908,7 +921,7 @@ impl<S: MutinyStorage> NodeManager<S> {
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?;
Expand Down Expand Up @@ -936,7 +949,7 @@ impl<S: MutinyStorage> NodeManager<S> {
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?;
Expand Down
21 changes: 18 additions & 3 deletions mutiny-core/src/onchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,12 @@ impl<S: MutinyStorage> OnChainWallet<S> {
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<bool, MutinyError> {
Ok(self.wallet.try_read()?.is_mine(script))
}
Expand Down Expand Up @@ -400,7 +406,10 @@ impl<S: MutinyStorage> OnChainWallet<S> {

// 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();
Expand All @@ -416,10 +425,16 @@ impl<S: MutinyStorage> OnChainWallet<S> {
// 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)
}
Expand Down

0 comments on commit 4a0fa7b

Please sign in to comment.