Skip to content

Commit 067931f

Browse files
authored
Merge pull request #11 from benthecarman/payment-events
2 parents 797943d + 055bc95 commit 067931f

4 files changed

Lines changed: 275 additions & 52 deletions

File tree

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

Lines changed: 102 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,20 @@ use crate::{Event, EventQueue, InitFailure, Seed, WalletConfig};
77

88
use ldk_node::bitcoin::hashes::Hash;
99
use ldk_node::bitcoin::hashes::sha256::Hash as Sha256;
10+
use ldk_node::bitcoin::hex::FromHex;
1011
use ldk_node::lightning::util::logger::Logger as _;
1112
use ldk_node::lightning::util::persist::KVStore;
1213
use ldk_node::lightning::{log_error, log_info};
1314
use ldk_node::lightning_invoice::Bolt11Invoice;
14-
use ldk_node::lightning_types::payment::PaymentHash;
15+
use ldk_node::lightning_types::payment::{PaymentHash, PaymentPreimage};
1516

1617
use bitcoin_payment_instructions::PaymentMethod;
1718
use bitcoin_payment_instructions::amount::Amount;
1819

1920
use cdk::amount::SplitTarget;
20-
use cdk::nuts::CurrencyUnit;
2121
use cdk::nuts::MeltOptions;
2222
use cdk::nuts::nut23::Amountless;
23+
use cdk::nuts::{CurrencyUnit, MeltQuoteState};
2324
use cdk::wallet::MintQuote;
2425
use cdk::wallet::Wallet;
2526
use cdk::wallet::types::{Transaction, TransactionDirection};
@@ -55,6 +56,7 @@ pub struct Cashu {
5556
logger: Arc<Logger>,
5657
supports_bolt12: bool,
5758
mint_quote_sender: mpsc::Sender<MintQuote>,
59+
event_queue: Arc<EventQueue>,
5860
}
5961

6062
impl TrustedWalletInterface for Cashu {
@@ -210,8 +212,11 @@ impl TrustedWalletInterface for Cashu {
210212
amountless: Amountless { amount_msat: amount.milli_sats().into() },
211213
});
212214

