lib.rs 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836
  1. //! CDK Fake LN Backend
  2. //!
  3. //! Used for testing where quotes are auto filled.
  4. //!
  5. //! The fake wallet now includes a secondary repayment system that continuously repays any-amount
  6. //! invoices (amount = 0) at random intervals between 30 seconds and 3 minutes to simulate
  7. //! real-world behavior where invoices might get multiple payments. Payments continue to be
  8. //! processed until they are evicted from the queue when the queue reaches its maximum size
  9. //! (default 100 items). This is in addition to the original immediate payment processing
  10. //! which is maintained for all invoice types.
  11. #![doc = include_str!("../README.md")]
  12. #![warn(missing_docs)]
  13. #![warn(rustdoc::bare_urls)]
  14. #![deny(clippy::unwrap_used)]
  15. use std::cmp::max;
  16. use std::collections::{HashMap, HashSet, VecDeque};
  17. use std::pin::Pin;
  18. use std::sync::atomic::{AtomicBool, Ordering};
  19. use std::sync::Arc;
  20. use std::time::{Duration, Instant};
  21. use async_trait::async_trait;
  22. use bitcoin::hashes::{sha256, Hash};
  23. use bitcoin::secp256k1::{Secp256k1, SecretKey};
  24. use cdk_common::amount::{to_unit, Amount};
  25. use cdk_common::common::FeeReserve;
  26. use cdk_common::ensure_cdk;
  27. use cdk_common::nuts::{CurrencyUnit, MeltOptions, MeltQuoteState};
  28. use cdk_common::payment::{
  29. self, Bolt11Settings, CreateIncomingPaymentResponse, Event, IncomingPaymentOptions,
  30. MakePaymentResponse, MintPayment, OutgoingPaymentOptions, PaymentIdentifier,
  31. PaymentQuoteResponse, WaitPaymentResponse,
  32. };
  33. use error::Error;
  34. use futures::stream::StreamExt;
  35. use futures::Stream;
  36. use lightning::offers::offer::OfferBuilder;
  37. use lightning_invoice::{Bolt11Invoice, Currency, InvoiceBuilder, PaymentSecret};
  38. use serde::{Deserialize, Serialize};
  39. use serde_json::Value;
  40. use tokio::sync::{Mutex, RwLock};
  41. use tokio::time;
  42. use tokio_stream::wrappers::ReceiverStream;
  43. use tokio_util::sync::CancellationToken;
  44. use tracing::instrument;
  45. use uuid::Uuid;
  46. pub mod error;
  47. /// Default maximum size for the secondary repayment queue
  48. const DEFAULT_REPAY_QUEUE_MAX_SIZE: usize = 100;
  49. /// Cache duration for exchange rate (5 minutes)
  50. const RATE_CACHE_DURATION: Duration = Duration::from_secs(300);
  51. /// Mempool.space prices API response structure
  52. #[derive(Debug, Deserialize)]
  53. struct MempoolPricesResponse {
  54. #[serde(rename = "USD")]
  55. usd: f64,
  56. #[serde(rename = "EUR")]
  57. eur: f64,
  58. }
  59. /// Exchange rate cache with built-in fallback rates
  60. #[derive(Debug, Clone)]
  61. struct ExchangeRateCache {
  62. rates: Arc<Mutex<Option<(MempoolPricesResponse, Instant)>>>,
  63. }
  64. impl ExchangeRateCache {
  65. fn new() -> Self {
  66. Self {
  67. rates: Arc::new(Mutex::new(None)),
  68. }
  69. }
  70. /// Get current BTC rate for the specified currency with caching and fallback
  71. async fn get_btc_rate(&self, currency: &CurrencyUnit) -> Result<f64, Error> {
  72. // Return cached rate if still valid
  73. {
  74. let cached_rates = self.rates.lock().await;
  75. if let Some((rates, timestamp)) = &*cached_rates {
  76. if timestamp.elapsed() < RATE_CACHE_DURATION {
  77. return Self::rate_for_currency(rates, currency);
  78. }
  79. }
  80. }
  81. // Try to fetch fresh rates, fallback on error
  82. match self.fetch_fresh_rate(currency).await {
  83. Ok(rate) => Ok(rate),
  84. Err(e) => {
  85. tracing::warn!(
  86. "Failed to fetch exchange rates, using fallback for {:?}: {}",
  87. currency,
  88. e
  89. );
  90. Self::fallback_rate(currency)
  91. }
  92. }
  93. }
  94. /// Fetch fresh rate and update cache
  95. async fn fetch_fresh_rate(&self, currency: &CurrencyUnit) -> Result<f64, Error> {
  96. let url = "https://mempool.space/api/v1/prices";
  97. let response = reqwest::get(url)
  98. .await
  99. .map_err(|_| Error::UnknownInvoiceAmount)?
  100. .json::<MempoolPricesResponse>()
  101. .await
  102. .map_err(|_| Error::UnknownInvoiceAmount)?;
  103. let rate = Self::rate_for_currency(&response, currency)?;
  104. *self.rates.lock().await = Some((response, Instant::now()));
  105. Ok(rate)
  106. }
  107. fn rate_for_currency(
  108. rates: &MempoolPricesResponse,
  109. currency: &CurrencyUnit,
  110. ) -> Result<f64, Error> {
  111. match currency {
  112. CurrencyUnit::Usd => Ok(rates.usd),
  113. CurrencyUnit::Eur => Ok(rates.eur),
  114. _ => Err(Error::UnknownInvoiceAmount),
  115. }
  116. }
  117. fn fallback_rate(currency: &CurrencyUnit) -> Result<f64, Error> {
  118. match currency {
  119. CurrencyUnit::Usd => Ok(110_000.0), // $110k per BTC
  120. CurrencyUnit::Eur => Ok(95_000.0), // €95k per BTC
  121. _ => Err(Error::UnknownInvoiceAmount),
  122. }
  123. }
  124. }
  125. async fn convert_currency_amount(
  126. amount: u64,
  127. from_unit: &CurrencyUnit,
  128. target_unit: &CurrencyUnit,
  129. rate_cache: &ExchangeRateCache,
  130. ) -> Result<Amount, Error> {
  131. use CurrencyUnit::*;
  132. // Try basic unit conversion first (handles SAT/MSAT and same-unit conversions)
  133. if let Ok(converted) = to_unit(amount, from_unit, target_unit) {
  134. return Ok(converted);
  135. }
  136. // Handle fiat <-> bitcoin conversions that require exchange rates
  137. match (from_unit, target_unit) {
  138. // Fiat to Bitcoin conversions
  139. (Usd | Eur, Sat) => {
  140. let rate = rate_cache.get_btc_rate(from_unit).await?;
  141. let fiat_amount = amount as f64 / 100.0; // cents to dollars/euros
  142. Ok(Amount::from(
  143. (fiat_amount / rate * 100_000_000.0).round() as u64
  144. )) // to sats
  145. }
  146. (Usd | Eur, Msat) => {
  147. let rate = rate_cache.get_btc_rate(from_unit).await?;
  148. let fiat_amount = amount as f64 / 100.0; // cents to dollars/euros
  149. Ok(Amount::from(
  150. (fiat_amount / rate * 100_000_000_000.0).round() as u64,
  151. )) // to msats
  152. }
  153. // Bitcoin to fiat conversions
  154. (Sat, Usd | Eur) => {
  155. let rate = rate_cache.get_btc_rate(target_unit).await?;
  156. let btc_amount = amount as f64 / 100_000_000.0; // sats to BTC
  157. Ok(Amount::from((btc_amount * rate * 100.0).round() as u64)) // to cents
  158. }
  159. (Msat, Usd | Eur) => {
  160. let rate = rate_cache.get_btc_rate(target_unit).await?;
  161. let btc_amount = amount as f64 / 100_000_000_000.0; // msats to BTC
  162. Ok(Amount::from((btc_amount * rate * 100.0).round() as u64)) // to cents
  163. }
  164. _ => Err(Error::UnknownInvoiceAmount), // Unsupported conversion
  165. }
  166. }
  167. /// Secondary repayment queue manager for any-amount invoices
  168. #[derive(Debug, Clone)]
  169. struct SecondaryRepaymentQueue {
  170. queue: Arc<Mutex<VecDeque<PaymentIdentifier>>>,
  171. max_size: usize,
  172. sender: tokio::sync::mpsc::Sender<WaitPaymentResponse>,
  173. unit: CurrencyUnit,
  174. }
  175. impl SecondaryRepaymentQueue {
  176. fn new(
  177. max_size: usize,
  178. sender: tokio::sync::mpsc::Sender<WaitPaymentResponse>,
  179. unit: CurrencyUnit,
  180. ) -> Self {
  181. let queue = Arc::new(Mutex::new(VecDeque::new()));
  182. let repayment_queue = Self {
  183. queue: queue.clone(),
  184. max_size,
  185. sender,
  186. unit,
  187. };
  188. // Start the background secondary repayment processor
  189. repayment_queue.start_secondary_repayment_processor();
  190. repayment_queue
  191. }
  192. /// Add a payment to the secondary repayment queue
  193. async fn enqueue_for_repayment(&self, payment: PaymentIdentifier) {
  194. let mut queue = self.queue.lock().await;
  195. // If queue is at max capacity, remove the oldest item
  196. if queue.len() >= self.max_size {
  197. if let Some(dropped) = queue.pop_front() {
  198. tracing::debug!(
  199. "Secondary repayment queue at capacity, dropping oldest payment: {:?}",
  200. dropped
  201. );
  202. }
  203. }
  204. queue.push_back(payment);
  205. tracing::debug!(
  206. "Added payment to secondary repayment queue, current size: {}",
  207. queue.len()
  208. );
  209. }
  210. /// Start the background task that randomly processes secondary repayments from the queue
  211. fn start_secondary_repayment_processor(&self) {
  212. let queue = self.queue.clone();
  213. let sender = self.sender.clone();
  214. let unit = self.unit.clone();
  215. tokio::spawn(async move {
  216. use bitcoin::secp256k1::rand::rngs::OsRng;
  217. use bitcoin::secp256k1::rand::Rng;
  218. let mut rng = OsRng;
  219. loop {
  220. // Wait for a random interval between 30 seconds and 3 minutes (180 seconds)
  221. let delay_secs = rng.gen_range(1..=3);
  222. time::sleep(time::Duration::from_secs(delay_secs)).await;
  223. // Try to process a random payment from the queue without removing it
  224. let payment_to_process = {
  225. let q = queue.lock().await;
  226. if q.is_empty() {
  227. None
  228. } else {
  229. // Pick a random index from the queue but don't remove it
  230. let index = rng.gen_range(0..q.len());
  231. q.get(index).cloned()
  232. }
  233. };
  234. if let Some(payment) = payment_to_process {
  235. // Generate a random amount for this secondary payment (same range as initial payment: 1-1000)
  236. let random_amount: u64 = rng.gen_range(1..=1000);
  237. // Create amount based on unit, ensuring minimum of 1 sat worth
  238. let secondary_amount = match &unit {
  239. CurrencyUnit::Sat => Amount::from(random_amount),
  240. CurrencyUnit::Msat => Amount::from(u64::max(random_amount * 1000, 1000)),
  241. _ => Amount::from(u64::max(random_amount, 1)), // fallback
  242. };
  243. // Generate a unique payment identifier for this secondary payment
  244. // We'll create a new payment hash by appending a timestamp and random bytes
  245. use bitcoin::hashes::{sha256, Hash};
  246. let mut random_bytes = [0u8; 16];
  247. rng.fill(&mut random_bytes);
  248. let timestamp = std::time::SystemTime::now()
  249. .duration_since(std::time::UNIX_EPOCH)
  250. .expect("System time before UNIX_EPOCH")
  251. .as_nanos() as u64;
  252. // Create a unique hash combining the original payment identifier, timestamp, and random bytes
  253. let mut hasher_input = Vec::new();
  254. hasher_input.extend_from_slice(payment.to_string().as_bytes());
  255. hasher_input.extend_from_slice(&timestamp.to_le_bytes());
  256. hasher_input.extend_from_slice(&random_bytes);
  257. let unique_hash = sha256::Hash::hash(&hasher_input);
  258. let unique_payment_id = PaymentIdentifier::PaymentHash(*unique_hash.as_ref());
  259. tracing::info!(
  260. "Processing secondary repayment: original={:?}, new_id={:?}, amount={}",
  261. payment,
  262. unique_payment_id,
  263. secondary_amount
  264. );
  265. // Send the payment notification using the original payment identifier
  266. // The mint will process this through the normal payment stream
  267. let secondary_response = WaitPaymentResponse {
  268. payment_identifier: payment.clone(),
  269. payment_amount: secondary_amount,
  270. unit: unit.clone(),
  271. payment_id: unique_payment_id.to_string(),
  272. };
  273. if let Err(e) = sender.send(secondary_response).await {
  274. tracing::error!(
  275. "Failed to send secondary repayment notification for {:?}: {}",
  276. unique_payment_id,
  277. e
  278. );
  279. }
  280. }
  281. }
  282. });
  283. }
  284. }
  285. /// Fake Wallet
  286. #[derive(Clone)]
  287. pub struct FakeWallet {
  288. fee_reserve: FeeReserve,
  289. sender: tokio::sync::mpsc::Sender<WaitPaymentResponse>,
  290. receiver: Arc<Mutex<Option<tokio::sync::mpsc::Receiver<WaitPaymentResponse>>>>,
  291. payment_states: Arc<Mutex<HashMap<String, (MeltQuoteState, Amount)>>>,
  292. failed_payment_check: Arc<Mutex<HashSet<String>>>,
  293. payment_delay: u64,
  294. wait_invoice_cancel_token: CancellationToken,
  295. wait_invoice_is_active: Arc<AtomicBool>,
  296. incoming_payments: Arc<RwLock<HashMap<PaymentIdentifier, Vec<WaitPaymentResponse>>>>,
  297. unit: CurrencyUnit,
  298. secondary_repayment_queue: SecondaryRepaymentQueue,
  299. exchange_rate_cache: ExchangeRateCache,
  300. }
  301. impl FakeWallet {
  302. /// Create new [`FakeWallet`]
  303. pub fn new(
  304. fee_reserve: FeeReserve,
  305. payment_states: HashMap<String, (MeltQuoteState, Amount)>,
  306. fail_payment_check: HashSet<String>,
  307. payment_delay: u64,
  308. unit: CurrencyUnit,
  309. ) -> Self {
  310. Self::new_with_repay_queue_size(
  311. fee_reserve,
  312. payment_states,
  313. fail_payment_check,
  314. payment_delay,
  315. unit,
  316. DEFAULT_REPAY_QUEUE_MAX_SIZE,
  317. )
  318. }
  319. /// Create new [`FakeWallet`] with custom secondary repayment queue size
  320. pub fn new_with_repay_queue_size(
  321. fee_reserve: FeeReserve,
  322. payment_states: HashMap<String, (MeltQuoteState, Amount)>,
  323. fail_payment_check: HashSet<String>,
  324. payment_delay: u64,
  325. unit: CurrencyUnit,
  326. repay_queue_max_size: usize,
  327. ) -> Self {
  328. let (sender, receiver) = tokio::sync::mpsc::channel(8);
  329. let incoming_payments = Arc::new(RwLock::new(HashMap::new()));
  330. let secondary_repayment_queue =
  331. SecondaryRepaymentQueue::new(repay_queue_max_size, sender.clone(), unit.clone());
  332. Self {
  333. fee_reserve,
  334. sender,
  335. receiver: Arc::new(Mutex::new(Some(receiver))),
  336. payment_states: Arc::new(Mutex::new(payment_states)),
  337. failed_payment_check: Arc::new(Mutex::new(fail_payment_check)),
  338. payment_delay,
  339. wait_invoice_cancel_token: CancellationToken::new(),
  340. wait_invoice_is_active: Arc::new(AtomicBool::new(false)),
  341. incoming_payments,
  342. unit,
  343. secondary_repayment_queue,
  344. exchange_rate_cache: ExchangeRateCache::new(),
  345. }
  346. }
  347. }
  348. /// Struct for signaling what methods should respond via invoice description
  349. #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
  350. pub struct FakeInvoiceDescription {
  351. /// State to be returned from pay invoice state
  352. pub pay_invoice_state: MeltQuoteState,
  353. /// State to be returned by check payment state
  354. pub check_payment_state: MeltQuoteState,
  355. /// Should pay invoice error
  356. pub pay_err: bool,
  357. /// Should check failure
  358. pub check_err: bool,
  359. }
  360. impl Default for FakeInvoiceDescription {
  361. fn default() -> Self {
  362. Self {
  363. pay_invoice_state: MeltQuoteState::Paid,
  364. check_payment_state: MeltQuoteState::Paid,
  365. pay_err: false,
  366. check_err: false,
  367. }
  368. }
  369. }
  370. #[async_trait]
  371. impl MintPayment for FakeWallet {
  372. type Err = payment::Error;
  373. #[instrument(skip_all)]
  374. async fn get_settings(&self) -> Result<Value, Self::Err> {
  375. Ok(serde_json::to_value(Bolt11Settings {
  376. mpp: true,
  377. unit: self.unit.clone(),
  378. invoice_description: true,
  379. amountless: false,
  380. bolt12: true,
  381. })?)
  382. }
  383. #[instrument(skip_all)]
  384. fn is_wait_invoice_active(&self) -> bool {
  385. self.wait_invoice_is_active.load(Ordering::SeqCst)
  386. }
  387. #[instrument(skip_all)]
  388. fn cancel_wait_invoice(&self) {
  389. self.wait_invoice_cancel_token.cancel()
  390. }
  391. #[instrument(skip_all)]
  392. async fn wait_payment_event(
  393. &self,
  394. ) -> Result<Pin<Box<dyn Stream<Item = Event> + Send>>, Self::Err> {
  395. tracing::info!("Starting stream for fake invoices");
  396. let receiver = self.receiver.lock().await.take().ok_or(Error::NoReceiver)?;
  397. let receiver_stream = ReceiverStream::new(receiver);
  398. Ok(Box::pin(receiver_stream.map(move |wait_response| {
  399. Event::PaymentReceived(wait_response)
  400. })))
  401. }
  402. #[instrument(skip_all)]
  403. async fn get_payment_quote(
  404. &self,
  405. unit: &CurrencyUnit,
  406. options: OutgoingPaymentOptions,
  407. ) -> Result<PaymentQuoteResponse, Self::Err> {
  408. let (amount_msat, request_lookup_id) = match options {
  409. OutgoingPaymentOptions::Bolt11(bolt11_options) => {
  410. // If we have specific amount options, use those
  411. let amount_msat: u64 = if let Some(melt_options) = bolt11_options.melt_options {
  412. let msats = match melt_options {
  413. MeltOptions::Amountless { amountless } => {
  414. let amount_msat = amountless.amount_msat;
  415. if let Some(invoice_amount) =
  416. bolt11_options.bolt11.amount_milli_satoshis()
  417. {
  418. ensure_cdk!(
  419. invoice_amount == u64::from(amount_msat),
  420. Error::UnknownInvoiceAmount.into()
  421. );
  422. }
  423. amount_msat
  424. }
  425. MeltOptions::Mpp { mpp } => mpp.amount,
  426. };
  427. u64::from(msats)
  428. } else {
  429. // Fall back to invoice amount
  430. bolt11_options
  431. .bolt11
  432. .amount_milli_satoshis()
  433. .ok_or(Error::UnknownInvoiceAmount)?
  434. };
  435. let payment_id =
  436. PaymentIdentifier::PaymentHash(*bolt11_options.bolt11.payment_hash().as_ref());
  437. (amount_msat, Some(payment_id))
  438. }
  439. OutgoingPaymentOptions::Bolt12(bolt12_options) => {
  440. let offer = bolt12_options.offer;
  441. let amount_msat: u64 = if let Some(amount) = bolt12_options.melt_options {
  442. amount.amount_msat().into()
  443. } else {
  444. // Fall back to offer amount
  445. let amount = offer.amount().ok_or(Error::UnknownInvoiceAmount)?;
  446. match amount {
  447. lightning::offers::offer::Amount::Bitcoin { amount_msats } => amount_msats,
  448. _ => return Err(Error::UnknownInvoiceAmount.into()),
  449. }
  450. };
  451. (amount_msat, None)
  452. }
  453. };
  454. let amount = convert_currency_amount(
  455. amount_msat,
  456. &CurrencyUnit::Msat,
  457. unit,
  458. &self.exchange_rate_cache,
  459. )
  460. .await?;
  461. let relative_fee_reserve =
  462. (self.fee_reserve.percent_fee_reserve * u64::from(amount) as f32) as u64;
  463. let absolute_fee_reserve: u64 = self.fee_reserve.min_fee_reserve.into();
  464. let fee = max(relative_fee_reserve, absolute_fee_reserve);
  465. Ok(PaymentQuoteResponse {
  466. request_lookup_id,
  467. amount,
  468. fee: fee.into(),
  469. state: MeltQuoteState::Unpaid,
  470. unit: unit.clone(),
  471. })
  472. }
  473. #[instrument(skip_all)]
  474. async fn make_payment(
  475. &self,
  476. unit: &CurrencyUnit,
  477. options: OutgoingPaymentOptions,
  478. ) -> Result<MakePaymentResponse, Self::Err> {
  479. match options {
  480. OutgoingPaymentOptions::Bolt11(bolt11_options) => {
  481. let bolt11 = bolt11_options.bolt11;
  482. let payment_hash = bolt11.payment_hash().to_string();
  483. let description = bolt11.description().to_string();
  484. let status: Option<FakeInvoiceDescription> =
  485. serde_json::from_str(&description).ok();
  486. let mut payment_states = self.payment_states.lock().await;
  487. let payment_status = status
  488. .clone()
  489. .map(|s| s.pay_invoice_state)
  490. .unwrap_or(MeltQuoteState::Paid);
  491. let checkout_going_status = status
  492. .clone()
  493. .map(|s| s.check_payment_state)
  494. .unwrap_or(MeltQuoteState::Paid);
  495. let amount_msat: u64 = if let Some(melt_options) = bolt11_options.melt_options {
  496. melt_options.amount_msat().into()
  497. } else {
  498. // Fall back to invoice amount
  499. bolt11
  500. .amount_milli_satoshis()
  501. .ok_or(Error::UnknownInvoiceAmount)?
  502. };
  503. let amount_spent = if checkout_going_status == MeltQuoteState::Paid {
  504. amount_msat.into()
  505. } else {
  506. Amount::ZERO
  507. };
  508. payment_states.insert(payment_hash.clone(), (checkout_going_status, amount_spent));
  509. if let Some(description) = status {
  510. if description.check_err {
  511. let mut fail = self.failed_payment_check.lock().await;
  512. fail.insert(payment_hash.clone());
  513. }
  514. ensure_cdk!(!description.pay_err, Error::UnknownInvoice.into());
  515. }
  516. let total_spent = convert_currency_amount(
  517. amount_msat,
  518. &CurrencyUnit::Msat,
  519. unit,
  520. &self.exchange_rate_cache,
  521. )
  522. .await?;
  523. Ok(MakePaymentResponse {
  524. payment_proof: Some("".to_string()),
  525. payment_lookup_id: PaymentIdentifier::PaymentHash(
  526. *bolt11.payment_hash().as_ref(),
  527. ),
  528. status: payment_status,
  529. total_spent: total_spent + 1.into(),
  530. unit: unit.clone(),
  531. })
  532. }
  533. OutgoingPaymentOptions::Bolt12(bolt12_options) => {
  534. let bolt12 = bolt12_options.offer;
  535. let amount_msat: u64 = if let Some(amount) = bolt12_options.melt_options {
  536. amount.amount_msat().into()
  537. } else {
  538. // Fall back to offer amount
  539. let amount = bolt12.amount().ok_or(Error::UnknownInvoiceAmount)?;
  540. match amount {
  541. lightning::offers::offer::Amount::Bitcoin { amount_msats } => amount_msats,
  542. _ => return Err(Error::UnknownInvoiceAmount.into()),
  543. }
  544. };
  545. let total_spent = convert_currency_amount(
  546. amount_msat,
  547. &CurrencyUnit::Msat,
  548. unit,
  549. &self.exchange_rate_cache,
  550. )
  551. .await?;
  552. Ok(MakePaymentResponse {
  553. payment_proof: Some("".to_string()),
  554. payment_lookup_id: PaymentIdentifier::CustomId(Uuid::new_v4().to_string()),
  555. status: MeltQuoteState::Paid,
  556. total_spent: total_spent + 1.into(),
  557. unit: unit.clone(),
  558. })
  559. }
  560. }
  561. }
  562. #[instrument(skip_all)]
  563. async fn create_incoming_payment_request(
  564. &self,
  565. unit: &CurrencyUnit,
  566. options: IncomingPaymentOptions,
  567. ) -> Result<CreateIncomingPaymentResponse, Self::Err> {
  568. let (payment_hash, request, amount, expiry) = match options {
  569. IncomingPaymentOptions::Bolt12(bolt12_options) => {
  570. let description = bolt12_options.description.unwrap_or_default();
  571. let amount = bolt12_options.amount;
  572. let expiry = bolt12_options.unix_expiry;
  573. let secret_key = SecretKey::new(&mut bitcoin::secp256k1::rand::rngs::OsRng);
  574. let secp_ctx = Secp256k1::new();
  575. let offer_builder = OfferBuilder::new(secret_key.public_key(&secp_ctx))
  576. .description(description.clone());
  577. let offer_builder = match amount {
  578. Some(amount) => {
  579. let amount_msat = convert_currency_amount(
  580. u64::from(amount),
  581. unit,
  582. &CurrencyUnit::Msat,
  583. &self.exchange_rate_cache,
  584. )
  585. .await?;
  586. offer_builder.amount_msats(amount_msat.into())
  587. }
  588. None => offer_builder,
  589. };
  590. let offer = offer_builder.build().expect("Failed to build BOLT12 offer");
  591. (
  592. PaymentIdentifier::OfferId(offer.id().to_string()),
  593. offer.to_string(),
  594. amount.unwrap_or(Amount::ZERO),
  595. expiry,
  596. )
  597. }
  598. IncomingPaymentOptions::Bolt11(bolt11_options) => {
  599. let description = bolt11_options.description.unwrap_or_default();
  600. let amount = bolt11_options.amount;
  601. let expiry = bolt11_options.unix_expiry;
  602. let amount_msat = convert_currency_amount(
  603. u64::from(amount),
  604. unit,
  605. &CurrencyUnit::Msat,
  606. &self.exchange_rate_cache,
  607. )
  608. .await?
  609. .into();
  610. let invoice = create_fake_invoice(amount_msat, description.clone());
  611. let payment_hash = invoice.payment_hash();
  612. (
  613. PaymentIdentifier::PaymentHash(*payment_hash.as_ref()),
  614. invoice.to_string(),
  615. amount,
  616. expiry,
  617. )
  618. }
  619. };
  620. // ALL invoices get immediate payment processing (original behavior)
  621. let sender = self.sender.clone();
  622. let duration = time::Duration::from_secs(self.payment_delay);
  623. let payment_hash_clone = payment_hash.clone();
  624. let incoming_payment = self.incoming_payments.clone();
  625. let unit_clone = self.unit.clone();
  626. let final_amount = if amount == Amount::ZERO {
  627. // For any-amount invoices, generate a random amount for the initial payment
  628. use bitcoin::secp256k1::rand::rngs::OsRng;
  629. use bitcoin::secp256k1::rand::Rng;
  630. let mut rng = OsRng;
  631. let random_amount: u64 = rng.gen_range(1000..=10000);
  632. // Use the same unit as the wallet for any-amount invoices
  633. Amount::from(random_amount)
  634. } else {
  635. amount
  636. };
  637. // Schedule the immediate payment (original behavior maintained)
  638. tokio::spawn(async move {
  639. // Wait for the random delay to elapse
  640. time::sleep(duration).await;
  641. let response = WaitPaymentResponse {
  642. payment_identifier: payment_hash_clone.clone(),
  643. payment_amount: final_amount,
  644. unit: unit_clone,
  645. payment_id: payment_hash_clone.to_string(),
  646. };
  647. let mut incoming = incoming_payment.write().await;
  648. incoming
  649. .entry(payment_hash_clone.clone())
  650. .or_insert_with(Vec::new)
  651. .push(response.clone());
  652. // Send the message after waiting for the specified duration
  653. if sender.send(response.clone()).await.is_err() {
  654. tracing::error!("Failed to send label: {:?}", payment_hash_clone);
  655. }
  656. });
  657. // For any-amount invoices ONLY, also add to the secondary repayment queue
  658. if amount == Amount::ZERO {
  659. tracing::info!(
  660. "Adding any-amount invoice to secondary repayment queue: {:?}",
  661. payment_hash
  662. );
  663. self.secondary_repayment_queue
  664. .enqueue_for_repayment(payment_hash.clone())
  665. .await;
  666. }
  667. Ok(CreateIncomingPaymentResponse {
  668. request_lookup_id: payment_hash,
  669. request,
  670. expiry,
  671. })
  672. }
  673. #[instrument(skip_all)]
  674. async fn check_incoming_payment_status(
  675. &self,
  676. request_lookup_id: &PaymentIdentifier,
  677. ) -> Result<Vec<WaitPaymentResponse>, Self::Err> {
  678. Ok(self
  679. .incoming_payments
  680. .read()
  681. .await
  682. .get(request_lookup_id)
  683. .cloned()
  684. .unwrap_or(vec![]))
  685. }
  686. #[instrument(skip_all)]
  687. async fn check_outgoing_payment(
  688. &self,
  689. request_lookup_id: &PaymentIdentifier,
  690. ) -> Result<MakePaymentResponse, Self::Err> {
  691. // For fake wallet if the state is not explicitly set default to paid
  692. let states = self.payment_states.lock().await;
  693. let status = states.get(&request_lookup_id.to_string()).cloned();
  694. let (status, total_spent) = status.unwrap_or((MeltQuoteState::Unknown, Amount::default()));
  695. let fail_payments = self.failed_payment_check.lock().await;
  696. if fail_payments.contains(&request_lookup_id.to_string()) {
  697. return Err(payment::Error::InvoicePaymentPending);
  698. }
  699. Ok(MakePaymentResponse {
  700. payment_proof: Some("".to_string()),
  701. payment_lookup_id: request_lookup_id.clone(),
  702. status,
  703. total_spent,
  704. unit: CurrencyUnit::Msat,
  705. })
  706. }
  707. }
  708. /// Create fake invoice
  709. #[instrument]
  710. pub fn create_fake_invoice(amount_msat: u64, description: String) -> Bolt11Invoice {
  711. let private_key = SecretKey::from_slice(
  712. &[
  713. 0xe1, 0x26, 0xf6, 0x8f, 0x7e, 0xaf, 0xcc, 0x8b, 0x74, 0xf5, 0x4d, 0x26, 0x9f, 0xe2,
  714. 0x06, 0xbe, 0x71, 0x50, 0x00, 0xf9, 0x4d, 0xac, 0x06, 0x7d, 0x1c, 0x04, 0xa8, 0xca,
  715. 0x3b, 0x2d, 0xb7, 0x34,
  716. ][..],
  717. )
  718. .expect("Valid 32-byte secret key");
  719. use bitcoin::secp256k1::rand::rngs::OsRng;
  720. use bitcoin::secp256k1::rand::Rng;
  721. let mut rng = OsRng;
  722. let mut random_bytes = [0u8; 32];
  723. rng.fill(&mut random_bytes);
  724. let payment_hash = sha256::Hash::from_slice(&random_bytes).expect("Valid 32-byte hash input");
  725. let payment_secret = PaymentSecret([42u8; 32]);
  726. InvoiceBuilder::new(Currency::Bitcoin)
  727. .description(description)
  728. .payment_hash(payment_hash)
  729. .payment_secret(payment_secret)
  730. .amount_milli_satoshis(amount_msat)
  731. .current_timestamp()
  732. .min_final_cltv_expiry_delta(144)
  733. .build_signed(|hash| Secp256k1::new().sign_ecdsa_recoverable(hash, &private_key))
  734. .expect("Failed to build fake invoice")
  735. }