Skip to content

Commit 306a78e

Browse files
committed
Update PaymentStore after each Wallet sync
We update the payment store whenever syncing the wallet state finished.
1 parent 9b33e52 commit 306a78e

File tree

3 files changed

+179
-13
lines changed

3 files changed

+179
-13
lines changed

src/payment/store.rs

+5
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,11 @@ impl_writeable_tlv_based_enum!(PaymentStatus,
284284
#[derive(Clone, Debug, PartialEq, Eq)]
285285
pub enum PaymentKind {
286286
/// An on-chain payment.
287+
///
288+
/// Payments of this kind will be considered pending until the respective transaction has
289+
/// reached [`ANTI_REORG_DELAY`] confirmations on-chain.
290+
///
291+
/// [`ANTI_REORG_DELAY`]: lightning::chain::channelmonitor::ANTI_REORG_DELAY
287292
Onchain {
288293
/// The transaction identifier of this payment.
289294
txid: Txid,

src/wallet/mod.rs

+86-2
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,16 @@ use persist::KVStoreWalletPersister;
1010
use crate::logger::{log_debug, log_error, log_info, log_trace, FilesystemLogger, Logger};
1111

1212
use crate::fee_estimator::{ConfirmationTarget, FeeEstimator};
13-
use crate::payment::store::PaymentStore;
13+
use crate::payment::store::{ConfirmationStatus, PaymentStore};
14+
use crate::payment::{PaymentDetails, PaymentDirection, PaymentStatus};
1415
use crate::Error;
1516

1617
use lightning::chain::chaininterface::BroadcasterInterface;
18+
use lightning::chain::channelmonitor::ANTI_REORG_DELAY;
1719
use lightning::chain::{BestBlock, Listen};
1820

1921
use lightning::events::bump_transaction::{Utxo, WalletSource};
22+
use lightning::ln::channelmanager::PaymentId;
2023
use lightning::ln::inbound_payment::ExpandedKey;
2124
use lightning::ln::msgs::{DecodeError, UnsignedGossipMessage};
2225
use lightning::ln::script::ShutdownScript;
@@ -45,6 +48,7 @@ use bitcoin::{
4548

4649
use std::ops::Deref;
4750
use std::sync::{Arc, Mutex};
51+
use std::time::{Duration, SystemTime, UNIX_EPOCH};
4852

4953
pub(crate) enum OnchainSendAmount {
5054
ExactRetainingReserve { amount_sats: u64, cur_anchor_reserve_sats: u64 },
@@ -109,6 +113,11 @@ where
109113
Error::PersistenceFailed
110114
})?;
111115

116+
self.update_payment_store(&mut *locked_wallet).map_err(|e| {
117+
log_error!(self.logger, "Failed to update payment store: {}", e);
118+
Error::PersistenceFailed
119+
})?;
120+
112121
Ok(())
113122
},
114123
Err(e) => {
@@ -133,6 +142,76 @@ where
133142
Ok(())
134143
}
135144

145+
fn update_payment_store<'a>(
146+
&self, locked_wallet: &'a mut PersistedWallet<KVStoreWalletPersister>,
147+
) -> Result<(), Error> {
148+
let latest_update_timestamp = SystemTime::now()
149+
.duration_since(UNIX_EPOCH)
150+
.unwrap_or(Duration::from_secs(0))
151+
.as_secs();
152+
153+
for wtx in locked_wallet.transactions() {
154+
let id = PaymentId(wtx.tx_node.txid.to_byte_array());
155+
let txid = wtx.tx_node.txid;
156+
let (payment_status, confirmation_status) = match wtx.chain_position {
157+
bdk_chain::ChainPosition::Confirmed { anchor, .. } => {
158+
let confirmation_height = anchor.block_id.height;
159+
let cur_height = locked_wallet.latest_checkpoint().height();
160+
let payment_status = if cur_height >= confirmation_height + ANTI_REORG_DELAY - 1
161+
{
162+
PaymentStatus::Succeeded
163+
} else {
164+
PaymentStatus::Pending
165+
};
166+
let confirmation_status = ConfirmationStatus::Confirmed {
167+
block_hash: anchor.block_id.hash,
168+
height: confirmation_height,
169+
timestamp: anchor.confirmation_time,
170+
};
171+
(payment_status, confirmation_status)
172+
},
173+
bdk_chain::ChainPosition::Unconfirmed { .. } => {
174+
(PaymentStatus::Pending, ConfirmationStatus::Unconfirmed)
175+
},
176+
};
177+
// TODO: It would be great to introduce additional variants for
178+
// `ChannelFunding` and `ChannelClosing`. For the former, we could just
179+
// take a reference to `ChannelManager` here and check against
180+
// `list_channels`. But for the latter the best approach is much less
181+
// clear: for force-closes/HTLC spends we should be good querying
182+
// `OutputSweeper::tracked_spendable_outputs`, but regular channel closes
183+
// (i.e., `SpendableOutputDescriptor::StaticOutput` variants) are directly
184+
// spent to a wallet address. The only solution I can come up with is to
185+
// create and persist a list of 'static pending outputs' that we could use
186+
// here to determine the `PaymentKind`, but that's not really satisfactory, so
187+
// we're punting on it until we can come up with a better solution.
188+
let kind = crate::payment::PaymentKind::Onchain { txid, status: confirmation_status };
189+
let (sent, received) = locked_wallet.sent_and_received(&wtx.tx_node.tx);
190+
let (direction, amount_msat) = if sent > received {
191+
let direction = PaymentDirection::Outbound;
192+
let amount_msat = Some(sent.to_sat().saturating_sub(received.to_sat()) * 1000);
193+
(direction, amount_msat)
194+
} else {
195+
let direction = PaymentDirection::Inbound;
196+
let amount_msat = Some(received.to_sat().saturating_sub(sent.to_sat()) * 1000);
197+
(direction, amount_msat)
198+
};
199+
200+
let payment = PaymentDetails {
201+
id,
202+
kind,
203+
amount_msat,
204+
direction,
205+
status: payment_status,
206+
latest_update_timestamp,
207+
};
208+
209+
self.payment_store.insert_or_update(&payment)?;
210+
}
211+
212+
Ok(())
213+
}
214+
136215
pub(crate) fn create_funding_transaction(
137216
&self, output_script: ScriptBuf, amount: Amount, confirmation_target: ConfirmationTarget,
138217
locktime: LockTime,
@@ -477,7 +556,12 @@ where
477556
}
478557

479558
match locked_wallet.apply_block(block, height) {
480-
Ok(()) => (),
559+
Ok(()) => {
560+
if let Err(e) = self.update_payment_store(&mut *locked_wallet) {
561+
log_error!(self.logger, "Failed to update payment store: {}", e);
562+
return;
563+
}
564+
},
481565
Err(e) => {
482566
log_error!(
483567
self.logger,

tests/common/mod.rs

+88-11
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,36 @@ pub(crate) fn do_channel_full_cycle<E: ElectrumApi>(
483483
assert_eq!(node_a.list_balances().spendable_onchain_balance_sats, premine_amount_sat);
484484
assert_eq!(node_b.list_balances().spendable_onchain_balance_sats, premine_amount_sat);
485485

486+
// Check we saw the node funding transactions.
487+
assert_eq!(
488+
node_a
489+
.list_payments_with_filter(|p| p.direction == PaymentDirection::Inbound
490+
&& matches!(p.kind, PaymentKind::Onchain { .. }))
491+
.len(),
492+
1
493+
);
494+
assert_eq!(
495+
node_a
496+
.list_payments_with_filter(|p| p.direction == PaymentDirection::Outbound
497+
&& matches!(p.kind, PaymentKind::Onchain { .. }))
498+
.len(),
499+
0
500+
);
501+
assert_eq!(
502+
node_b
503+
.list_payments_with_filter(|p| p.direction == PaymentDirection::Inbound
504+
&& matches!(p.kind, PaymentKind::Onchain { .. }))
505+
.len(),
506+
1
507+
);
508+
assert_eq!(
509+
node_b
510+
.list_payments_with_filter(|p| p.direction == PaymentDirection::Outbound
511+
&& matches!(p.kind, PaymentKind::Onchain { .. }))
512+
.len(),
513+
0
514+
);
515+
486516
// Check we haven't got any events yet
487517
assert_eq!(node_a.next_event(), None);
488518
assert_eq!(node_b.next_event(), None);
@@ -515,6 +545,15 @@ pub(crate) fn do_channel_full_cycle<E: ElectrumApi>(
515545
node_a.sync_wallets().unwrap();
516546
node_b.sync_wallets().unwrap();
517547

548+
// Check we now see the channel funding transaction as outbound.
549+
assert_eq!(
550+
node_a
551+
.list_payments_with_filter(|p| p.direction == PaymentDirection::Outbound
552+
&& matches!(p.kind, PaymentKind::Onchain { .. }))
553+
.len(),
554+
1
555+
);
556+
518557
let onchain_fee_buffer_sat = 5000;
519558
let node_a_anchor_reserve_sat = if expect_anchor_channel { 25_000 } else { 0 };
520559
let node_a_upper_bound_sat =
@@ -564,22 +603,26 @@ pub(crate) fn do_channel_full_cycle<E: ElectrumApi>(
564603
let payment_id = node_a.bolt11_payment().send(&invoice, None).unwrap();
565604
assert_eq!(node_a.bolt11_payment().send(&invoice, None), Err(NodeError::DuplicatePayment));
566605

567-
assert_eq!(node_a.list_payments().first().unwrap().id, payment_id);
606+
assert!(!node_a.list_payments_with_filter(|p| p.id == payment_id).is_empty());
568607

569-
let outbound_payments_a =
570-
node_a.list_payments_with_filter(|p| p.direction == PaymentDirection::Outbound);
608+
let outbound_payments_a = node_a.list_payments_with_filter(|p| {
609+
p.direction == PaymentDirection::Outbound && matches!(p.kind, PaymentKind::Bolt11 { .. })
610+
});
571611
assert_eq!(outbound_payments_a.len(), 1);
572612

573-
let inbound_payments_a =
574-
node_a.list_payments_with_filter(|p| p.direction == PaymentDirection::Inbound);
613+
let inbound_payments_a = node_a.list_payments_with_filter(|p| {
614+
p.direction == PaymentDirection::Inbound && matches!(p.kind, PaymentKind::Bolt11 { .. })
615+
});
575616
assert_eq!(inbound_payments_a.len(), 0);
576617

577-
let outbound_payments_b =
578-
node_b.list_payments_with_filter(|p| p.direction == PaymentDirection::Outbound);
618+
let outbound_payments_b = node_b.list_payments_with_filter(|p| {
619+
p.direction == PaymentDirection::Outbound && matches!(p.kind, PaymentKind::Bolt11 { .. })
620+
});
579621
assert_eq!(outbound_payments_b.len(), 0);
580622

581-
let inbound_payments_b =
582-
node_b.list_payments_with_filter(|p| p.direction == PaymentDirection::Inbound);
623+
let inbound_payments_b = node_b.list_payments_with_filter(|p| {
624+
p.direction == PaymentDirection::Inbound && matches!(p.kind, PaymentKind::Bolt11 { .. })
625+
});
583626
assert_eq!(inbound_payments_b.len(), 1);
584627

585628
expect_event!(node_a, PaymentSuccessful);
@@ -813,8 +856,26 @@ pub(crate) fn do_channel_full_cycle<E: ElectrumApi>(
813856
node_b.payment(&keysend_payment_id).unwrap().kind,
814857
PaymentKind::Spontaneous { .. }
815858
));
816-
assert_eq!(node_a.list_payments().len(), 6);
817-
assert_eq!(node_b.list_payments().len(), 7);
859+
assert_eq!(
860+
node_a.list_payments_with_filter(|p| matches!(p.kind, PaymentKind::Bolt11 { .. })).len(),
861+
5
862+
);
863+
assert_eq!(
864+
node_b.list_payments_with_filter(|p| matches!(p.kind, PaymentKind::Bolt11 { .. })).len(),
865+
6
866+
);
867+
assert_eq!(
868+
node_a
869+
.list_payments_with_filter(|p| matches!(p.kind, PaymentKind::Spontaneous { .. }))
870+
.len(),
871+
1
872+
);
873+
assert_eq!(
874+
node_b
875+
.list_payments_with_filter(|p| matches!(p.kind, PaymentKind::Spontaneous { .. }))
876+
.len(),
877+
1
878+
);
818879

819880
println!("\nB close_channel (force: {})", force_close);
820881
if force_close {
@@ -935,6 +996,22 @@ pub(crate) fn do_channel_full_cycle<E: ElectrumApi>(
935996
assert_eq!(node_a.list_balances().total_anchor_channels_reserve_sats, 0);
936997
assert_eq!(node_b.list_balances().total_anchor_channels_reserve_sats, 0);
937998

999+
// Now we should have seen the channel closing transaction on-chain.
1000+
assert_eq!(
1001+
node_a
1002+
.list_payments_with_filter(|p| p.direction == PaymentDirection::Inbound
1003+
&& matches!(p.kind, PaymentKind::Onchain { .. }))
1004+
.len(),
1005+
2
1006+
);
1007+
assert_eq!(
1008+
node_b
1009+
.list_payments_with_filter(|p| p.direction == PaymentDirection::Inbound
1010+
&& matches!(p.kind, PaymentKind::Onchain { .. }))
1011+
.len(),
1012+
2
1013+
);
1014+
9381015
// Check we handled all events
9391016
assert_eq!(node_a.next_event(), None);
9401017
assert_eq!(node_b.next_event(), None);

0 commit comments

Comments
 (0)