From c5a0f3c5d5aa2793dc01b0fab8b27ea5e5bb7888 Mon Sep 17 00:00:00 2001 From: jbesraa Date: Wed, 3 Jul 2024 13:38:44 +0300 Subject: [PATCH] f e2e working --- src/builder.rs | 4 +- src/error.rs | 6 + src/event.rs | 48 ++-- src/lib.rs | 4 +- src/payment/payjoin/handler.rs | 239 ++++++++++++++++ src/payment/payjoin/mod.rs | 113 ++++---- src/payment/payjoin/send.rs | 255 ------------------ src/wallet.rs | 1 - tests/common/mod.rs | 4 +- tests/integration_tests_payjoin.rs | 14 +- ...tion_tests_payjoin_with_channel_opening.rs | 8 +- 11 files changed, 356 insertions(+), 340 deletions(-) create mode 100644 src/payment/payjoin/handler.rs delete mode 100644 src/payment/payjoin/send.rs diff --git a/src/builder.rs b/src/builder.rs index c8151d664..0406d4206 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -12,7 +12,7 @@ use crate::liquidity::LiquiditySource; use crate::logger::{log_error, log_info, FilesystemLogger, Logger}; use crate::message_handler::NodeCustomMessageHandler; use crate::payjoin_receiver::PayjoinReceiver; -use crate::payment::payjoin::send::PayjoinSender; +use crate::payment::payjoin::handler::PayjoinHandler; use crate::payment::store::PaymentStore; use crate::peer_store::PeerStore; use crate::tx_broadcaster::TransactionBroadcaster; @@ -1022,7 +1022,7 @@ fn build_with_store_internal( let mut payjoin_sender = None; let mut payjoin_receiver = None; if let Some(pj_config) = payjoin_config { - payjoin_sender = Some(Arc::new(PayjoinSender::new( + payjoin_sender = Some(Arc::new(PayjoinHandler::new( Arc::clone(&logger), pj_config.payjoin_relay.clone(), Arc::clone(&tx_sync), diff --git a/src/error.rs b/src/error.rs index e2c5189ac..a7c4ce576 100644 --- a/src/error.rs +++ b/src/error.rs @@ -222,3 +222,9 @@ impl From for Error { Self::TxSyncFailed } } + +impl From for Error { + fn from(e: reqwest::Error) -> Self { + Self::PayjoinRequestCreationFailed + } +} diff --git a/src/event.rs b/src/event.rs index 76c83b66a..0027bd093 100644 --- a/src/event.rs +++ b/src/event.rs @@ -34,7 +34,7 @@ use lightning_liquidity::lsps2::utils::compute_opening_fee; use bitcoin::blockdata::locktime::absolute::LockTime; use bitcoin::secp256k1::PublicKey; -use bitcoin::OutPoint; +use bitcoin::{OutPoint, ScriptBuf}; use rand::{thread_rng, Rng}; @@ -144,28 +144,40 @@ pub enum Event { /// This will be `None` for events serialized by LDK Node v0.2.1 and prior. reason: Option, }, + /// Failed to send Payjoin transaction. + /// + /// This event is emitted when our attempt to send Payjoin transaction fail. + PayjoinPaymentPending { + /// Transaction ID of the successfully sent Payjoin transaction. + txid: bitcoin::Txid, + /// docs + amount: u64, + /// docs + receipient: ScriptBuf, + }, /// A Payjoin transaction has been successfully sent. /// /// This event is emitted when we send a Payjoin transaction and it was accepted by the /// receiver, and then finalised and broadcasted by us. - PayjoinTxSendSuccess { + PayjoinPaymentSuccess { /// Transaction ID of the successfully sent Payjoin transaction. txid: bitcoin::Txid, + /// docs + amount: u64, + /// docs + receipient: ScriptBuf, }, /// Failed to send Payjoin transaction. /// /// This event is emitted when our attempt to send Payjoin transaction fail. - PayjoinTxSendFailed { + PayjoinPaymentFailed { + /// docs + amount: u64, + /// docs + receipient: ScriptBuf, /// Reason for the failure. reason: String, }, - /// Failed to send Payjoin transaction. - /// - /// This event is emitted when our attempt to send Payjoin transaction fail. - PayjoinPaymentPending { - /// Transaction ID of the successfully sent Payjoin transaction. - txid: bitcoin::Txid, - }, } impl_writeable_tlv_based_enum!(Event, @@ -208,14 +220,20 @@ impl_writeable_tlv_based_enum!(Event, (4, claimable_amount_msat, required), (6, claim_deadline, option), }, - (7, PayjoinTxSendSuccess) => { + (7, PayjoinPaymentPending) => { (0, txid, required), + (2, amount, required), + (4, receipient, required), }, - (8, PayjoinTxSendFailed) => { - (0, reason, required), - }, - (9, PayjoinPaymentPending) => { + (8, PayjoinPaymentSuccess) => { (0, txid, required), + (2, amount, required), + (4, receipient, required), + }, + (9, PayjoinPaymentFailed) => { + (0, amount, required), + (2, receipient, required), + (4, reason, required), }; ); diff --git a/src/lib.rs b/src/lib.rs index 346d31865..d727fd124 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -112,7 +112,7 @@ use error::Error; pub use event::Event; use payjoin_receiver::PayjoinReceiver; -use payment::payjoin::send::PayjoinSender; +use payment::payjoin::handler::PayjoinHandler; pub use types::ChannelConfig; pub use io::utils::generate_entropy_mnemonic; @@ -192,7 +192,7 @@ pub struct Node { output_sweeper: Arc, peer_manager: Arc, connection_manager: Arc>>, - payjoin_sender: Option>, + payjoin_sender: Option>, payjoin_receiver: Option>, keys_manager: Arc, network_graph: Arc, diff --git a/src/payment/payjoin/handler.rs b/src/payment/payjoin/handler.rs new file mode 100644 index 000000000..115fb16e6 --- /dev/null +++ b/src/payment/payjoin/handler.rs @@ -0,0 +1,239 @@ +use lightning::util::logger::Logger; +use crate::config::PAYJOIN_REQUEST_TIMEOUT; +use crate::error::Error; +use crate::io::utils::ohttp_headers; +use crate::logger::FilesystemLogger; +use crate::types::{ChainSource, EventQueue, Wallet}; +use crate::Event; + +use bitcoin::address::NetworkChecked; +use bitcoin::block::Header; +use bitcoin::psbt::Psbt; +use bitcoin::{Address, Amount, BlockHash, Script, Transaction, Txid}; +use lightning::chain::channelmonitor::ANTI_REORG_DELAY; +use lightning::chain::transaction::TransactionData; +use lightning::chain::{BestBlock, Filter, WatchedOutput}; +use lightning::log_info; + +use std::sync::{Arc, RwLock}; + +#[derive(Clone, Debug)] +enum PayjoinTransaction { + PendingFirstConfirmation { + tx: Transaction, + receiver: Address, + amount: Amount, + first_broadcast_height: u32, + first_broadcast_hash: BlockHash, + }, + PendingThresholdConfirmations { + tx: Transaction, + receiver: Address, + amount: Amount, + first_broadcast_height: u32, + first_broadcast_hash: BlockHash, + first_confirmation_height: u32, + first_confirmation_hash: BlockHash, + }, +} + +impl PayjoinTransaction { + fn txid(&self) -> Option { + match self { + PayjoinTransaction::PendingFirstConfirmation { tx, .. } => Some(tx.txid()), + PayjoinTransaction::PendingThresholdConfirmations { tx, .. } => Some(tx.txid()), + } + } + fn first_confirmation_height(&self) -> Option { + match self { + PayjoinTransaction::PendingFirstConfirmation { .. } => None, + PayjoinTransaction::PendingThresholdConfirmations { + first_confirmation_height, .. + } => Some(*first_confirmation_height), + } + } + fn amount(&self) -> Amount { + match self { + PayjoinTransaction::PendingFirstConfirmation { amount, .. } => *amount, + PayjoinTransaction::PendingThresholdConfirmations { amount, .. } => *amount, + } + } + fn receiver(&self) -> Address { + match self { + PayjoinTransaction::PendingFirstConfirmation { receiver, .. } => receiver.clone(), + PayjoinTransaction::PendingThresholdConfirmations { receiver, .. } => receiver.clone(), + } + } +} + +pub(crate) struct PayjoinHandler { + logger: Arc, + payjoin_relay: payjoin::Url, + chain_source: Arc, + best_known_block: RwLock>, + transactions: RwLock>, + event_queue: Arc, + wallet: Arc, +} + +impl PayjoinHandler { + pub(crate) fn new( + logger: Arc, payjoin_relay: payjoin::Url, chain_source: Arc, + event_queue: Arc, wallet: Arc, + ) -> Self { + Self { + logger, + payjoin_relay, + transactions: RwLock::new(Vec::new()), + best_known_block: RwLock::new(None), + chain_source, + event_queue, + wallet, + } + } + + pub(crate) fn payjoin_relay(&self) -> &payjoin::Url { + &self.payjoin_relay + } + + pub(crate) async fn send_request(&self, request: &payjoin::Request) -> Result, Error> { + let response = reqwest::Client::new() + .post(request.url.clone()) + .body(request.body.clone()) + .timeout(PAYJOIN_REQUEST_TIMEOUT) + .headers(ohttp_headers()) + .send() + .await?; + let response = response.error_for_status()?; + let response = response.bytes().await?; + let response = response.to_vec(); + Ok(response) + } + + pub(crate) fn finalise_payjoin_transaction( + &self, payjoin_proposal: &mut Psbt, original_psbt: &mut Psbt, + payjoin_uri: payjoin::Uri, + ) -> Result { + let wallet = self.wallet.clone(); + wallet.sign_payjoin_proposal(payjoin_proposal, original_psbt)?; + let tx = payjoin_proposal.clone().extract_tx(); + let our_input = + tx.output.iter().find(|output| wallet.is_mine(&output.script_pubkey).unwrap_or(false)); + if let Some(our_input) = our_input { + let best_known_block = self.best_known_block.read().unwrap().clone(); + let (current_height, current_hash) = match best_known_block { + Some(b) => (b.height, b.block_hash), + None => return Err(Error::PayjoinReceiverRequestValidationFailed), // fixeror + }; + self.transactions.write().unwrap().push(PayjoinTransaction::PendingFirstConfirmation { + tx: tx.clone(), + receiver: payjoin_uri.address, + amount: payjoin_uri.amount.unwrap_or_default(), + first_broadcast_height: current_height, + first_broadcast_hash: current_hash, + }); + self.register_tx(&tx.txid(), &our_input.script_pubkey); + Ok(tx) + } else { + Err(Error::PayjoinReceiverRequestValidationFailed) // fixeror + } + } + + fn internal_transactions_confirmed( + &self, header: &Header, txdata: &TransactionData, height: u32, + ) { + let (_, tx) = txdata[0]; + let confirmed_tx_txid = tx.txid(); + let mut transactions = self.transactions.write().unwrap(); + let position = match transactions.iter().position(|o| o.txid() == Some(confirmed_tx_txid)) { + Some(position) => position, + None => { + log_info!(self.logger, "Confirmed transaction {} not found in payjoin transactions", confirmed_tx_txid); + return + }, + }; + let pj_tx = transactions.remove(position); + match pj_tx { + PayjoinTransaction::PendingFirstConfirmation { + ref tx, + receiver, + amount, + first_broadcast_height, + first_broadcast_hash, + } => { + transactions.push(PayjoinTransaction::PendingThresholdConfirmations { + tx: tx.clone(), + receiver, + amount, + first_broadcast_height, + first_broadcast_hash, + first_confirmation_height: height, + first_confirmation_hash: header.block_hash(), + }); + }, + _ => { + unreachable!() + }, + }; + } +} + +impl Filter for PayjoinHandler { + fn register_tx(&self, txid: &Txid, script_pubkey: &Script) { + self.chain_source.register_tx(txid, script_pubkey); + } + + fn register_output(&self, output: WatchedOutput) { + self.chain_source.register_output(output); + } +} + +impl lightning::chain::Confirm for PayjoinHandler { + fn transactions_confirmed(&self, header: &Header, txdata: &TransactionData, height: u32) { + self.internal_transactions_confirmed(header, txdata, height); + } + + fn transaction_unconfirmed(&self, _txid: &Txid) {} + + fn best_block_updated(&self, header: &Header, height: u32) { + *self.best_known_block.write().unwrap() = + Some(BestBlock { height, block_hash: header.block_hash() }); + let mut transactions = self.transactions.write().unwrap(); + transactions.retain(|tx| { + if let (Some(first_conf), Some(txid)) = (tx.first_confirmation_height(), tx.txid()) { + if height - first_conf >= ANTI_REORG_DELAY { + let _ = self.event_queue.add_event(Event::PayjoinPaymentSuccess { + txid, + amount: tx.amount().to_sat(), + receipient: tx.receiver().into(), + }); + false + } else { + true + } + } else { + true + } + }); + } + + fn get_relevant_txids(&self) -> Vec<(Txid, u32, Option)> { + let state_lock = self.transactions.read().unwrap(); + state_lock + .iter() + .filter_map(|o| match o { + PayjoinTransaction::PendingThresholdConfirmations { + tx, + first_confirmation_height, + first_confirmation_hash, + .. + } => Some(( + tx.clone().txid(), + first_confirmation_height.clone(), + Some(first_confirmation_hash.clone()), + )), + _ => None, + }) + .collect::>() + } +} diff --git a/src/payment/payjoin/mod.rs b/src/payment/payjoin/mod.rs index bc232475e..02fd488e8 100644 --- a/src/payment/payjoin/mod.rs +++ b/src/payment/payjoin/mod.rs @@ -3,7 +3,7 @@ use lightning::chain::chaininterface::BroadcasterInterface; use crate::config::{PAYJOIN_REQUEST_TOTAL_DURATION, PAYJOIN_RETRY_INTERVAL}; -use crate::logger::{log_error, log_info, FilesystemLogger, Logger}; +use crate::logger::{log_info, FilesystemLogger, Logger}; use crate::types::{Broadcaster, ChannelManager, EventQueue, Wallet}; use crate::Event; use bitcoin::secp256k1::PublicKey; @@ -18,9 +18,9 @@ use crate::{error::Error, Config}; use std::sync::{Arc, RwLock}; -use self::send::PayjoinSender; +pub(crate) mod handler; -pub(crate) mod send; +use handler::PayjoinHandler; /// A payment handler allowing to send Payjoin payments. /// @@ -66,7 +66,7 @@ pub(crate) mod send; /// [`BIP77`]: https://github.com/bitcoin/bips/blob/3b863a402e0250658985f08a455a6cd103e269e5/bip-0077.mediawiki pub struct PayjoinPayment { runtime: Arc>>, - sender: Option>, + sender: Option>, receiver: Option>, config: Arc, event_queue: Arc, @@ -80,7 +80,7 @@ pub struct PayjoinPayment { impl PayjoinPayment { pub(crate) fn new( - runtime: Arc>>, sender: Option>, + runtime: Arc>>, sender: Option>, receiver: Option>, config: Arc, event_queue: Arc, logger: Arc, wallet: Arc, tx_broadcaster: Arc, peer_store: Arc>>, channel_manager: Arc, @@ -130,77 +130,78 @@ impl PayjoinPayment { } let payjoin_sender = self.sender.as_ref().ok_or(Error::PayjoinUnavailable)?; let payjoin_uri = - payjoin::Uri::try_from(payjoin_uri).map_err(|_| Error::PayjoinUriInvalid)?; - let payjoin_uri = - payjoin_uri.require_network(self.config.network).map_err(|_| Error::InvalidNetwork)?; - let amount_to_send = match payjoin_uri.amount { - Some(amount) => amount, - None => return Err(Error::PayjoinRequestMissingAmount), - }; - let original_psbt = self.wallet.build_payjoin_transaction( - payjoin_uri.address.script_pubkey(), - amount_to_send.to_sat(), - )?; + payjoin::Uri::try_from(payjoin_uri).map_err(|_| Error::PayjoinUriInvalid).and_then( + |uri| uri.require_network(self.config.network).map_err(|_| Error::InvalidNetwork), + )?; + let amount_to_send = payjoin_uri.amount.ok_or(Error::PayjoinRequestMissingAmount)?.to_sat(); + let original_psbt = self + .wallet + .build_payjoin_transaction(payjoin_uri.address.script_pubkey(), amount_to_send)?; let payjoin_sender = Arc::clone(payjoin_sender); let runtime = rt_lock.as_ref().unwrap(); let event_queue = Arc::clone(&self.event_queue); let tx_broadcaster = Arc::clone(&self.tx_broadcaster); - let wallet = Arc::clone(&self.wallet); - let logger = Arc::clone(&self.logger); let payjoin_relay = payjoin_sender.payjoin_relay().clone(); runtime.spawn(async move { let mut interval = tokio::time::interval(PAYJOIN_RETRY_INTERVAL); loop { tokio::select! { _ = tokio::time::sleep(PAYJOIN_REQUEST_TOTAL_DURATION) => { - let _ = event_queue.add_event(Event::PayjoinTxSendFailed { + let _ = event_queue.add_event(Event::PayjoinPaymentFailed { + receipient: payjoin_uri.address.clone().into(), + amount: amount_to_send, reason: "Payjoin request timed out.".to_string(), }); break; } _ = interval.tick() => { let payjoin_uri = payjoin_uri.clone(); + let receiver = payjoin_uri.address.clone(); let (request, context) = - payjoin::send::RequestBuilder::from_psbt_and_uri(original_psbt.clone(), payjoin_uri) + payjoin::send::RequestBuilder::from_psbt_and_uri(original_psbt.clone(), payjoin_uri.clone()) .and_then(|b| b.build_non_incentivizing()) .and_then(|mut c| c.extract_v2(payjoin_relay.clone())) .map_err(|_e| Error::PayjoinRequestCreationFailed).unwrap(); - match payjoin_sender.send_request(&request).await { - Some(response) => { - match context.process_response(&mut response.as_slice()) { - Ok(Some(payjoin_proposal_psbt)) => { - let payjoin_proposal_psbt = &mut payjoin_proposal_psbt.clone(); - match payjoin_sender.finalise_payjoin_transaction(payjoin_proposal_psbt, &mut original_psbt.clone()) { - Ok(tx) => { - tx_broadcaster.broadcast_transactions(&[&tx]); - let txid = tx.txid(); - let _ = event_queue.add_event(Event::PayjoinPaymentPending { txid }); - } - Err(e) => { - dbg!(&e); - let _ = event_queue - .add_event(Event::PayjoinTxSendFailed { reason: "Unable to sign proposal".to_string(), }); - break; - } - } - }, - Err(e) => { - dbg!(&e); - let _ = event_queue - .add_event(Event::PayjoinTxSendFailed { reason: e.to_string() }); - log_error!(logger, "Error processing Payjoin response: {}", e); - break; - }, - Ok(None) => { - log_info!(logger, "Payjoin response received, waiting for next response."); - continue; - } + if let Ok(response) = payjoin_sender.send_request(&request).await { + match context.process_response(&mut response.as_slice()) { + Ok(Some(payjoin_proposal_psbt)) => { + let payjoin_proposal_psbt = &mut payjoin_proposal_psbt.clone(); + match payjoin_sender.finalise_payjoin_transaction(payjoin_proposal_psbt, &mut original_psbt.clone(), payjoin_uri) { + Ok(tx) => { + tx_broadcaster.broadcast_transactions(&[&tx]); + let txid = tx.txid(); + let _ = event_queue.add_event(Event::PayjoinPaymentPending { + txid, + amount: amount_to_send, + receipient: receiver.into() + }); + break; + } + Err(e) => { + let _ = event_queue + .add_event(Event::PayjoinPaymentFailed { + amount: amount_to_send, + receipient: receiver.into(), + reason: e.to_string() + }); + break; + } + } + }, + Ok(None) => { + continue; } - }, - None => { - continue; - }, - }; + Err(e) => { + let _ = event_queue + .add_event(Event::PayjoinPaymentFailed { + amount: amount_to_send, + receipient: receiver.into(), + reason: e.to_string() + }); + break; + }, + } + } } } } @@ -295,7 +296,7 @@ impl PayjoinPayment { user_channel_id, ) .await; - }); + }); let user_config = UserConfig { channel_handshake_limits: Default::default(), channel_handshake_config: ChannelHandshakeConfig { diff --git a/src/payment/payjoin/send.rs b/src/payment/payjoin/send.rs deleted file mode 100644 index 641884eff..000000000 --- a/src/payment/payjoin/send.rs +++ /dev/null @@ -1,255 +0,0 @@ -#![allow(unused_variables)] -use crate::config::{PAYJOIN_REQUEST_TIMEOUT, PAYJOIN_RETRY_INTERVAL}; -use crate::error::Error; -use crate::io::utils::ohttp_headers; -use crate::logger::FilesystemLogger; -use crate::types::{ChainSource, EventQueue, Wallet}; -use crate::Event; - -use bitcoin::block::Header; -use bitcoin::psbt::Psbt; -use bitcoin::{BlockHash, Script, Transaction, Txid}; -use lightning::chain::channelmonitor::ANTI_REORG_DELAY; -use lightning::chain::transaction::TransactionData; -use lightning::chain::{BestBlock, Filter, WatchedOutput}; -use lightning::util::logger::Logger; -use lightning::{log_error, log_info}; - -use std::sync::{Arc, Mutex}; - -#[derive(Clone, Debug)] -enum PayjoinTransaction { - PendingFirstConfirmation { - tx: Transaction, - first_broadcast_height: u32, - first_broadcast_hash: BlockHash, - }, - PendingThresholdConfirmations { - tx: Transaction, - first_broadcast_height: u32, - first_broadcast_hash: BlockHash, - latest_confirmation_height: u32, - latest_confirmation_hash: BlockHash, - }, -} - -impl PayjoinTransaction { - fn txid(&self) -> Option { - match self { - PayjoinTransaction::PendingFirstConfirmation { tx, .. } => Some(tx.txid()), - PayjoinTransaction::PendingThresholdConfirmations { tx, .. } => Some(tx.txid()), - } - } -} - -pub(crate) struct PayjoinSender { - logger: Arc, - payjoin_relay: payjoin::Url, - chain_source: Arc, - best_known_block: Mutex>, - transactions: Mutex>, - event_queue: Arc, - wallet: Arc, -} - -impl PayjoinSender { - pub(crate) fn new( - logger: Arc, payjoin_relay: payjoin::Url, chain_source: Arc, - event_queue: Arc, wallet: Arc, - ) -> Self { - Self { - logger, - payjoin_relay, - transactions: Mutex::new(Vec::new()), - best_known_block: Mutex::new(None), - chain_source, - event_queue, - wallet, - } - } - - pub(crate) fn payjoin_relay(&self) -> &payjoin::Url { - &self.payjoin_relay - } - - pub(crate) async fn send_request(&self, request: &payjoin::Request) -> Option> { - let response = match reqwest::Client::new() - .post(request.url.clone()) - .body(request.body.clone()) - .timeout(PAYJOIN_REQUEST_TIMEOUT) - .headers(ohttp_headers()) - .send() - .await - { - Ok(response) => response, - Err(e) => { - log_error!( - self.logger, - "Error trying to poll Payjoin response: {}, retrying in {} seconds", - e, - PAYJOIN_RETRY_INTERVAL.as_secs() - ); - return None; - }, - }; - if response.status() == reqwest::StatusCode::OK { - match response.bytes().await.and_then(|r| Ok(r.to_vec())) { - Ok(response) => { - if response.is_empty() { - log_info!( - self.logger, - "Got empty response while polling Payjoin response, retrying in {} seconds", PAYJOIN_RETRY_INTERVAL.as_secs() - ); - return None; - } - return Some(response); - }, - Err(e) => { - log_error!( - self.logger, - "Error reading polling Payjoin response: {}, retrying in {} seconds", - e, - PAYJOIN_RETRY_INTERVAL.as_secs() - ); - return None; - }, - }; - } else { - log_info!( - self.logger, - "Got status code {} while polling Payjoin response, retrying in {} seconds", - response.status(), - PAYJOIN_RETRY_INTERVAL.as_secs() - ); - return None; - } - } - - fn internal_transactions_confirmed( - &self, header: &Header, txdata: &TransactionData, height: u32, - ) { - let (index, tx) = txdata[0]; - let txid = tx.txid(); - let position = self.transactions.lock().unwrap().iter().position(|o| o.txid() == Some(txid)).unwrap(); - let pj_tx = self.transactions.lock().unwrap().remove(position); - dbg!("found confirmed", &pj_tx); - let pj_tx = match pj_tx { - PayjoinTransaction::PendingFirstConfirmation { - ref tx, - first_broadcast_height, - first_broadcast_hash, - } => { - dbg!("Here in peding first confirmation"); - self.transactions.lock().unwrap().push(PayjoinTransaction::PendingThresholdConfirmations { - tx: tx.clone(), - first_broadcast_height, - first_broadcast_hash, - latest_confirmation_height: height, - latest_confirmation_hash: header.block_hash(), - })}, - PayjoinTransaction::PendingThresholdConfirmations { - ref tx, - first_broadcast_height, - first_broadcast_hash, - latest_confirmation_height, - latest_confirmation_hash, - } => { - dbg!("Transaction confirmed here"); - dbg!("height: {}", height); - dbg!("first_broadcast_height: {}", first_broadcast_height); - if height - first_broadcast_height >= ANTI_REORG_DELAY { - let _ = self.event_queue.add_event(Event::PayjoinTxSendSuccess { txid }); - } else { - self.transactions.lock().unwrap().push(PayjoinTransaction::PendingThresholdConfirmations { - tx: tx.clone(), - first_broadcast_height, - first_broadcast_hash, - latest_confirmation_height: height, - latest_confirmation_hash: header.block_hash(), - }); - } - }, - }; - // dbg!("here blocked"); - // *self.transactions.lock().unwrap() = transactions.clone(); - // dbg!("here2 unblocked"); - } - - pub(crate) fn finalise_payjoin_transaction( - &self, payjoin_proposal: &mut Psbt, original_psbt: &mut Psbt, - ) -> Result { - let wallet = self.wallet.clone(); - wallet.sign_payjoin_proposal(payjoin_proposal, original_psbt)?; - let tx = payjoin_proposal.clone().extract_tx(); - let our_input = tx.output.iter().find(|output| { - wallet.is_mine(&output.script_pubkey).unwrap_or(false) - }); - if let Some(our_input) = our_input { - let best_known_block = self.best_known_block.lock().unwrap(); - dbg!(&best_known_block); - let best_known_block = best_known_block.as_ref(); - dbg!(&best_known_block); - let (current_height, current_hash) = match best_known_block { - Some(b) => (b.height, b.block_hash), - None => return Err(Error::PayjoinReceiverRequestValidationFailed), // fixeror - }; - self.transactions.lock().unwrap().push( - PayjoinTransaction::PendingFirstConfirmation { - tx: tx.clone(), - first_broadcast_height: current_height, - first_broadcast_hash: current_hash, - } - ); - self.register_tx(&tx.txid(), &our_input.script_pubkey); - Ok(tx) - } else { - Err(Error::PayjoinReceiverRequestValidationFailed) // fixeror - } - } -} - -impl Filter for PayjoinSender { - fn register_tx(&self, txid: &Txid, script_pubkey: &Script) { - dbg!("Registering transaction {:?}", txid); - self.chain_source.register_tx(txid, script_pubkey); - } - - fn register_output(&self, output: WatchedOutput) { - self.chain_source.register_output(output); - } -} - -impl lightning::chain::Confirm for PayjoinSender { - fn transactions_confirmed(&self, header: &Header, txdata: &TransactionData, height: u32) { - dbg!("Confirmed transaction"); - self.internal_transactions_confirmed(header, txdata, height); - } - fn transaction_unconfirmed(&self, txid: &Txid) { - dbg!("Unconfirmed transaction {:?}", txid); - } - fn best_block_updated(&self, header: &Header, height: u32) { - dbg!("Best block updated {:?}", height); - *self.best_known_block.lock().unwrap() = - Some(BestBlock { height, block_hash: header.block_hash() }); - } - fn get_relevant_txids(&self) -> Vec<(Txid, u32, Option)> { - dbg!("Getting relevant txids"); - let state_lock = self.transactions.lock().unwrap(); - state_lock - .iter() - .filter_map(|o| match o { - PayjoinTransaction::PendingThresholdConfirmations { - tx, - latest_confirmation_height, - latest_confirmation_hash, - .. - } => Some(( - tx.clone().txid(), - latest_confirmation_height.clone(), - Some(latest_confirmation_hash.clone()), - )), - _ => None, - }) - .collect::>() - } -} diff --git a/src/wallet.rs b/src/wallet.rs index 1eb1c3190..d1fcd05a1 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -218,7 +218,6 @@ where let wallet = self.inner.lock().unwrap(); let is_signed = wallet.sign(payjoin_proposal_psbt, SignOptions::default())?; if !is_signed { - dbg!("Failed to sign payjoin proposal"); log_error!(self.logger, "Failed to sign payjoin proposal"); return Err(Error::WalletOperationFailed); } diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 84faedb22..ecf5d16e7 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -150,7 +150,7 @@ pub(crate) use expect_payment_successful_event; macro_rules! expect_payjoin_payment_pending_event { ($node: expr) => {{ match $node.wait_next_event() { - ref e @ Event::PayjoinPaymentPending { txid } => { + ref e @ Event::PayjoinPaymentPending { txid, .. } => { println!("{} got event {:?}", $node.node_id(), e); $node.event_handled(); txid @@ -167,7 +167,7 @@ pub(crate) use expect_payjoin_payment_pending_event; macro_rules! expect_payjoin_payment_success_event { ($node: expr) => {{ match $node.wait_next_event() { - ref e @ Event::PayjoinTxSendSuccess { txid } => { + ref e @ Event::PayjoinPaymentSuccess { txid, .. } => { println!("{} got event {:?}", $node.node_id(), e); $node.event_handled(); txid diff --git a/tests/integration_tests_payjoin.rs b/tests/integration_tests_payjoin.rs index bb14829a6..da7fa7b41 100644 --- a/tests/integration_tests_payjoin.rs +++ b/tests/integration_tests_payjoin.rs @@ -38,15 +38,17 @@ fn send_receive_regular_payjoin_transaction() { wait_for_tx(&electrsd.client, txid); generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 1); node_b_pj_sender.sync_wallets().unwrap(); - generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 5); + generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 1); node_b_pj_sender.sync_wallets().unwrap(); - // expect_payjoin_payment_success_event!(node_b_pj_sender); - let node_b_balance = node_b_pj_sender.list_balances(); - assert!(node_b_balance.total_onchain_balance_sats < premine_amount_sat - 80000); - assert!(false); + generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 1); + node_b_pj_sender.sync_wallets().unwrap(); + generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 4); + node_b_pj_sender.sync_wallets().unwrap(); + expect_payjoin_payment_success_event!(node_b_pj_sender); + let node_b_balance = node_b_pj_sender.list_balances().total_onchain_balance_sats; + assert!(node_b_balance < premine_amount_sat - 80000); } -#[ignore] #[test] fn send_payjoin_with_amount() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); diff --git a/tests/integration_tests_payjoin_with_channel_opening.rs b/tests/integration_tests_payjoin_with_channel_opening.rs index 99f6e3e9f..c04bab2bb 100644 --- a/tests/integration_tests_payjoin_with_channel_opening.rs +++ b/tests/integration_tests_payjoin_with_channel_opening.rs @@ -9,7 +9,8 @@ use common::{ use bitcoin::Amount; use ldk_node::Event; -#[ignore] +use crate::common::expect_payjoin_payment_success_event; + #[test] fn send_receive_payjoin_transaction_with_channel_opening() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); @@ -48,6 +49,10 @@ fn send_receive_payjoin_transaction_with_channel_opening() { expect_channel_pending_event!(node_b_pj_sender, node_a_pj_receiver.node_id()); let txid = expect_payjoin_payment_pending_event!(node_b_pj_sender); wait_for_tx(&electrsd.client, txid); + generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 1); + node_a_pj_receiver.sync_wallets().unwrap(); + node_b_pj_sender.sync_wallets().unwrap(); + generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6); node_a_pj_receiver.sync_wallets().unwrap(); node_b_pj_sender.sync_wallets().unwrap(); @@ -56,6 +61,7 @@ fn send_receive_payjoin_transaction_with_channel_opening() { expect_channel_ready_event!(node_a_pj_receiver, node_b_pj_sender.node_id()); expect_channel_ready_event!(node_b_pj_sender, node_a_pj_receiver.node_id()); + expect_payjoin_payment_success_event!(node_b_pj_sender); let channels = node_a_pj_receiver.list_channels(); let channel = channels.get(0).unwrap(); assert_eq!(channel.channel_value_sats, 80_000);