From 256b8a7efb271829c136f6bf5a6706be2671846c Mon Sep 17 00:00:00 2001 From: benthecarman Date: Sat, 30 Sep 2023 23:56:17 -0500 Subject: [PATCH] Fix compile --- mutiny-core/src/error.rs | 12 ++----- mutiny-core/src/nodemanager.rs | 66 +++++++++++++++++++++++----------- mutiny-core/src/onchain.rs | 29 ++++++++------- mutiny-wasm/Cargo.toml | 1 + mutiny-wasm/src/error.rs | 2 +- mutiny-wasm/src/lib.rs | 7 +++- 6 files changed, 70 insertions(+), 47 deletions(-) diff --git a/mutiny-core/src/error.rs b/mutiny-core/src/error.rs index 35e3c11b4..50359ded9 100644 --- a/mutiny-core/src/error.rs +++ b/mutiny-core/src/error.rs @@ -142,7 +142,7 @@ pub enum MutinyError { PayjoinCreateRequest, /// Payjoin response validation failed. #[error("Failed to validate payjoin response.")] - PayjoinValidateResponse, + PayjoinValidateResponse(payjoin::send::ValidationError), /// Payjoin configuration error #[error("Payjoin configuration failed.")] PayjoinConfigError, @@ -394,13 +394,7 @@ impl From for MutinyError { } impl From for MutinyError { - fn from(_e: payjoin::send::ValidationError) -> Self { - Self::PayjoinValidateResponse - } -} - -impl From for MutinyError { - fn from(_e: payjoin::send::ConfigurationError) -> Self { - Self::PayjoinConfigError + fn from(e: payjoin::send::ValidationError) -> Self { + Self::PayjoinValidateResponse(e) } } diff --git a/mutiny-core/src/nodemanager.rs b/mutiny-core/src/nodemanager.rs index fbe34b4eb..d43551453 100644 --- a/mutiny-core/src/nodemanager.rs +++ b/mutiny-core/src/nodemanager.rs @@ -31,10 +31,11 @@ use crate::{gossip::*, scorer::HubPreferentialScorer}; use crate::{labels::LabelStorage, subscription::MutinySubscriptionClient}; use anyhow::anyhow; use bdk::chain::{BlockId, ConfirmationTime}; -use bdk::{wallet::AddressIndex, LocalUtxo}; +use bdk::{wallet::AddressIndex, FeeRate, LocalUtxo}; use bitcoin::blockdata::script; use bitcoin::hashes::hex::ToHex; use bitcoin::hashes::{sha256, Hash}; +use bitcoin::psbt::PartiallySignedTransaction; use bitcoin::secp256k1::{rand, PublicKey, Secp256k1, SecretKey}; use bitcoin::util::bip32::{DerivationPath, ExtendedPrivKey}; use bitcoin::{Address, Network, OutPoint, Transaction, Txid}; @@ -59,10 +60,11 @@ use lnurl::lnurl::LnUrl; use lnurl::{AsyncClient as LnUrlClient, LnUrlResponse, Response}; use nostr::key::XOnlyPublicKey; use nostr::{EventBuilder, Keys, Kind, Tag, TagKind}; -use payjoin::PjUri; +use payjoin::{PjUri, PjUriExt}; use reqwest::Client; use serde::{Deserialize, Serialize}; use serde_json::Value; +use std::io::Cursor; use std::str::FromStr; use std::sync::atomic::{AtomicBool, Ordering}; use std::{collections::HashMap, ops::Deref, sync::Arc}; @@ -1063,53 +1065,75 @@ impl NodeManager { pub async fn send_payjoin( &self, - pj_uri: PjUri, + uri: PjUri<'_>, amount: u64, labels: Vec, fee_rate: Option, ) -> Result { - let uri = payjoin::Uri::try_from(uri)? - .require_network(self.network) - .map_err(|_| MutinyError::IncorrectNetwork(self.network))? - .check_pj_supported() + let address = Address::from_str(&uri.address.to_string()) .map_err(|_| MutinyError::PayjoinConfigError)?; + let original_psbt = self.wallet.create_signed_psbt(address, amount, fee_rate)?; - let original_psbt = self - .wallet - .create_signed_psbt(uri.address, amount, Some(fee_rate))?; - - let payout_scripts = std::iter::once(uri.address.script_pubkey()).collect(); + let payout_scripts = std::iter::once(uri.address.script_pubkey()).collect::>(); let fee_rate = if let Some(rate) = fee_rate { FeeRate::from_sat_per_vb(rate) } else { let sat_per_kwu = self - .fees + .fee_estimator .get_est_sat_per_1000_weight(ConfirmationTarget::Normal); FeeRate::from_sat_per_kwu(sat_per_kwu as f32) }; let fee_rate = payjoin::bitcoin::FeeRate::from_sat_per_kwu(fee_rate.sat_per_kwu() as u64); + let original_psbt = payjoin::bitcoin::psbt::PartiallySignedTransaction::from_str( + &original_psbt.to_string(), + ) + .map_err(|_| MutinyError::PayjoinConfigError)?; let pj_params = - payjoin::send::Configuration::recommended(original_psbt, payout_scripts, fee_rate)?; - let (req, ctx) = uri.create_pj_request(psbt, pj_params)?; + payjoin::send::Configuration::recommended(&original_psbt, payout_scripts, fee_rate) + .map_err(|_| MutinyError::PayjoinConfigError)?; + + log_debug!(self.logger, "Creating payjoin request"); + let (req, ctx) = uri.create_pj_request(original_psbt.clone(), pj_params)?; let client = Client::builder() .build() .map_err(|_| MutinyError::PayjoinConfigError)?; - let mut res = client + log_debug!(self.logger, "Sending payjoin request"); + let res = client .post(req.url) .body(req.body) .header("Content-Type", "text/plain") .send() .await - .map_err(|_| MutinyError::PayjoinValidateResponseError)? - .text() + .map_err(|_| MutinyError::PayjoinCreateRequest)? + .bytes() .await - .map_err(|_| MutinyError::PayjoinValidateResponseError)?; + .map_err(|_| MutinyError::PayjoinCreateRequest)?; + + let mut cursor = Cursor::new(res.to_vec()); - let mut proposal_psbt = ctx.process_response(res)?; + log_debug!(self.logger, "Processing payjoin response"); + let proposal_psbt = ctx.process_response(&mut cursor).map_err(|e| { + log_error!(self.logger, "Error processing payjoin response: {e}"); + e + })?; - self.wallet.send_payjoin(original_psbt, proposal_psbt).await + // convert to pdk types + let original_psbt = PartiallySignedTransaction::from_str(&original_psbt.to_string()) + .map_err(|_| MutinyError::PayjoinConfigError)?; + let proposal_psbt = PartiallySignedTransaction::from_str(&proposal_psbt.to_string()) + .map_err(|_| MutinyError::PayjoinConfigError)?; + + log_debug!(self.logger, "Sending payjoin.."); + let tx = self + .wallet + .send_payjoin(original_psbt, proposal_psbt, labels) + .await?; + let txid = tx.txid(); + self.broadcast_transaction(tx).await?; + log_debug!(self.logger, "Payjoin broadcast! TXID: {txid}"); + Ok(txid) } /// Sends an on-chain transaction to the given address. diff --git a/mutiny-core/src/onchain.rs b/mutiny-core/src/onchain.rs index 0a3eeab69..a02d2653a 100644 --- a/mutiny-core/src/onchain.rs +++ b/mutiny-core/src/onchain.rs @@ -12,14 +12,13 @@ use bdk::{FeeRate, LocalUtxo, SignOptions, TransactionDetails, Wallet}; use bdk_esplora::EsploraAsyncExt; use bitcoin::consensus::serialize; use bitcoin::hashes::hex::ToHex; -use bitcoin::psbt::PartiallySignedTransaction; +use bitcoin::psbt::{Input, PartiallySignedTransaction}; use bitcoin::util::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey}; use bitcoin::{Address, Network, OutPoint, Script, Transaction, Txid}; use lightning::chain::chaininterface::{ConfirmationTarget, FeeEstimator}; use lightning::events::bump_transaction::{Utxo, WalletSource}; use lightning::util::logger::Logger; use lightning::{log_debug, log_error, log_info, log_warn}; -use payjoin::{PjUri, PjUriExt, UriExt}; use crate::error::MutinyError; use crate::fees::MutinyFeeEstimator; @@ -486,19 +485,20 @@ impl OnChainWallet { Ok(txid) } - pub fn send_payjoin( + pub async fn send_payjoin( &self, - original_psbt: PartiallySignedTransaction, - proposal_psbt: PartiallySignedTransaction, - ) -> Result { - let mut wallet = self.wallet.try_write()?; + mut original_psbt: PartiallySignedTransaction, + mut proposal_psbt: PartiallySignedTransaction, + labels: Vec, + ) -> Result { + let wallet = self.wallet.try_read()?; // add original psbt input map data in place so BDK knows which scripts to sign, // proposal_psbt only contains the sender input outpoints, not scripts, which BDK // does not look up fn input_pairs( psbt: &mut PartiallySignedTransaction, - ) -> Box + '_> { + ) -> Box + '_> { Box::new(psbt.unsigned_tx.input.iter().zip(&mut psbt.inputs)) } @@ -515,14 +515,13 @@ impl OnChainWallet { } // sign and finalize payjoin - let payjoin = wallet - .sign(&mut proposal_psbt, SignOptions::default())? - .extract_tx(); - let txid = payjoin.txid(); + wallet.sign(&mut proposal_psbt, SignOptions::default())?; + drop(wallet); - self.broadcast_transaction(payjoin.clone()).await?; - log_debug!(self.logger, "Payjoin broadcast! TXID: {txid}"); - Ok(txid) + self.label_psbt(&proposal_psbt, labels)?; + let payjoin = proposal_psbt.extract_tx(); + + Ok(payjoin) } pub fn create_sweep_psbt( diff --git a/mutiny-wasm/Cargo.toml b/mutiny-wasm/Cargo.toml index 5b650d5ef..a18fea9d8 100644 --- a/mutiny-wasm/Cargo.toml +++ b/mutiny-wasm/Cargo.toml @@ -41,6 +41,7 @@ bip39 = { version = "2.0.0" } getrandom = { version = "0.2", features = ["js"] } futures = "0.3.25" urlencoding = "2.1.2" +payjoin = { version = "0.10.0", features = ["send", "base64"] } # The `console_error_panic_hook` crate provides better debugging of panics by # logging them with `console.error`. This is great for development, but requires diff --git a/mutiny-wasm/src/error.rs b/mutiny-wasm/src/error.rs index 9ee370e2b..29fc6c747 100644 --- a/mutiny-wasm/src/error.rs +++ b/mutiny-wasm/src/error.rs @@ -200,7 +200,7 @@ impl From for MutinyJsError { MutinyError::LspAmountTooHighError => MutinyJsError::LspAmountTooHighError, MutinyError::PayjoinConfigError => MutinyJsError::PayjoinConfigError, MutinyError::PayjoinCreateRequest => MutinyJsError::PayjoinCreateRequest, - MutinyError::PayjoinValidateResponse => MutinyJsError::PayjoinValidateResponse, + MutinyError::PayjoinValidateResponse(_) => MutinyJsError::PayjoinValidateResponse, } } } diff --git a/mutiny-wasm/src/lib.rs b/mutiny-wasm/src/lib.rs index bb0ece25c..6dbc7380e 100644 --- a/mutiny-wasm/src/lib.rs +++ b/mutiny-wasm/src/lib.rs @@ -41,6 +41,7 @@ use mutiny_core::{labels::LabelStorage, nodemanager::NodeManager}; use mutiny_core::{logging::MutinyLogger, nostr::ProfileType}; use nostr::key::XOnlyPublicKey; use nostr::prelude::FromBech32; +use payjoin::UriExt; use std::str::FromStr; use std::sync::Arc; use std::{ @@ -355,7 +356,11 @@ impl MutinyWallet { fee_rate: Option, ) -> Result { // I know walia parses `pj=` and `pjos=` but payjoin::Uri parses the whole bip21 uri - let pj_uri = payjoin::Uri::try_from(payjoin_uri.as_str())?; + let pj_uri = payjoin::Uri::try_from(payjoin_uri.as_str()) + .map_err(|_| MutinyJsError::InvalidArgumentsError)? + .assume_checked() + .check_pj_supported() + .map_err(|_| MutinyJsError::InvalidArgumentsError)?; let labels: Vec = labels .into_serde() .map_err(|_| MutinyJsError::InvalidArgumentsError)?;