215+
let mut payment_hash: Option<PaymentHash> = None;
216+
213217
let quote = match method {
214218
PaymentMethod::LightningBolt11(invoice) => {
219+
payment_hash = Some(PaymentHash(*invoice.payment_hash().as_byte_array()));
215220
// Create a melt quote
216221
self.cashu_wallet.melt_quote(invoice.to_string(), melt_options).await.map_err(
217222
|e| {
@@ -244,26 +249,104 @@ impl TrustedWalletInterface for Cashu {
244249
},
245250
};
246251

252+
// Convert quote ID to a 32-byte array for consistency
253+
// We'll use the quote ID as the payment identifier
254+
let payment_id = Self::id_to_32_byte_array(&quote.id);
255+
247256
// Execute the melt in separate thread, do not block on it being successful/failed
248257
let cashu_wallet = Arc::clone(&self.cashu_wallet);
249258
let logger = Arc::clone(&self.logger);
259+
let event_queue = Arc::clone(&self.event_queue);
250260
let quote_id = quote.id.clone();
251261
tokio::spawn(async move {
252262
// todo react with events too
253263
match cashu_wallet.melt(&quote_id).await {
254-
Ok(_) => {
255-
log_info!(logger, "Successfully sent for quote: {quote_id}");
264+
Ok(res) => {
265+
match res.state {
266+
MeltQuoteState::Paid => {
267+
log_info!(logger, "Successfully sent for quote: {quote_id}");
268+
269+
let preimage: Option<PaymentPreimage> = match &res.preimage {
270+
Some(str) => match FromHex::from_hex(str) {
271+
Ok(b) => Some(PaymentPreimage(b)),
272+
Err(e) => {
273+
log_error!(
274+
logger,
275+
"Failed to decode preimage ({:?}) for quote {quote_id}: {e}",
276+
res.preimage
277+
);
278+
None
279+
},
280+
},
281+
None => {
282+
debug_assert!(
283+
false,
284+
"Melt succeeded but no preimage for quote: {quote_id}"
285+
);
286+
log_error!(
287+
logger,
288+
"Melt succeeded but no preimage for quote: {quote_id}"
289+
);
290+
None // Placeholder, should not happen
291+
},
292+
};
293+
294+
let hash = match payment_hash {
295+
Some(hash) => hash,
296+
None => {
297+
match preimage {
298+
Some(pre) => {
299+
let hash = Sha256::hash(&pre.0);
300+
PaymentHash(hash.to_byte_array())
301+
},
302+
None => {
303+
log_error!(
304+
logger,
305+
"Melt succeeded but no payment hash or preimage for quote: {quote_id}"
306+
);
307+
PaymentHash([0u8; 32]) // Placeholder, should not happen
308+
},
309+
}
310+
},
311+
};
312+
313+
let fee_paid_sat: u64 = res.fee_paid.into();
314+
let _ = event_queue.add_event(Event::PaymentSuccessful {
315+
payment_id: PaymentId::Trusted(payment_id),
316+
payment_hash: hash,
317+
payment_preimage: preimage
318+
.unwrap_or(PaymentPreimage([0u8; 32])),
319+
fee_paid_msat: Some(fee_paid_sat * 1_000), // convert to msats
320+
});
321+
},
322+
MeltQuoteState::Failed => {
323+
log_error!(logger, "Melt failed for quote: {quote_id}");
324+
let _ = event_queue.add_event(Event::PaymentFailed {
325+
payment_id: PaymentId::Trusted(payment_id),
326+
payment_hash,
327+
reason: None,
328+
});
329+
},
330+
state => {
331+
log_error!(
332+
logger,
333+
"Melt in unknown state {state} for quote: {quote_id}"
334+
);
335+
// todo should we watch for it to complete?
336+
},
337+
}
256338
},
257339
Err(e) => {
258-
log_error!(logger, "Failed to melt quote {quote_id}: {e}",);
340+
log_error!(logger, "Failed to melt quote {quote_id}: {e}");
341+
let _ = event_queue.add_event(Event::PaymentFailed {
342+
payment_id: PaymentId::Trusted(payment_id),
343+
payment_hash,
344+
reason: None,
345+
});
259346
},
260347
}
261348
});
262349

263-
// Convert quote ID to a 32-byte array for consistency
264-
// We'll use the quote ID as the payment identifier
265-
let payment_id = Self::id_to_32_byte_array(&quote.id);
266-
267350
Ok(payment_id)
268351
})
269352
}
@@ -333,6 +416,7 @@ impl Cashu {
333416
// Start mint quote monitoring task
334417
let wallet_for_monitoring = Arc::clone(&cashu_wallet);
335418
let logger_for_monitoring = Arc::clone(&logger);
419+
let eq_for_monitoring = Arc::clone(&event_queue);
336420
tokio::spawn(async move {
337421
loop {
338422
tokio::select! {
@@ -345,7 +429,7 @@ impl Cashu {
345429

346430
// Start monitoring this quote
347431
let wallet = Arc::clone(&wallet_for_monitoring);
348-
let event_queue = Arc::clone(&event_queue);
432+
let event_queue = Arc::clone(&eq_for_monitoring);
349433
let logger = Arc::clone(&logger_for_monitoring);
350434
tokio::spawn(async move {
351435
if let Err(e) = Self::monitor_mint_quote(wallet, event_queue, &logger, mint_quote).await {
@@ -369,7 +453,14 @@ impl Cashu {
369453
}
370454
}
371455

372-
Ok(Cashu { cashu_wallet, shutdown_sender, logger, supports_bolt12, mint_quote_sender })
456+
Ok(Cashu {
457+
cashu_wallet,
458+
shutdown_sender,
459+
logger,
460+
supports_bolt12,
461+
mint_quote_sender,
462+
event_queue,
463+
})
373464
}
374465

375466
/// Convert an ID string to a 32-byte array

orange-sdk/src/trusted_wallet/dummy.rs

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,12 @@ impl DummyTrustedWallet {
8484
loop {
8585
let event = events_ref.next_event_async().await;
8686
match event {
87-
Event::PaymentSuccessful { payment_id, fee_paid_msat, .. } => {
87+
Event::PaymentSuccessful {
88+
payment_id,
89+
fee_paid_msat,
90+
payment_hash,
91+
payment_preimage,
92+
} => {
8893
// convert id
8994
let id = payment_id.unwrap().0;
9095

@@ -96,17 +101,37 @@ impl DummyTrustedWallet {
96101
payment.fee = Amount::from_milli_sats(fee).expect("valid fee")
97102
}
98103
}
104+
105+
// Send a PaymentSuccessful event
106+
event_queue
107+
.add_event(crate::Event::PaymentSuccessful {
108+
payment_id: PaymentId::Trusted(id),
109+
payment_hash,
110+
payment_preimage: payment_preimage.unwrap(), // safe
111+
fee_paid_msat,
112+
})
113+
.unwrap();
99114
},
100-
Event::PaymentFailed { payment_id, .. } => {
115+
Event::PaymentFailed { payment_id, payment_hash, reason } => {
101116
// convert id
102117
let id = payment_id.unwrap().0;
103118

104119
let mut payments = pays.write().unwrap();
105120
let item = payments.iter().cloned().enumerate().find(|(_, p)| p.id == id);
106121
if let Some((idx, payment)) = item {
122+
// remove from list and refund balance
107123
payments.remove(idx);
108-
bal.fetch_sub(payment.amount.milli_sats(), Ordering::SeqCst);
124+
bal.fetch_add(payment.amount.milli_sats(), Ordering::SeqCst);
109125
}
126+
127+
// Send a PaymentFailed event
128+
event_queue
129+
.add_event(crate::Event::PaymentFailed {
130+
payment_id: PaymentId::Trusted(id),
131+
payment_hash,
132+
reason,
133+
})
134+
.unwrap();
110135
},
111136
Event::PaymentReceived { payment_id, amount_msat, payment_hash, .. } => {
112137
// convert id

0 commit comments

Comments
 (0)