lib.rs 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835
  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. use std::cmp::max;
  15. use std::collections::{HashMap, HashSet, VecDeque};
  16. use std::pin::Pin;
  17. use std::sync::atomic::{AtomicBool, Ordering};
  18. use std::sync::Arc;
  19. use std::time::{Duration, Instant};
  20. use async_trait::async_trait;
  21. use bitcoin::hashes::{sha256, Hash};
  22. use bitcoin::secp256k1::{Secp256k1, SecretKey};
  23. use cdk_common::amount::{to_unit, Amount};
  24. use cdk_common::common::FeeReserve;
  25. use cdk_common::ensure_cdk;
  26. use cdk_common::nuts::{CurrencyUnit, MeltOptions, MeltQuoteState};
  27. use cdk_common::payment::{
  28. self, Bolt11Settings, CreateIncomingPaymentResponse, Event, IncomingPaymentOptions,
  29. MakePaymentResponse, MintPayment, OutgoingPaymentOptions, PaymentIdentifier,
  30. PaymentQuoteResponse, WaitPaymentResponse,
  31. };
  32. use error::Error;
  33. use futures::stream::StreamExt;
  34. use futures::Stream;
  35. use lightning::offers::offer::OfferBuilder;
  36. use lightning_invoice::{Bolt11Invoice, Currency, InvoiceBuilder, PaymentSecret};
  37. use serde::{Deserialize, Serialize};
  38. use serde_json::Value;
  39. use tokio::sync::{Mutex, RwLock};
  40. use tokio::time;
  41. use tokio_stream::wrappers::ReceiverStream;
  42. use tokio_util::sync::CancellationToken;
  43. use tracing::instrument;
  44. use uuid::Uuid;
  45. pub mod error;
  46. /// Default maximum size for the secondary repayment queue
  47. const DEFAULT_REPAY_QUEUE_MAX_SIZE: usize = 100;
  48. /// Cache duration for exchange rate (5 minutes)
  49. const RATE_CACHE_DURATION: Duration = Duration::from_secs(300);
  50. /// Mempool.space prices API response structure
  51. #[derive(Debug, Deserialize)]
  52. struct MempoolPricesResponse {
  53. #[serde(rename = "USD")]
  54. usd: f64,
  55. #[serde(rename = "EUR")]
  56. eur: f64,
  57. }
  58. /// Exchange rate cache with built-in fallback rates
  59. #[derive(Debug, Clone)]
  60. struct ExchangeRateCache {
  61. rates: Arc<Mutex<Option<(MempoolPricesResponse, Instant)>>>,
  62. }
  63. impl ExchangeRateCache {
  64. fn new() -> Self {
  65. Self {
  66. rates: Arc::new(Mutex::new(None)),
  67. }
  68. }
  69. /// Get current BTC rate for the specified currency with caching and fallback
  70. async fn get_btc_rate(&self, currency: &CurrencyUnit) -> Result<f64, Error> {
  71. // Return cached rate if still valid
  72. {
  73. let cached_rates = self.rates.lock().await;
  74. if let Some((rates, timestamp)) = &*cached_rates {
  75. if timestamp.elapsed() < RATE_CACHE_DURATION {
  76. return Self::rate_for_currency(rates, currency);
  77. }
  78. }
  79. }
  80. // Try to fetch fresh rates, fallback on error
  81. match self.fetch_fresh_rate(currency).await {
  82. Ok(rate) => Ok(rate),
  83. Err(e) => {
  84. tracing::warn!(
  85. "Failed to fetch exchange rates, using fallback for {:?}: {}",
  86. currency,
  87. e
  88. );
  89. Self::fallback_rate(currency)
  90. }
  91. }
  92. }
  93. /// Fetch fresh rate and update cache
  94. async fn fetch_fresh_rate(&self, currency: &CurrencyUnit) -> Result<f64, Error> {
  95. let url = "https://mempool.space/api/v1/prices";
  96. let response = reqwest::get(url)
  97. .await
  98. .map_err(|_| Error::UnknownInvoiceAmount)?
  99. .json::<MempoolPricesResponse>()
  100. .await
  101. .map_err(|_| Error::UnknownInvoiceAmount)?;
  102. let rate = Self::rate_for_currency(&response, currency)?;
  103. *self.rates.lock().await = Some((response, Instant::now()));
  104. Ok(rate)
  105. }
  106. fn rate_for_currency(
  107. rates: &MempoolPricesResponse,
  108. currency: &CurrencyUnit,
  109. ) -> Result<f64, Error> {
  110. match currency {
  111. CurrencyUnit::Usd => Ok(rates.usd),
  112. CurrencyUnit::Eur => Ok(rates.eur),
  113. _ => Err(Error::UnknownInvoiceAmount),
  114. }
  115. }
  116. fn fallback_rate(currency: &CurrencyUnit) -> Result<f64, Error> {
  117. match currency {
  118. CurrencyUnit::Usd => Ok(110_000.0), // $110k per BTC
  119. CurrencyUnit::Eur => Ok(95_000.0), // €95k per BTC
  120. _ => Err(Error::UnknownInvoiceAmount),
  121. }
  122. }
  123. }
  124. async fn convert_currency_amount(
  125. amount: u64,
  126. from_unit: &CurrencyUnit,
  127. target_unit: &CurrencyUnit,
  128. rate_cache: &ExchangeRateCache,
  129. ) -> Result<Amount, Error> {
  130. use CurrencyUnit::*;
  131. // Try basic unit conversion first (handles SAT/MSAT and same-unit conversions)
  132. if let Ok(converted) = to_unit(amount, from_unit, target_unit) {
  133. return Ok(converted);
  134. }
  135. // Handle fiat <-> bitcoin conversions that require exchange rates
  136. match (from_unit, target_unit) {
  137. // Fiat to Bitcoin conversions
  138. (Usd | Eur, Sat) => {
  139. let rate = rate_cache.get_btc_rate(from_unit).await?;
  140. let fiat_amount = amount as f64 / 100.0; // cents to dollars/euros
  141. Ok(Amount::from(
  142. (fiat_amount / rate * 100_000_000.0).round() as u64
  143. )) // to sats
  144. }
  145. (Usd | Eur, Msat) => {
  146. let rate = rate_cache.get_btc_rate(from_unit).await?;
  147. let fiat_amount = amount as f64 / 100.0; // cents to dollars/euros
  148. Ok(Amount::from(
  149. (fiat_amount / rate * 100_000_000_000.0).round() as u64,
  150. )) // to msats
  151. }
  152. // Bitcoin to fiat conversions
  153. (Sat, Usd | Eur) => {
  154. let rate = rate_cache.get_btc_rate(target_unit).await?;
  155. let btc_amount = amount as f64 / 100_000_000.0; // sats to BTC
  156. Ok(Amount::from((btc_amount * rate * 100.0).round() as u64)) // to cents
  157. }
  158. (Msat, Usd | Eur) => {
  159. let rate = rate_cache.get_btc_rate(target_unit).await?;
  160. let btc_amount = amount as f64 / 100_000_000_000.0; // msats to BTC
  161. Ok(Amount::from((btc_amount * rate * 100.0).round() as u64)) // to cents
  162. }
  163. _ => Err(Error::UnknownInvoiceAmount), // Unsupported conversion
  164. }
  165. }
  166. /// Secondary repayment queue manager for any-amount invoices
  167. #[derive(Debug, Clone)]
  168. struct SecondaryRepaymentQueue {
  169. queue: Arc<Mutex<VecDeque<PaymentIdentifier>>>,
  170. max_size: usize,
  171. sender: tokio::sync::mpsc::Sender<WaitPaymentResponse>,
  172. unit: CurrencyUnit,
  173. }
  174. impl SecondaryRepaymentQueue {
  175. fn new(
  176. max_size: usize,
  177. sender: tokio::sync::mpsc::Sender<WaitPaymentResponse>,
  178. unit: CurrencyUnit,
  179. ) -> Self {
  180. let queue = Arc::new(Mutex::new(VecDeque::new()));
  181. let repayment_queue = Self {
  182. queue: queue.clone(),
  183. max_size,
  184. sender,
  185. unit,
  186. };
  187. // Start the background secondary repayment processor
  188. repayment_queue.start_secondary_repayment_processor();
  189. repayment_queue
  190. }
  191. /// Add a payment to the secondary repayment queue
  192. async fn enqueue_for_repayment(&self, payment: PaymentIdentifier) {
  193. let mut queue = self.queue.lock().await;
  194. // If queue is at max capacity, remove the oldest item
  195. if queue.len() >= self.max_size {
  196. if let Some(dropped) = queue.pop_front() {
  197. tracing::debug!(
  198. "Secondary repayment queue at capacity, dropping oldest payment: {:?}",
  199. dropped
  200. );
  201. }
  202. }
  203. queue.push_back(payment);
  204. tracing::debug!(
  205. "Added payment to secondary repayment queue, current size: {}",
  206. queue.len()
  207. );
  208. }
  209. /// Start the background task that randomly processes secondary repayments from the queue
  210. fn start_secondary_repayment_processor(&self) {
  211. let queue = self.queue.clone();
  212. let sender = self.sender.clone();
  213. let unit = self.unit.clone();
  214. tokio::spawn(async move {
  215. use bitcoin::secp256k1::rand::rngs::OsRng;
  216. use bitcoin::secp256k1::rand::Rng;
  217. let mut rng = OsRng;
  218. loop {
  219. // Wait for a random interval between 30 seconds and 3 minutes (180 seconds)
  220. let delay_secs = rng.gen_range(1..=3);
  221. time::sleep(time::Duration::from_secs(delay_secs)).await;
  222. // Try to process a random payment from the queue without removing it
  223. let payment_to_process = {
  224. let q = queue.lock().await;
  225. if q.is_empty() {
  226. None
  227. } else {
  228. // Pick a random index from the queue but don't remove it
  229. let index = rng.gen_range(0..q.len());
  230. q.get(index).cloned()
  231. }
  232. };
  233. if let Some(payment) = payment_to_process {
  234. // Generate a random amount for this secondary payment (same range as initial payment: 1-1000)
  235. let random_amount: u64 = rng.gen_range(1..=1000);
  236. // Create amount based on unit, ensuring minimum of 1 sat worth
  237. let secondary_amount = match &unit {
  238. CurrencyUnit::Sat => Amount::from(random_amount),
  239. CurrencyUnit::Msat => Amount::from(u64::max(random_amount * 1000, 1000)),
  240. _ => Amount::from(u64::max(random_amount, 1)), // fallback
  241. };
  242. // Generate a unique payment identifier for this secondary payment
  243. // We'll create a new payment hash by appending a timestamp and random bytes
  244. use bitcoin::hashes::{sha256, Hash};
  245. let mut random_bytes = [0u8; 16];
  246. rng.fill(&mut random_bytes);
  247. let timestamp = std::time::SystemTime::now()
  248. .duration_since(std::time::UNIX_EPOCH)
  249. .unwrap()
  250. .as_nanos() as u64;
  251. // Create a unique hash combining the original payment identifier, timestamp, and random bytes
  252. let mut hasher_input = Vec::new();
  253. hasher_input.extend_from_slice(payment.to_string().as_bytes());
  254. hasher_input.extend_from_slice(&timestamp.to_le_bytes());
  255. hasher_input.extend_from_slice(&random_bytes);
  256. let unique_hash = sha256::Hash::hash(&hasher_input);
  257. let unique_payment_id = PaymentIdentifier::PaymentHash(*unique_hash.as_ref());
  258. tracing::info!(
  259. "Processing secondary repayment: original={:?}, new_id={:?}, amount={}",
  260. payment,
  261. unique_payment_id,
  262. secondary_amount
  263. );
  264. // Send the payment notification using the original payment identifier
  265. // The mint will process this through the normal payment stream
  266. let secondary_response = WaitPaymentResponse {
  267. payment_identifier: payment.clone(),
  268. payment_amount: secondary_amount,
  269. unit: unit.clone(),
  270. payment_id: unique_payment_id.to_string(),
  271. };
  272. if let Err(e) = sender.send(secondary_response).await {
  273. tracing::error!(
  274. "Failed to send secondary repayment notification for {:?}: {}",
  275. unique_payment_id,
  276. e
  277. );
  278. }
  279. }
  280. }
  281. });
  282. }
  283. }
  284. /// Fake Wallet
  285. #[derive(Clone)]
  286. pub struct FakeWallet {
  287. fee_reserve: FeeReserve,
  288. sender: tokio::sync::mpsc::Sender<WaitPaymentResponse>,
  289. receiver: Arc<Mutex<Option<tokio::sync::mpsc::Receiver<WaitPaymentResponse>>>>,
  290. payment_states: Arc<Mutex<HashMap<String, MeltQuoteState>>>,
  291. failed_payment_check: Arc<Mutex<HashSet<String>>>,
  292. payment_delay: u64,
  293. wait_invoice_cancel_token: CancellationToken,
  294. wait_invoice_is_active: Arc<AtomicBool>,
  295. incoming_payments: Arc<RwLock<HashMap<PaymentIdentifier, Vec<WaitPaymentResponse>>>>,
  296. unit: CurrencyUnit,
  297. secondary_repayment_queue: SecondaryRepaymentQueue,
  298. exchange_rate_cache: ExchangeRateCache,
  299. }
  300. impl FakeWallet {
  301. /// Create new [`FakeWallet`]
  302. pub fn new(
  303. fee_reserve: FeeReserve,
  304. payment_states: HashMap<String, MeltQuoteState>,
  305. fail_payment_check: HashSet<String>,
  306. payment_delay: u64,
  307. unit: CurrencyUnit,
  308. ) -> Self {
  309. Self::new_with_repay_queue_size(
  310. fee_reserve,
  311. payment_states,
  312. fail_payment_check,
  313. payment_delay,
  314. unit,
  315. DEFAULT_REPAY_QUEUE_MAX_SIZE,
  316. )
  317. }
  318. /// Create new [`FakeWallet`] with custom secondary repayment queue size
  319. pub fn new_with_repay_queue_size(
  320. fee_reserve: FeeReserve,
  321. payment_states: HashMap<String, MeltQuoteState>,
  322. fail_payment_check: HashSet<String>,
  323. payment_delay: u64,
  324. unit: CurrencyUnit,
  325. repay_queue_max_size: usize,
  326. ) -> Self {
  327. let (sender, receiver) = tokio::sync::mpsc::channel(8);
  328. let incoming_payments = Arc::new(RwLock::new(HashMap::new()));
  329. let secondary_repayment_queue =
  330. SecondaryRepaymentQueue::new(repay_queue_max_size, sender.clone(), unit.clone());
  331. Self {
  332. fee_reserve,
  333. sender,
  334. receiver: Arc::new(Mutex::new(Some(receiver))),
  335. payment_states: Arc::new(Mutex::new(payment_states)),
  336. failed_payment_check: Arc::new(Mutex::new(fail_payment_check)),
  337. payment_delay,
  338. wait_invoice_cancel_token: CancellationToken::new(),
  339. wait_invoice_is_active: Arc::new(AtomicBool::new(false)),
  340. incoming_payments,
  341. unit,
  342. secondary_repayment_queue,
  343. exchange_rate_cache: ExchangeRateCache::new(),
  344. }
  345. }
  346. }
  347. /// Struct for signaling what methods should respond via invoice description
  348. #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
  349. pub struct FakeInvoiceDescription {
  350. /// State to be returned from pay invoice state
  351. pub pay_invoice_state: MeltQuoteState,
  352. /// State to be returned by check payment state
  353. pub check_payment_state: MeltQuoteState,
  354. /// Should pay invoice error
  355. pub pay_err: bool,
  356. /// Should check failure
  357. pub check_err: bool,
  358. }
  359. impl Default for FakeInvoiceDescription {
  360. fn default() -> Self {
  361. Self {
  362. pay_invoice_state: MeltQuoteState::Paid,
  363. check_payment_state: MeltQuoteState::Paid,
  364. pay_err: false,
  365. check_err: false,
  366. }
  367. }
  368. }
  369. #[async_trait]
  370. impl MintPayment for FakeWallet {
  371. type Err = payment::Error;
  372. #[instrument(skip_all)]
  373. async fn get_settings(&self) -> Result<Value, Self::Err> {
  374. Ok(serde_json::to_value(Bolt11Settings {
  375. mpp: true,
  376. unit: self.unit.clone(),
  377. invoice_description: true,
  378. amountless: false,
  379. bolt12: true,
  380. })?)
  381. }
  382. #[instrument(skip_all)]
  383. fn is_wait_invoice_active(&self) -> bool {
  384. self.wait_invoice_is_active.load(Ordering::SeqCst)
  385. }
  386. #[instrument(skip_all)]
  387. fn cancel_wait_invoice(&self) {
  388. self.wait_invoice_cancel_token.cancel()
  389. }
  390. #[instrument(skip_all)]
  391. async fn wait_payment_event(
  392. &self,
  393. ) -> Result<Pin<Box<dyn Stream<Item = Event> + Send>>, Self::Err> {
  394. tracing::info!("Starting stream for fake invoices");
  395. let receiver = self
  396. .receiver
  397. .lock()
  398. .await
  399. .take()
  400. .ok_or(Error::NoReceiver)
  401. .unwrap();
  402. let receiver_stream = ReceiverStream::new(receiver);
  403. Ok(Box::pin(receiver_stream.map(move |wait_response| {
  404. Event::PaymentReceived(wait_response)
  405. })))
  406. }
  407. #[instrument(skip_all)]
  408. async fn get_payment_quote(
  409. &self,
  410. unit: &CurrencyUnit,
  411. options: OutgoingPaymentOptions,
  412. ) -> Result<PaymentQuoteResponse, Self::Err> {
  413. let (amount_msat, request_lookup_id) = match options {
  414. OutgoingPaymentOptions::Bolt11(bolt11_options) => {
  415. // If we have specific amount options, use those
  416. let amount_msat: u64 = if let Some(melt_options) = bolt11_options.melt_options {
  417. let msats = match melt_options {
  418. MeltOptions::Amountless { amountless } => {
  419. let amount_msat = amountless.amount_msat;
  420. if let Some(invoice_amount) =
  421. bolt11_options.bolt11.amount_milli_satoshis()
  422. {
  423. ensure_cdk!(
  424. invoice_amount == u64::from(amount_msat),
  425. Error::UnknownInvoiceAmount.into()
  426. );
  427. }
  428. amount_msat
  429. }
  430. MeltOptions::Mpp { mpp } => mpp.amount,
  431. };
  432. u64::from(msats)
  433. } else {
  434. // Fall back to invoice amount
  435. bolt11_options
  436. .bolt11
  437. .amount_milli_satoshis()
  438. .ok_or(Error::UnknownInvoiceAmount)?
  439. };
  440. let payment_id =
  441. PaymentIdentifier::PaymentHash(*bolt11_options.bolt11.payment_hash().as_ref());
  442. (amount_msat, Some(payment_id))
  443. }
  444. OutgoingPaymentOptions::Bolt12(bolt12_options) => {
  445. let offer = bolt12_options.offer;
  446. let amount_msat: u64 = if let Some(amount) = bolt12_options.melt_options {
  447. amount.amount_msat().into()
  448. } else {
  449. // Fall back to offer amount
  450. let amount = offer.amount().ok_or(Error::UnknownInvoiceAmount)?;
  451. match amount {
  452. lightning::offers::offer::Amount::Bitcoin { amount_msats } => amount_msats,
  453. _ => return Err(Error::UnknownInvoiceAmount.into()),
  454. }
  455. };
  456. (amount_msat, None)
  457. }
  458. };
  459. let amount = convert_currency_amount(
  460. amount_msat,
  461. &CurrencyUnit::Msat,
  462. unit,
  463. &self.exchange_rate_cache,
  464. )
  465. .await?;
  466. let relative_fee_reserve =
  467. (self.fee_reserve.percent_fee_reserve * u64::from(amount) as f32) as u64;
  468. let absolute_fee_reserve: u64 = self.fee_reserve.min_fee_reserve.into();
  469. let fee = max(relative_fee_reserve, absolute_fee_reserve);
  470. Ok(PaymentQuoteResponse {
  471. request_lookup_id,
  472. amount,
  473. fee: fee.into(),
  474. state: MeltQuoteState::Unpaid,
  475. unit: unit.clone(),
  476. })
  477. }
  478. #[instrument(skip_all)]
  479. async fn make_payment(
  480. &self,
  481. unit: &CurrencyUnit,
  482. options: OutgoingPaymentOptions,
  483. ) -> Result<MakePaymentResponse, Self::Err> {
  484. match options {
  485. OutgoingPaymentOptions::Bolt11(bolt11_options) => {
  486. let bolt11 = bolt11_options.bolt11;
  487. let payment_hash = bolt11.payment_hash().to_string();
  488. let description = bolt11.description().to_string();
  489. let status: Option<FakeInvoiceDescription> =
  490. serde_json::from_str(&description).ok();
  491. let mut payment_states = self.payment_states.lock().await;
  492. let payment_status = status
  493. .clone()
  494. .map(|s| s.pay_invoice_state)
  495. .unwrap_or(MeltQuoteState::Paid);
  496. let checkout_going_status = status
  497. .clone()
  498. .map(|s| s.check_payment_state)
  499. .unwrap_or(MeltQuoteState::Paid);
  500. payment_states.insert(payment_hash.clone(), checkout_going_status);
  501. if let Some(description) = status {
  502. if description.check_err {
  503. let mut fail = self.failed_payment_check.lock().await;
  504. fail.insert(payment_hash.clone());
  505. }
  506. ensure_cdk!(!description.pay_err, Error::UnknownInvoice.into());
  507. }
  508. let amount_msat: u64 = if let Some(melt_options) = bolt11_options.melt_options {
  509. melt_options.amount_msat().into()
  510. } else {
  511. // Fall back to invoice amount
  512. bolt11
  513. .amount_milli_satoshis()
  514. .ok_or(Error::UnknownInvoiceAmount)?
  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().unwrap();
  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 = status.unwrap_or(MeltQuoteState::Paid);
  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: Amount::ZERO,
  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. .unwrap();
  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).unwrap();
  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. .unwrap()
  735. }