Skip to content

Commit e08c3a8

Browse files
committed
Set preimages for successful payments
1 parent cd15d56 commit e08c3a8

8 files changed

Lines changed: 134 additions & 6 deletions

File tree

orange-sdk/src/event.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,7 @@ impl Future for EventFuture {
309309
pub(crate) struct LdkEventHandler {
310310
pub(crate) event_queue: Arc<EventQueue>,
311311
pub(crate) ldk_node: Arc<ldk_node::Node>,
312+
pub(crate) tx_metadata: store::TxMetadataStore,
312313
pub(crate) payment_receipt_sender: watch::Sender<()>,
313314
pub(crate) channel_pending_sender: watch::Sender<u128>,
314315
pub(crate) logger: Arc<Logger>,
@@ -323,10 +324,17 @@ impl LdkEventHandler {
323324
payment_preimage,
324325
fee_paid_msat,
325326
} => {
327+
let preimage = payment_preimage.unwrap(); // safe
328+
let payment_id = PaymentId::SelfCustodial(payment_id.unwrap().0); // safe
329+
330+
if self.tx_metadata.set_preimage(payment_id, preimage.0).is_err() {
331+
log_error!(self.logger, "Failed to set preimage for payment {payment_id:?}");
332+
}
333+
326334
if let Err(e) = self.event_queue.add_event(Event::PaymentSuccessful {
327-
payment_id: PaymentId::SelfCustodial(payment_id.unwrap().0), // safe
335+
payment_id,
328336
payment_hash,
329-
payment_preimage: payment_preimage.unwrap(), // safe
337+
payment_preimage: preimage,
330338
fee_paid_msat,
331339
}) {
332340
log_error!(self.logger, "Failed to add PaymentSuccessful event: {e:?}");

orange-sdk/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,7 @@ impl Wallet {
564564
config,
565565
Arc::clone(&store),
566566
Arc::clone(&event_queue),
567+
tx_metadata.clone(),
567568
Arc::clone(&logger),
568569
)
569570
.await?,

orange-sdk/src/lightning_wallet.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::bitcoin::OutPoint;
22
use crate::event::{EventQueue, LdkEventHandler};
33
use crate::logging::Logger;
4-
use crate::store::TxStatus;
4+
use crate::store::{TxMetadataStore, TxStatus};
55
use crate::{ChainSource, InitFailure, PaymentType, Seed, WalletConfig};
66

77
use bitcoin_payment_instructions::PaymentMethod;
@@ -53,7 +53,7 @@ const DEFAULT_INVOICE_EXPIRY_SECS: u32 = 86_400; // 24 hours
5353
impl LightningWallet {
5454
pub(super) async fn init(
5555
runtime: Arc<Runtime>, config: WalletConfig, store: Arc<dyn KVStore + Sync + Send>,
56-
event_queue: Arc<EventQueue>, logger: Arc<Logger>,
56+
event_queue: Arc<EventQueue>, tx_metadata: TxMetadataStore, logger: Arc<Logger>,
5757
) -> Result<Self, InitFailure> {
5858
log_info!(logger, "Creating LDK node...");
5959
let anchor_channels_config = ldk_node::config::AnchorChannelsConfig {
@@ -162,6 +162,7 @@ impl LightningWallet {
162162
let ev_handler = Arc::new(LdkEventHandler {
163163
event_queue,
164164
ldk_node: Arc::clone(&ldk_node),
165+
tx_metadata,
165166
payment_receipt_sender,
166167
channel_pending_sender,
167168
logger,

orange-sdk/src/store.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,75 @@ impl TxMetadataStore {
358358
Err(())
359359
}
360360
}
361+
362+
/// Sets the preimage for an outgoing lightning payment. If the payment already has a preimage,
363+
/// this is a no-op and returns Ok(()). If the payment_id does not exist in the store, or if the payment
364+
/// is not an outgoing lightning payment, returns Err(()).
365+
pub fn set_preimage(&self, payment_id: PaymentId, preimage: [u8; 32]) -> Result<(), ()> {
366+
let mut tx_metadata = self.tx_metadata.write().unwrap();
367+
if let Some(metadata) = tx_metadata.get_mut(&payment_id) {
368+
match metadata.ty {
369+
TxType::Payment { ty } => match ty {
370+
PaymentType::OutgoingLightningBolt12 { payment_preimage } => {
371+
if payment_preimage.is_some() {
372+
Ok(())
373+
} else {
374+
metadata.ty = TxType::Payment {
375+
ty: PaymentType::OutgoingLightningBolt12 {
376+
payment_preimage: Some(PaymentPreimage(preimage)),
377+
},
378+
};
379+
380+
self.store
381+
.write(
382+
STORE_PRIMARY_KEY,
383+
STORE_SECONDARY_KEY,
384+
&payment_id.to_string(),
385+
&metadata.encode(),
386+
)
387+
.expect("We do not allow writes to fail");
388+
Ok(())
389+
}
390+
},
391+
PaymentType::OutgoingLightningBolt11 { payment_preimage } => {
392+
if payment_preimage.is_some() {
393+
Ok(())
394+
} else {
395+
metadata.ty = TxType::Payment {
396+
ty: PaymentType::OutgoingLightningBolt11 {
397+
payment_preimage: Some(PaymentPreimage(preimage)),
398+
},
399+
};
400+
401+
self.store
402+
.write(
403+
STORE_PRIMARY_KEY,
404+
STORE_SECONDARY_KEY,
405+
&payment_id.to_string(),
406+
&metadata.encode(),
407+
)
408+
.expect("We do not allow writes to fail");
409+
Ok(())
410+
}
411+
},
412+
_ => {
413+
eprintln!(
414+
"payment_id {payment_id} is not an outgoing lightning payment, cannot set preimage"
415+
);
416+
Err(())
417+
},
418+
},
419+
_ => {
420+
// if we're trying to set a preimage on a non-payment, just continue
421+
// this should only happen when we finish a rebalance payment
422+
Ok(())
423+
},
424+
}
425+
} else {
426+
eprintln!("doesn't exist in metadata store: {payment_id}");
427+
Err(())
428+
}
429+
}
361430
}
362431

363432
const REBALANCE_ENABLED_KEY: &str = "rebalance_enabled";

orange-sdk/src/trusted_wallet/cashu/mod.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -351,12 +351,22 @@ impl TrustedWalletInterface for Cashu {
351351
},
352352
};
353353

354+
let payment_preimage =
355+
preimage.unwrap_or(PaymentPreimage([0u8; 32]));
356+
357+
if tx_metadata.set_preimage(payment_id, payment_preimage.0).is_err()
358+
{
359+
log_error!(
360+
logger,
361+
"Failed to set preimage for payment {payment_id:?}"
362+
);
363+
}
364+
354365
let fee_paid_sat: u64 = res.fee_paid.into();
355366
let _ = event_queue.add_event(Event::PaymentSuccessful {
356367
payment_id,
357368
payment_hash: hash,
358-
payment_preimage: preimage
359-
.unwrap_or(PaymentPreimage([0u8; 32])),
369+
payment_preimage,
360370
fee_paid_msat: Some(fee_paid_sat * 1_000), // convert to msats
361371
});
362372
},

orange-sdk/src/trusted_wallet/dummy.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,12 @@ impl DummyTrustedWallet {
111111

112112
// Send a PaymentSuccessful event if not a rebalance
113113
if !is_rebalance {
114+
if tx_metadata
115+
.set_preimage(payment_id, payment_preimage.unwrap().0)
116+
.is_err()
117+
{
118+
println!("Failed to set preimage for payment {payment_id:?}");
119+
}
114120
event_queue
115121
.add_event(crate::Event::PaymentSuccessful {
116122
payment_id,

orange-sdk/src/trusted_wallet/spark/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,13 @@ impl SparkEventHandler {
342342
TrustedError::Other(format!("Invalid payment_hash hex: {e:?}"))
343343
})?;
344344

345+
if self.tx_metadata.set_preimage(payment_id, preimage).is_err() {
346+
log_error!(
347+
self.logger,
348+
"Failed to set preimage for payment {payment_id:?}"
349+
);
350+
}
351+
345352
self.event_queue.add_event(Event::PaymentSuccessful {
346353
payment_id,
347354
payment_hash: PaymentHash(payment_hash),

orange-sdk/tests/integration_tests.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,23 @@ fn test_pay_from_trusted() {
142142
wallet.get_balance().await.unwrap().trusted < bal.trusted
143143
})
144144
.await;
145+
146+
let txs = wallet.list_transactions().await.unwrap();
147+
assert_eq!(txs.len(), 2);
148+
let tx = txs.into_iter().find(|t| t.outbound).unwrap();
149+
150+
assert!(tx.fee.is_some(), "Trusted wallet send should have fees set");
151+
assert!(tx.outbound, "Outgoing payment should be outbound");
152+
assert_eq!(tx.status, TxStatus::Completed, "Payment should be completed");
153+
match tx.payment_type {
154+
PaymentType::OutgoingLightningBolt11 { payment_preimage } => {
155+
assert!(
156+
payment_preimage.is_some_and(|p| p.0 != [0; 32]),
157+
"Completed payment should have payment_preimage"
158+
);
159+
},
160+
pt => panic!("Payment type should be OutgoingLightningBolt11, got {pt:?}"),
161+
}
145162
})
146163
}
147164

@@ -505,6 +522,15 @@ fn run_test_pay_lightning_from_self_custody(amountless: bool) {
505522
);
506523
assert_eq!(payment.status, TxStatus::Completed, "Payment should be completed");
507524
assert_ne!(payment.time_since_epoch, Duration::ZERO, "Time should be set");
525+
match payment.payment_type {
526+
PaymentType::OutgoingLightningBolt11 { payment_preimage } => {
527+
assert!(
528+
payment_preimage.is_some_and(|p| p.0 != [0; 32]),
529+
"Completed payment should have payment_preimage"
530+
);
531+
},
532+
pt => panic!("Payment type should be OutgoingLightningBolt11, got {pt:?}"),
533+
}
508534

509535
// Validate fee is reasonable
510536
let fee_ratio = payment.fee.unwrap().milli_sats() as f64 / amount.milli_sats() as f64;

0 commit comments

Comments
 (0)