55// http://opensource.org/licenses/MIT>, at your option. You may not use this file except in
66// accordance with one or both of these licenses.
77
8+ use std:: collections:: HashMap ;
9+ use std:: ops:: Deref ;
10+ use std:: sync:: { Arc , Mutex } ;
11+
812use bitcoin:: Txid ;
913use lightning:: impl_writeable_tlv_based;
1014use lightning:: ln:: channelmanager:: PaymentId ;
15+ use lightning_types:: payment:: PaymentHash ;
1116
12- use crate :: data_store:: { StorableObject , StorableObjectUpdate } ;
17+ use crate :: data_store:: { DataStore , StorableObject , StorableObjectUpdate } ;
18+ use crate :: logger:: LdkLogger ;
1319use crate :: payment:: store:: PaymentDetailsUpdate ;
14- use crate :: payment:: { PaymentDetails , PaymentKind } ;
20+ use crate :: payment:: { PaymentDetails , PaymentDirection , PaymentKind , PaymentStatus } ;
21+ use crate :: types:: DynStore ;
22+ use crate :: Error ;
1523
1624/// One candidate transaction in an interactive-funding (splice) RBF history, holding this node's
1725/// share of the funding amount and fee for that candidate. Both are `None` for a candidate this
@@ -47,25 +55,38 @@ pub struct PendingPaymentDetails {
4755 /// RBF history, keyed by each candidate's txid. Empty for non-funding payments and for
4856 /// records written before per-candidate tracking existed.
4957 pub ( crate ) candidates : Vec < FundingTxCandidate > ,
58+ /// The timestamp after which this pending payment can be pruned.
59+ pub expires_at : Option < u64 > ,
5060}
5161
5262impl PendingPaymentDetails {
5363 pub ( crate ) fn new (
5464 details : PaymentDetails , conflicting_txids : Vec < Txid > , candidates : Vec < FundingTxCandidate > ,
5565 ) -> Self {
56- Self { details, conflicting_txids, candidates }
66+ Self { details, conflicting_txids, candidates, expires_at : None }
67+ }
68+
69+ pub ( crate ) fn new_with_expiry (
70+ details : PaymentDetails , conflicting_txids : Vec < Txid > , expires_at : Option < u64 > ,
71+ ) -> Self {
72+ Self { details, conflicting_txids, candidates : Vec :: new ( ) , expires_at }
5773 }
5874
5975 /// Returns this node's recorded funding figures for the candidate with the given txid, if any.
6076 pub ( crate ) fn candidate ( & self , txid : Txid ) -> Option < & FundingTxCandidate > {
6177 self . candidates . iter ( ) . find ( |candidate| candidate. txid == txid)
6278 }
79+
80+ pub ( crate ) fn has_expired ( & self , now : u64 ) -> bool {
81+ self . expires_at . map_or ( false , |expires_at| expires_at <= now)
82+ }
6383}
6484
6585impl_writeable_tlv_based ! ( PendingPaymentDetails , {
6686 ( 0 , details, required) ,
6787 ( 2 , conflicting_txids, optional_vec) ,
6888 ( 4 , candidates, optional_vec) ,
89+ ( 6 , expires_at, option) ,
6990} ) ;
7091
7192#[ derive( Clone , Debug , PartialEq , Eq ) ]
@@ -74,6 +95,7 @@ pub(crate) struct PendingPaymentDetailsUpdate {
7495 pub payment_update : Option < PaymentDetailsUpdate > ,
7596 pub conflicting_txids : Option < Vec < Txid > > ,
7697 pub candidates : Vec < FundingTxCandidate > ,
98+ pub expires_at : Option < Option < u64 > > ,
7799}
78100
79101impl StorableObject for PendingPaymentDetails {
@@ -112,6 +134,13 @@ impl StorableObject for PendingPaymentDetails {
112134 updated = true ;
113135 }
114136
137+ if let Some ( new_expires_at) = update. expires_at {
138+ if self . expires_at != new_expires_at {
139+ self . expires_at = new_expires_at;
140+ updated = true ;
141+ }
142+ }
143+
115144 updated
116145 }
117146
@@ -138,7 +167,151 @@ impl From<&PendingPaymentDetails> for PendingPaymentDetailsUpdate {
138167 payment_update : Some ( value. details . to_update ( ) ) ,
139168 conflicting_txids,
140169 candidates : value. candidates . clone ( ) ,
170+ expires_at : Some ( value. expires_at ) ,
171+ }
172+ }
173+ }
174+
175+ pub ( crate ) struct PendingPaymentStore < L : Deref >
176+ where
177+ L :: Target : LdkLogger ,
178+ {
179+ inner : DataStore < PendingPaymentDetails , L > ,
180+ mutation_lock : tokio:: sync:: Mutex < ( ) > ,
181+ manual_bolt11_payment_hash_index : Mutex < HashMap < PaymentHash , Vec < PaymentId > > > ,
182+ }
183+
184+ impl < L : Deref > PendingPaymentStore < L >
185+ where
186+ L :: Target : LdkLogger ,
187+ {
188+ pub ( crate ) fn new (
189+ pending_payments : Vec < PendingPaymentDetails > , primary_namespace : String ,
190+ secondary_namespace : String , kv_store : Arc < DynStore > , logger : L ,
191+ ) -> Self {
192+ // TODO: Revisit this initialization once pending payments are no longer all kept in
193+ // memory.
194+ let manual_bolt11_payment_hash_index =
195+ Mutex :: new ( Self :: build_manual_bolt11_payment_hash_index ( & pending_payments) ) ;
196+ let inner = DataStore :: new (
197+ pending_payments,
198+ primary_namespace,
199+ secondary_namespace,
200+ kv_store,
201+ logger,
202+ ) ;
203+ Self { inner, mutation_lock : tokio:: sync:: Mutex :: new ( ( ) ) , manual_bolt11_payment_hash_index }
204+ }
205+
206+ pub ( crate ) async fn insert_or_update (
207+ & self , pending_payment : PendingPaymentDetails ,
208+ ) -> Result < bool , Error > {
209+ let _guard = self . mutation_lock . lock ( ) . await ;
210+ let id = pending_payment. id ( ) ;
211+ let before = self . inner . get ( & id) ;
212+ let updated = self . inner . insert_or_update ( pending_payment) . await ?;
213+ if updated {
214+ let after = self . inner . get ( & id) ;
215+ self . replace_in_index ( before. as_ref ( ) , after. as_ref ( ) ) ;
216+ }
217+ Ok ( updated)
218+ }
219+
220+ pub ( crate ) async fn remove ( & self , id : & PaymentId ) -> Result < ( ) , Error > {
221+ let _guard = self . mutation_lock . lock ( ) . await ;
222+ let before = self . inner . get ( id) ;
223+ self . inner . remove ( id) . await ?;
224+ if let Some ( payment) = before. as_ref ( ) {
225+ self . remove_from_index ( payment) ;
141226 }
227+ Ok ( ( ) )
228+ }
229+
230+ pub ( crate ) fn get ( & self , id : & PaymentId ) -> Option < PendingPaymentDetails > {
231+ self . inner . get ( id)
232+ }
233+
234+ pub ( crate ) fn contains_key ( & self , id : & PaymentId ) -> bool {
235+ self . inner . contains_key ( id)
236+ }
237+
238+ pub ( crate ) fn list_filter < F : FnMut ( & & PendingPaymentDetails ) -> bool > (
239+ & self , f : F ,
240+ ) -> Vec < PendingPaymentDetails > {
241+ self . inner . list_filter ( f)
242+ }
243+
244+ pub ( crate ) fn get_pending_manual_bolt11_by_payment_hash (
245+ & self , payment_hash : & PaymentHash ,
246+ ) -> Option < PendingPaymentDetails > {
247+ let ids = self
248+ . manual_bolt11_payment_hash_index
249+ . lock ( )
250+ . expect ( "lock" )
251+ . get ( payment_hash)
252+ . cloned ( )
253+ . unwrap_or_default ( ) ;
254+ ids. into_iter ( ) . find_map ( |id| self . inner . get ( & id) )
255+ }
256+
257+ fn build_manual_bolt11_payment_hash_index (
258+ pending_payments : & [ PendingPaymentDetails ] ,
259+ ) -> HashMap < PaymentHash , Vec < PaymentId > > {
260+ let mut index = HashMap :: new ( ) ;
261+ for payment in pending_payments {
262+ Self :: insert_into_manual_bolt11_hash_index ( & mut index, payment) ;
263+ }
264+ index
265+ }
266+
267+ fn replace_in_index (
268+ & self , before : Option < & PendingPaymentDetails > , after : Option < & PendingPaymentDetails > ,
269+ ) {
270+ let mut index = self . manual_bolt11_payment_hash_index . lock ( ) . expect ( "lock" ) ;
271+ if let Some ( payment) = before {
272+ Self :: remove_from_manual_bolt11_hash_index ( & mut index, payment) ;
273+ }
274+ if let Some ( payment) = after {
275+ Self :: insert_into_manual_bolt11_hash_index ( & mut index, payment) ;
276+ }
277+ }
278+
279+ fn remove_from_index ( & self , payment : & PendingPaymentDetails ) {
280+ let mut index = self . manual_bolt11_payment_hash_index . lock ( ) . expect ( "lock" ) ;
281+ Self :: remove_from_manual_bolt11_hash_index ( & mut index, payment) ;
282+ }
283+
284+ fn insert_into_manual_bolt11_hash_index (
285+ index : & mut HashMap < PaymentHash , Vec < PaymentId > > , payment : & PendingPaymentDetails ,
286+ ) {
287+ if let Some ( payment_hash) = manual_bolt11_payment_hash ( & payment. details ) {
288+ index. entry ( payment_hash) . or_default ( ) . push ( payment. details . id ) ;
289+ }
290+ }
291+
292+ fn remove_from_manual_bolt11_hash_index (
293+ index : & mut HashMap < PaymentHash , Vec < PaymentId > > , payment : & PendingPaymentDetails ,
294+ ) {
295+ if let Some ( payment_hash) = manual_bolt11_payment_hash ( & payment. details ) {
296+ if let Some ( ids) = index. get_mut ( & payment_hash) {
297+ ids. retain ( |id| * id != payment. details . id ) ;
298+ if ids. is_empty ( ) {
299+ index. remove ( & payment_hash) ;
300+ }
301+ }
302+ }
303+ }
304+ }
305+
306+ fn manual_bolt11_payment_hash ( payment : & PaymentDetails ) -> Option < PaymentHash > {
307+ match payment. kind {
308+ PaymentKind :: Bolt11 { hash, preimage : None , .. }
309+ if payment. direction == PaymentDirection :: Inbound
310+ && payment. status == PaymentStatus :: Pending =>
311+ {
312+ Some ( hash)
313+ } ,
314+ _ => None ,
142315 }
143316}
144317
0 commit comments