Skip to content

Commit 8cfe125

Browse files
committed
Implement Confirm trait to PaymentHandler
The `Confirm` trait is implemented in order to track the Payjoin transaction(s). We track two different transaction: 1. Original PSBT, which is the initial transaction sent to the Payjoin receiver. The receiver can decide to broadcast this transaction instead of finishing the Payjoin flow. Those we track it. 2. Final Payjoin transaction. The transaction constructed after completing the Payjoin flow, validated and broadcasted by the Payjoin sender.
1 parent 588fbdb commit 8cfe125

File tree

1 file changed

+99
-2
lines changed

1 file changed

+99
-2
lines changed

src/payment/payjoin/handler.rs

Lines changed: 99 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use bitcoin::address::NetworkChecked;
2+
use bitcoin::block::Header;
23
use bitcoin::psbt::Psbt;
3-
use bitcoin::{Script, Transaction, Txid};
4+
use bitcoin::{BlockHash, Script, Transaction, Txid};
5+
use lightning::chain::transaction::TransactionData;
46

57
use crate::config::PAYJOIN_REQUEST_TIMEOUT;
68
use crate::error::Error;
@@ -13,7 +15,7 @@ use crate::types::{ChainSource, EventQueue, PaymentStore, Wallet};
1315
use crate::Event;
1416
use crate::PaymentDetails;
1517

16-
use lightning::chain::Filter;
18+
use lightning::chain::{BestBlock, Confirm, Filter};
1719
use lightning::ln::channelmanager::PaymentId;
1820
use lightning::log_error;
1921
use lightning::util::logger::Logger;
@@ -165,4 +167,99 @@ impl PayjoinHandler {
165167
Err(Error::PayjoinRequestSendingFailed)
166168
}
167169
}
170+
171+
fn internal_transactions_confirmed(
172+
&self, header: &Header, txdata: &TransactionData, height: u32,
173+
) {
174+
for (_, tx) in txdata {
175+
let confirmed_tx_txid = tx.txid();
176+
let payment_store = self.payment_store.clone();
177+
let payment_id = self.payment_id(&confirmed_tx_txid);
178+
let payjoin_tx_filter = |payment_details: &&PaymentDetails| {
179+
payment_details.txid == Some(confirmed_tx_txid)
180+
&& payment_details.amount_msat.is_some()
181+
};
182+
let payjoin_tx_details = payment_store.list_filter(payjoin_tx_filter);
183+
if let Some(payjoin_tx_details) = payjoin_tx_details.get(0) {
184+
let mut payment_update = PaymentDetailsUpdate::new(payjoin_tx_details.id);
185+
payment_update.status = Some(PaymentStatus::Succeeded);
186+
payment_update.best_block = Some(BestBlock::new(header.block_hash(), height));
187+
let _ = payment_store.update(&payment_update);
188+
let _ = self.event_queue.add_event(Event::PayjoinPaymentSuccessful {
189+
txid: confirmed_tx_txid,
190+
amount_sats: payjoin_tx_details
191+
.amount_msat
192+
.expect("Unreachable, asserted in `payjoin_tx_filter`"),
193+
is_original_psbt_modified: if payment_id == payjoin_tx_details.id {
194+
false
195+
} else {
196+
true
197+
},
198+
});
199+
// check if this is the original psbt transaction
200+
} else if let Some(payment_details) = payment_store.get(&payment_id) {
201+
let mut payment_update = PaymentDetailsUpdate::new(payment_id);
202+
payment_update.status = Some(PaymentStatus::Succeeded);
203+
let _ = payment_store.update(&payment_update);
204+
payment_update.best_block = Some(BestBlock::new(header.block_hash(), height));
205+
payment_update.txid = Some(confirmed_tx_txid);
206+
let _ = self.event_queue.add_event(Event::PayjoinPaymentSuccessful {
207+
txid: confirmed_tx_txid,
208+
amount_sats: payment_details
209+
.amount_msat
210+
.expect("Unreachable, payjoin transactions must have amount"),
211+
is_original_psbt_modified: false,
212+
});
213+
}
214+
}
215+
}
216+
217+
fn internal_get_relevant_txids(&self) -> Vec<(Txid, u32, Option<BlockHash>)> {
218+
let payjoin_tx_filter = |payment_details: &&PaymentDetails| {
219+
payment_details.txid.is_some()
220+
&& payment_details.status == PaymentStatus::Succeeded
221+
&& payment_details.kind == PaymentKind::Payjoin
222+
};
223+
let payjoin_tx_details = self.payment_store.list_filter(payjoin_tx_filter);
224+
let mut ret = Vec::new();
225+
for payjoin_tx_details in payjoin_tx_details {
226+
if let (Some(txid), Some(best_block)) =
227+
(payjoin_tx_details.txid, payjoin_tx_details.best_block)
228+
{
229+
ret.push((txid, best_block.height, Some(best_block.block_hash)));
230+
}
231+
}
232+
ret
233+
}
234+
235+
fn internal_best_block_updated(&self, height: u32, block_hash: BlockHash) {
236+
let payment_store = self.payment_store.clone();
237+
let payjoin_tx_filter = |payment_details: &&PaymentDetails| {
238+
payment_details.kind == PaymentKind::Payjoin
239+
&& payment_details.status == PaymentStatus::Succeeded
240+
};
241+
let payjoin_tx_details = payment_store.list_filter(payjoin_tx_filter);
242+
for payjoin_tx_details in payjoin_tx_details {
243+
let mut payment_update = PaymentDetailsUpdate::new(payjoin_tx_details.id);
244+
payment_update.best_block = Some(BestBlock::new(block_hash, height));
245+
let _ = payment_store.update(&payment_update);
246+
}
247+
}
248+
}
249+
250+
impl Confirm for PayjoinHandler {
251+
fn transactions_confirmed(&self, header: &Header, txdata: &TransactionData, height: u32) {
252+
self.internal_transactions_confirmed(header, txdata, height);
253+
}
254+
255+
fn get_relevant_txids(&self) -> Vec<(Txid, u32, Option<BlockHash>)> {
256+
self.internal_get_relevant_txids()
257+
}
258+
259+
fn best_block_updated(&self, header: &Header, height: u32) {
260+
let block_hash = header.block_hash();
261+
self.internal_best_block_updated(height, block_hash);
262+
}
263+
264+
fn transaction_unconfirmed(&self, _txid: &Txid) {}
168265
}

0 commit comments

Comments
 (0)