lib.rs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674
  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 async_trait::async_trait;
  20. use bitcoin::hashes::{sha256, Hash};
  21. use bitcoin::secp256k1::{Secp256k1, SecretKey};
  22. use cdk_common::amount::{to_unit, Amount};
  23. use cdk_common::common::FeeReserve;
  24. use cdk_common::ensure_cdk;
  25. use cdk_common::nuts::{CurrencyUnit, MeltOptions, MeltQuoteState};
  26. use cdk_common::payment::{
  27. self, Bolt11Settings, CreateIncomingPaymentResponse, Event, IncomingPaymentOptions,
  28. MakePaymentResponse, MintPayment, OutgoingPaymentOptions, PaymentIdentifier,
  29. PaymentQuoteResponse, WaitPaymentResponse,
  30. };
  31. use error::Error;
  32. use futures::stream::StreamExt;
  33. use futures::Stream;
  34. use lightning::offers::offer::OfferBuilder;
  35. use lightning_invoice::{Bolt11Invoice, Currency, InvoiceBuilder, PaymentSecret};
  36. use serde::{Deserialize, Serialize};
  37. use serde_json::Value;
  38. use tokio::sync::{Mutex, RwLock};
  39. use tokio::time;
  40. use tokio_stream::wrappers::ReceiverStream;
  41. use tokio_util::sync::CancellationToken;
  42. use tracing::instrument;
  43. use uuid::Uuid;
  44. pub mod error;
  45. /// Default maximum size for the secondary repayment queue
  46. const DEFAULT_REPAY_QUEUE_MAX_SIZE: usize = 100;
  47. /// Secondary repayment queue manager for any-amount invoices
  48. #[derive(Debug, Clone)]
  49. struct SecondaryRepaymentQueue {
  50. queue: Arc<Mutex<VecDeque<PaymentIdentifier>>>,
  51. max_size: usize,
  52. sender: tokio::sync::mpsc::Sender<WaitPaymentResponse>,
  53. unit: CurrencyUnit,
  54. }
  55. impl SecondaryRepaymentQueue {
  56. fn new(
  57. max_size: usize,
  58. sender: tokio::sync::mpsc::Sender<WaitPaymentResponse>,
  59. unit: CurrencyUnit,
  60. ) -> Self {
  61. let queue = Arc::new(Mutex::new(VecDeque::new()));
  62. let repayment_queue = Self {
  63. queue: queue.clone(),
  64. max_size,
  65. sender,
  66. unit,
  67. };
  68. // Start the background secondary repayment processor
  69. repayment_queue.start_secondary_repayment_processor();
  70. repayment_queue
  71. }
  72. /// Add a payment to the secondary repayment queue
  73. async fn enqueue_for_repayment(&self, payment: PaymentIdentifier) {
  74. let mut queue = self.queue.lock().await;
  75. // If queue is at max capacity, remove the oldest item
  76. if queue.len() >= self.max_size {
  77. if let Some(dropped) = queue.pop_front() {
  78. tracing::debug!(
  79. "Secondary repayment queue at capacity, dropping oldest payment: {:?}",
  80. dropped
  81. );
  82. }
  83. }
  84. queue.push_back(payment);
  85. tracing::debug!(
  86. "Added payment to secondary repayment queue, current size: {}",
  87. queue.len()
  88. );
  89. }
  90. /// Start the background task that randomly processes secondary repayments from the queue
  91. fn start_secondary_repayment_processor(&self) {
  92. let queue = self.queue.clone();
  93. let sender = self.sender.clone();
  94. let unit = self.unit.clone();
  95. tokio::spawn(async move {
  96. use bitcoin::secp256k1::rand::rngs::OsRng;
  97. use bitcoin::secp256k1::rand::Rng;
  98. let mut rng = OsRng;
  99. loop {
  100. // Wait for a random interval between 30 seconds and 3 minutes (180 seconds)
  101. let delay_secs = rng.gen_range(1..=3);
  102. time::sleep(time::Duration::from_secs(delay_secs)).await;
  103. // Try to process a random payment from the queue without removing it
  104. let payment_to_process = {
  105. let q = queue.lock().await;
  106. if q.is_empty() {
  107. None
  108. } else {
  109. // Pick a random index from the queue but don't remove it
  110. let index = rng.gen_range(0..q.len());
  111. q.get(index).cloned()
  112. }
  113. };
  114. if let Some(payment) = payment_to_process {
  115. // Generate a random amount for this secondary payment (same range as initial payment: 1-1000)
  116. let random_amount: u64 = rng.gen_range(1..=1000);
  117. // Create amount based on unit, ensuring minimum of 1 sat worth
  118. let secondary_amount = match &unit {
  119. CurrencyUnit::Sat => Amount::from(random_amount),
  120. CurrencyUnit::Msat => Amount::from(u64::max(random_amount * 1000, 1000)),
  121. _ => Amount::from(u64::max(random_amount, 1)), // fallback
  122. };
  123. // Generate a unique payment identifier for this secondary payment
  124. // We'll create a new payment hash by appending a timestamp and random bytes
  125. use bitcoin::hashes::{sha256, Hash};
  126. let mut random_bytes = [0u8; 16];
  127. rng.fill(&mut random_bytes);
  128. let timestamp = std::time::SystemTime::now()
  129. .duration_since(std::time::UNIX_EPOCH)
  130. .unwrap()
  131. .as_nanos() as u64;
  132. // Create a unique hash combining the original payment identifier, timestamp, and random bytes
  133. let mut hasher_input = Vec::new();
  134. hasher_input.extend_from_slice(payment.to_string().as_bytes());
  135. hasher_input.extend_from_slice(&timestamp.to_le_bytes());
  136. hasher_input.extend_from_slice(&random_bytes);
  137. let unique_hash = sha256::Hash::hash(&hasher_input);
  138. let unique_payment_id = PaymentIdentifier::PaymentHash(*unique_hash.as_ref());
  139. tracing::info!(
  140. "Processing secondary repayment: original={:?}, new_id={:?}, amount={}",
  141. payment,
  142. unique_payment_id,
  143. secondary_amount
  144. );
  145. // Send the payment notification using the original payment identifier
  146. // The mint will process this through the normal payment stream
  147. let secondary_response = WaitPaymentResponse {
  148. payment_identifier: payment.clone(),
  149. payment_amount: secondary_amount,
  150. unit: unit.clone(),
  151. payment_id: unique_payment_id.to_string(),
  152. };
  153. if let Err(e) = sender.send(secondary_response).await {
  154. tracing::error!(
  155. "Failed to send secondary repayment notification for {:?}: {}",
  156. unique_payment_id,
  157. e
  158. );
  159. }
  160. }
  161. }
  162. });
  163. }
  164. }
  165. /// Fake Wallet
  166. #[derive(Clone)]
  167. pub struct FakeWallet {
  168. fee_reserve: FeeReserve,
  169. sender: tokio::sync::mpsc::Sender<WaitPaymentResponse>,
  170. receiver: Arc<Mutex<Option<tokio::sync::mpsc::Receiver<WaitPaymentResponse>>>>,
  171. payment_states: Arc<Mutex<HashMap<String, MeltQuoteState>>>,
  172. failed_payment_check: Arc<Mutex<HashSet<String>>>,
  173. payment_delay: u64,
  174. wait_invoice_cancel_token: CancellationToken,
  175. wait_invoice_is_active: Arc<AtomicBool>,
  176. incoming_payments: Arc<RwLock<HashMap<PaymentIdentifier, Vec<WaitPaymentResponse>>>>,
  177. unit: CurrencyUnit,
  178. secondary_repayment_queue: SecondaryRepaymentQueue,
  179. }
  180. impl FakeWallet {
  181. /// Create new [`FakeWallet`]
  182. pub fn new(
  183. fee_reserve: FeeReserve,
  184. payment_states: HashMap<String, MeltQuoteState>,
  185. fail_payment_check: HashSet<String>,
  186. payment_delay: u64,
  187. unit: CurrencyUnit,
  188. ) -> Self {
  189. Self::new_with_repay_queue_size(
  190. fee_reserve,
  191. payment_states,
  192. fail_payment_check,
  193. payment_delay,
  194. unit,
  195. DEFAULT_REPAY_QUEUE_MAX_SIZE,
  196. )
  197. }
  198. /// Create new [`FakeWallet`] with custom secondary repayment queue size
  199. pub fn new_with_repay_queue_size(
  200. fee_reserve: FeeReserve,
  201. payment_states: HashMap<String, MeltQuoteState>,
  202. fail_payment_check: HashSet<String>,
  203. payment_delay: u64,
  204. unit: CurrencyUnit,
  205. repay_queue_max_size: usize,
  206. ) -> Self {
  207. let (sender, receiver) = tokio::sync::mpsc::channel(8);
  208. let incoming_payments = Arc::new(RwLock::new(HashMap::new()));
  209. let secondary_repayment_queue =
  210. SecondaryRepaymentQueue::new(repay_queue_max_size, sender.clone(), unit.clone());
  211. Self {
  212. fee_reserve,
  213. sender,
  214. receiver: Arc::new(Mutex::new(Some(receiver))),
  215. payment_states: Arc::new(Mutex::new(payment_states)),
  216. failed_payment_check: Arc::new(Mutex::new(fail_payment_check)),
  217. payment_delay,
  218. wait_invoice_cancel_token: CancellationToken::new(),
  219. wait_invoice_is_active: Arc::new(AtomicBool::new(false)),
  220. incoming_payments,
  221. unit,
  222. secondary_repayment_queue,
  223. }
  224. }
  225. }
  226. /// Struct for signaling what methods should respond via invoice description
  227. #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
  228. pub struct FakeInvoiceDescription {
  229. /// State to be returned from pay invoice state
  230. pub pay_invoice_state: MeltQuoteState,
  231. /// State to be returned by check payment state
  232. pub check_payment_state: MeltQuoteState,
  233. /// Should pay invoice error
  234. pub pay_err: bool,
  235. /// Should check failure
  236. pub check_err: bool,
  237. }
  238. impl Default for FakeInvoiceDescription {
  239. fn default() -> Self {
  240. Self {
  241. pay_invoice_state: MeltQuoteState::Paid,
  242. check_payment_state: MeltQuoteState::Paid,
  243. pay_err: false,
  244. check_err: false,
  245. }
  246. }
  247. }
  248. #[async_trait]
  249. impl MintPayment for FakeWallet {
  250. type Err = payment::Error;
  251. #[instrument(skip_all)]
  252. async fn get_settings(&self) -> Result<Value, Self::Err> {
  253. Ok(serde_json::to_value(Bolt11Settings {
  254. mpp: true,
  255. unit: self.unit.clone(),
  256. invoice_description: true,
  257. amountless: false,
  258. bolt12: true,
  259. })?)
  260. }
  261. #[instrument(skip_all)]
  262. fn is_wait_invoice_active(&self) -> bool {
  263. self.wait_invoice_is_active.load(Ordering::SeqCst)
  264. }
  265. #[instrument(skip_all)]
  266. fn cancel_wait_invoice(&self) {
  267. self.wait_invoice_cancel_token.cancel()
  268. }
  269. #[instrument(skip_all)]
  270. async fn wait_payment_event(
  271. &self,
  272. ) -> Result<Pin<Box<dyn Stream<Item = Event> + Send>>, Self::Err> {
  273. tracing::info!("Starting stream for fake invoices");
  274. let receiver = self
  275. .receiver
  276. .lock()
  277. .await
  278. .take()
  279. .ok_or(Error::NoReceiver)
  280. .unwrap();
  281. let receiver_stream = ReceiverStream::new(receiver);
  282. Ok(Box::pin(receiver_stream.map(move |wait_response| {
  283. Event::PaymentReceived(wait_response)
  284. })))
  285. }
  286. #[instrument(skip_all)]
  287. async fn get_payment_quote(
  288. &self,
  289. unit: &CurrencyUnit,
  290. options: OutgoingPaymentOptions,
  291. ) -> Result<PaymentQuoteResponse, Self::Err> {
  292. let (amount_msat, request_lookup_id) = match options {
  293. OutgoingPaymentOptions::Bolt11(bolt11_options) => {
  294. // If we have specific amount options, use those
  295. let amount_msat: u64 = if let Some(melt_options) = bolt11_options.melt_options {
  296. let msats = match melt_options {
  297. MeltOptions::Amountless { amountless } => {
  298. let amount_msat = amountless.amount_msat;
  299. if let Some(invoice_amount) =
  300. bolt11_options.bolt11.amount_milli_satoshis()
  301. {
  302. ensure_cdk!(
  303. invoice_amount == u64::from(amount_msat),
  304. Error::UnknownInvoiceAmount.into()
  305. );
  306. }
  307. amount_msat
  308. }
  309. MeltOptions::Mpp { mpp } => mpp.amount,
  310. };
  311. u64::from(msats)
  312. } else {
  313. // Fall back to invoice amount
  314. bolt11_options
  315. .bolt11
  316. .amount_milli_satoshis()
  317. .ok_or(Error::UnknownInvoiceAmount)?
  318. };
  319. let payment_id =
  320. PaymentIdentifier::PaymentHash(*bolt11_options.bolt11.payment_hash().as_ref());
  321. (amount_msat, Some(payment_id))
  322. }
  323. OutgoingPaymentOptions::Bolt12(bolt12_options) => {
  324. let offer = bolt12_options.offer;
  325. let amount_msat: u64 = if let Some(amount) = bolt12_options.melt_options {
  326. amount.amount_msat().into()
  327. } else {
  328. // Fall back to offer amount
  329. let amount = offer.amount().ok_or(Error::UnknownInvoiceAmount)?;
  330. match amount {
  331. lightning::offers::offer::Amount::Bitcoin { amount_msats } => amount_msats,
  332. _ => return Err(Error::UnknownInvoiceAmount.into()),
  333. }
  334. };
  335. (amount_msat, None)
  336. }
  337. };
  338. let amount = to_unit(amount_msat, &CurrencyUnit::Msat, unit)?;
  339. let relative_fee_reserve =
  340. (self.fee_reserve.percent_fee_reserve * u64::from(amount) as f32) as u64;
  341. let absolute_fee_reserve: u64 = self.fee_reserve.min_fee_reserve.into();
  342. let fee = max(relative_fee_reserve, absolute_fee_reserve);
  343. Ok(PaymentQuoteResponse {
  344. request_lookup_id,
  345. amount,
  346. fee: fee.into(),
  347. state: MeltQuoteState::Unpaid,
  348. unit: unit.clone(),
  349. })
  350. }
  351. #[instrument(skip_all)]
  352. async fn make_payment(
  353. &self,
  354. unit: &CurrencyUnit,
  355. options: OutgoingPaymentOptions,
  356. ) -> Result<MakePaymentResponse, Self::Err> {
  357. match options {
  358. OutgoingPaymentOptions::Bolt11(bolt11_options) => {
  359. let bolt11 = bolt11_options.bolt11;
  360. let payment_hash = bolt11.payment_hash().to_string();
  361. let description = bolt11.description().to_string();
  362. let status: Option<FakeInvoiceDescription> =
  363. serde_json::from_str(&description).ok();
  364. let mut payment_states = self.payment_states.lock().await;
  365. let payment_status = status
  366. .clone()
  367. .map(|s| s.pay_invoice_state)
  368. .unwrap_or(MeltQuoteState::Paid);
  369. let checkout_going_status = status
  370. .clone()
  371. .map(|s| s.check_payment_state)
  372. .unwrap_or(MeltQuoteState::Paid);
  373. payment_states.insert(payment_hash.clone(), checkout_going_status);
  374. if let Some(description) = status {
  375. if description.check_err {
  376. let mut fail = self.failed_payment_check.lock().await;
  377. fail.insert(payment_hash.clone());
  378. }
  379. ensure_cdk!(!description.pay_err, Error::UnknownInvoice.into());
  380. }
  381. let amount_msat: u64 = if let Some(melt_options) = bolt11_options.melt_options {
  382. melt_options.amount_msat().into()
  383. } else {
  384. // Fall back to invoice amount
  385. bolt11
  386. .amount_milli_satoshis()
  387. .ok_or(Error::UnknownInvoiceAmount)?
  388. };
  389. let total_spent = to_unit(amount_msat, &CurrencyUnit::Msat, unit)?;
  390. Ok(MakePaymentResponse {
  391. payment_proof: Some("".to_string()),
  392. payment_lookup_id: PaymentIdentifier::PaymentHash(
  393. *bolt11.payment_hash().as_ref(),
  394. ),
  395. status: payment_status,
  396. total_spent: total_spent + 1.into(),
  397. unit: unit.clone(),
  398. })
  399. }
  400. OutgoingPaymentOptions::Bolt12(bolt12_options) => {
  401. let bolt12 = bolt12_options.offer;
  402. let amount_msat: u64 = if let Some(amount) = bolt12_options.melt_options {
  403. amount.amount_msat().into()
  404. } else {
  405. // Fall back to offer amount
  406. let amount = bolt12.amount().ok_or(Error::UnknownInvoiceAmount)?;
  407. match amount {
  408. lightning::offers::offer::Amount::Bitcoin { amount_msats } => amount_msats,
  409. _ => return Err(Error::UnknownInvoiceAmount.into()),
  410. }
  411. };
  412. let total_spent = to_unit(amount_msat, &CurrencyUnit::Msat, unit)?;
  413. Ok(MakePaymentResponse {
  414. payment_proof: Some("".to_string()),
  415. payment_lookup_id: PaymentIdentifier::CustomId(Uuid::new_v4().to_string()),
  416. status: MeltQuoteState::Paid,
  417. total_spent: total_spent + 1.into(),
  418. unit: unit.clone(),
  419. })
  420. }
  421. }
  422. }
  423. #[instrument(skip_all)]
  424. async fn create_incoming_payment_request(
  425. &self,
  426. unit: &CurrencyUnit,
  427. options: IncomingPaymentOptions,
  428. ) -> Result<CreateIncomingPaymentResponse, Self::Err> {
  429. let (payment_hash, request, amount, expiry) = match options {
  430. IncomingPaymentOptions::Bolt12(bolt12_options) => {
  431. let description = bolt12_options.description.unwrap_or_default();
  432. let amount = bolt12_options.amount;
  433. let expiry = bolt12_options.unix_expiry;
  434. let secret_key = SecretKey::new(&mut bitcoin::secp256k1::rand::rngs::OsRng);
  435. let secp_ctx = Secp256k1::new();
  436. let offer_builder = OfferBuilder::new(secret_key.public_key(&secp_ctx))
  437. .description(description.clone());
  438. let offer_builder = match amount {
  439. Some(amount) => {
  440. let amount_msat = to_unit(amount, unit, &CurrencyUnit::Msat)?;
  441. offer_builder.amount_msats(amount_msat.into())
  442. }
  443. None => offer_builder,
  444. };
  445. let offer = offer_builder.build().unwrap();
  446. (
  447. PaymentIdentifier::OfferId(offer.id().to_string()),
  448. offer.to_string(),
  449. amount.unwrap_or(Amount::ZERO),
  450. expiry,
  451. )
  452. }
  453. IncomingPaymentOptions::Bolt11(bolt11_options) => {
  454. let description = bolt11_options.description.unwrap_or_default();
  455. let amount = bolt11_options.amount;
  456. let expiry = bolt11_options.unix_expiry;
  457. // For fake invoices, always use msats regardless of unit
  458. let amount_msat = if unit == &CurrencyUnit::Sat {
  459. u64::from(amount) * 1000
  460. } else {
  461. // If unit is Msat, use as-is
  462. u64::from(amount)
  463. };
  464. let invoice = create_fake_invoice(amount_msat, description.clone());
  465. let payment_hash = invoice.payment_hash();
  466. (
  467. PaymentIdentifier::PaymentHash(*payment_hash.as_ref()),
  468. invoice.to_string(),
  469. amount,
  470. expiry,
  471. )
  472. }
  473. };
  474. // ALL invoices get immediate payment processing (original behavior)
  475. let sender = self.sender.clone();
  476. let duration = time::Duration::from_secs(self.payment_delay);
  477. let payment_hash_clone = payment_hash.clone();
  478. let incoming_payment = self.incoming_payments.clone();
  479. let unit_clone = self.unit.clone();
  480. let final_amount = if amount == Amount::ZERO {
  481. // For any-amount invoices, generate a random amount for the initial payment
  482. use bitcoin::secp256k1::rand::rngs::OsRng;
  483. use bitcoin::secp256k1::rand::Rng;
  484. let mut rng = OsRng;
  485. let random_amount: u64 = rng.gen_range(1000..=10000);
  486. // Use the same unit as the wallet for any-amount invoices
  487. Amount::from(random_amount)
  488. } else {
  489. amount
  490. };
  491. // Schedule the immediate payment (original behavior maintained)
  492. tokio::spawn(async move {
  493. // Wait for the random delay to elapse
  494. time::sleep(duration).await;
  495. let response = WaitPaymentResponse {
  496. payment_identifier: payment_hash_clone.clone(),
  497. payment_amount: final_amount,
  498. unit: unit_clone,
  499. payment_id: payment_hash_clone.to_string(),
  500. };
  501. let mut incoming = incoming_payment.write().await;
  502. incoming
  503. .entry(payment_hash_clone.clone())
  504. .or_insert_with(Vec::new)
  505. .push(response.clone());
  506. // Send the message after waiting for the specified duration
  507. if sender.send(response.clone()).await.is_err() {
  508. tracing::error!("Failed to send label: {:?}", payment_hash_clone);
  509. }
  510. });
  511. // For any-amount invoices ONLY, also add to the secondary repayment queue
  512. if amount == Amount::ZERO {
  513. tracing::info!(
  514. "Adding any-amount invoice to secondary repayment queue: {:?}",
  515. payment_hash
  516. );
  517. self.secondary_repayment_queue
  518. .enqueue_for_repayment(payment_hash.clone())
  519. .await;
  520. }
  521. Ok(CreateIncomingPaymentResponse {
  522. request_lookup_id: payment_hash,
  523. request,
  524. expiry,
  525. })
  526. }
  527. #[instrument(skip_all)]
  528. async fn check_incoming_payment_status(
  529. &self,
  530. request_lookup_id: &PaymentIdentifier,
  531. ) -> Result<Vec<WaitPaymentResponse>, Self::Err> {
  532. Ok(self
  533. .incoming_payments
  534. .read()
  535. .await
  536. .get(request_lookup_id)
  537. .cloned()
  538. .unwrap_or(vec![]))
  539. }
  540. #[instrument(skip_all)]
  541. async fn check_outgoing_payment(
  542. &self,
  543. request_lookup_id: &PaymentIdentifier,
  544. ) -> Result<MakePaymentResponse, Self::Err> {
  545. // For fake wallet if the state is not explicitly set default to paid
  546. let states = self.payment_states.lock().await;
  547. let status = states.get(&request_lookup_id.to_string()).cloned();
  548. let status = status.unwrap_or(MeltQuoteState::Paid);
  549. let fail_payments = self.failed_payment_check.lock().await;
  550. if fail_payments.contains(&request_lookup_id.to_string()) {
  551. return Err(payment::Error::InvoicePaymentPending);
  552. }
  553. Ok(MakePaymentResponse {
  554. payment_proof: Some("".to_string()),
  555. payment_lookup_id: request_lookup_id.clone(),
  556. status,
  557. total_spent: Amount::ZERO,
  558. unit: CurrencyUnit::Msat,
  559. })
  560. }
  561. }
  562. /// Create fake invoice
  563. #[instrument]
  564. pub fn create_fake_invoice(amount_msat: u64, description: String) -> Bolt11Invoice {
  565. let private_key = SecretKey::from_slice(
  566. &[
  567. 0xe1, 0x26, 0xf6, 0x8f, 0x7e, 0xaf, 0xcc, 0x8b, 0x74, 0xf5, 0x4d, 0x26, 0x9f, 0xe2,
  568. 0x06, 0xbe, 0x71, 0x50, 0x00, 0xf9, 0x4d, 0xac, 0x06, 0x7d, 0x1c, 0x04, 0xa8, 0xca,
  569. 0x3b, 0x2d, 0xb7, 0x34,
  570. ][..],
  571. )
  572. .unwrap();
  573. use bitcoin::secp256k1::rand::rngs::OsRng;
  574. use bitcoin::secp256k1::rand::Rng;
  575. let mut rng = OsRng;
  576. let mut random_bytes = [0u8; 32];
  577. rng.fill(&mut random_bytes);
  578. let payment_hash = sha256::Hash::from_slice(&random_bytes).unwrap();
  579. let payment_secret = PaymentSecret([42u8; 32]);
  580. InvoiceBuilder::new(Currency::Bitcoin)
  581. .description(description)
  582. .payment_hash(payment_hash)
  583. .payment_secret(payment_secret)
  584. .amount_milli_satoshis(amount_msat)
  585. .current_timestamp()
  586. .min_final_cltv_expiry_delta(144)
  587. .build_signed(|hash| Secp256k1::new().sign_ecdsa_recoverable(hash, &private_key))
  588. .unwrap()
  589. }