melt.rs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700
  1. use std::collections::HashSet;
  2. use std::str::FromStr;
  3. use anyhow::bail;
  4. use lightning_invoice::Bolt11Invoice;
  5. use tracing::instrument;
  6. use uuid::Uuid;
  7. use super::{
  8. CurrencyUnit, MeltBolt11Request, MeltQuote, MeltQuoteBolt11Request, MeltQuoteBolt11Response,
  9. Mint, PaymentMethod, PublicKey, State,
  10. };
  11. use crate::amount::to_unit;
  12. use crate::cdk_lightning::{MintLightning, PayInvoiceResponse};
  13. use crate::mint::SigFlag;
  14. use crate::nuts::nut00::ProofsMethods;
  15. use crate::nuts::nut11::{enforce_sig_flag, EnforceSigFlag};
  16. use crate::nuts::{Id, MeltQuoteState};
  17. use crate::types::LnKey;
  18. use crate::util::unix_time;
  19. use crate::{cdk_lightning, Amount, Error};
  20. impl Mint {
  21. fn check_melt_request_acceptable(
  22. &self,
  23. amount: Amount,
  24. unit: CurrencyUnit,
  25. method: PaymentMethod,
  26. ) -> Result<(), Error> {
  27. let mint_info = self.mint_info();
  28. let nut05 = mint_info.nuts.nut05;
  29. if nut05.disabled {
  30. return Err(Error::MeltingDisabled);
  31. }
  32. let settings = nut05
  33. .get_settings(&unit, &method)
  34. .ok_or(Error::UnitUnsupported)?;
  35. let is_above_max = matches!(settings.max_amount, Some(max) if amount > max);
  36. let is_below_min = matches!(settings.min_amount, Some(min) if amount < min);
  37. match is_above_max || is_below_min {
  38. true => Err(Error::AmountOutofLimitRange(
  39. settings.min_amount.unwrap_or_default(),
  40. settings.max_amount.unwrap_or_default(),
  41. amount,
  42. )),
  43. false => Ok(()),
  44. }
  45. }
  46. /// Get melt bolt11 quote
  47. #[instrument(skip_all)]
  48. pub async fn get_melt_bolt11_quote(
  49. &self,
  50. melt_request: &MeltQuoteBolt11Request,
  51. ) -> Result<MeltQuoteBolt11Response<Uuid>, Error> {
  52. let MeltQuoteBolt11Request {
  53. request,
  54. unit,
  55. options: _,
  56. } = melt_request;
  57. let amount = match melt_request.options {
  58. Some(mpp_amount) => mpp_amount.amount,
  59. None => {
  60. let amount_msat = request
  61. .amount_milli_satoshis()
  62. .ok_or(Error::InvoiceAmountUndefined)?;
  63. to_unit(amount_msat, &CurrencyUnit::Msat, unit)
  64. .map_err(|_err| Error::UnsupportedUnit)?
  65. }
  66. };
  67. self.check_melt_request_acceptable(amount, unit.clone(), PaymentMethod::Bolt11)?;
  68. let ln = self
  69. .ln
  70. .get(&LnKey::new(unit.clone(), PaymentMethod::Bolt11))
  71. .ok_or_else(|| {
  72. tracing::info!("Could not get ln backend for {}, bolt11 ", unit);
  73. Error::UnitUnsupported
  74. })?;
  75. let payment_quote = ln.get_payment_quote(melt_request).await.map_err(|err| {
  76. tracing::error!(
  77. "Could not get payment quote for mint quote, {} bolt11, {}",
  78. unit,
  79. err
  80. );
  81. Error::UnitUnsupported
  82. })?;
  83. let quote = MeltQuote::new(
  84. request.to_string(),
  85. unit.clone(),
  86. payment_quote.amount,
  87. payment_quote.fee,
  88. unix_time() + self.config.quote_ttl().melt_ttl,
  89. payment_quote.request_lookup_id.clone(),
  90. );
  91. tracing::debug!(
  92. "New melt quote {} for {} {} with request id {}",
  93. quote.id,
  94. amount,
  95. unit,
  96. payment_quote.request_lookup_id
  97. );
  98. self.localstore.add_melt_quote(quote.clone()).await?;
  99. Ok(quote.into())
  100. }
  101. /// Check melt quote status
  102. #[instrument(skip(self))]
  103. pub async fn check_melt_quote(
  104. &self,
  105. quote_id: &Uuid,
  106. ) -> Result<MeltQuoteBolt11Response<Uuid>, Error> {
  107. let quote = self
  108. .localstore
  109. .get_melt_quote(quote_id)
  110. .await?
  111. .ok_or(Error::UnknownQuote)?;
  112. let blind_signatures = self
  113. .localstore
  114. .get_blind_signatures_for_quote(quote_id)
  115. .await?;
  116. let change = (!blind_signatures.is_empty()).then_some(blind_signatures);
  117. Ok(MeltQuoteBolt11Response {
  118. quote: quote.id,
  119. paid: Some(quote.state == MeltQuoteState::Paid),
  120. state: quote.state,
  121. expiry: quote.expiry,
  122. amount: quote.amount,
  123. fee_reserve: quote.fee_reserve,
  124. payment_preimage: quote.payment_preimage,
  125. change,
  126. })
  127. }
  128. /// Update melt quote
  129. #[instrument(skip_all)]
  130. pub async fn update_melt_quote(&self, quote: MeltQuote) -> Result<(), Error> {
  131. self.localstore.add_melt_quote(quote).await?;
  132. Ok(())
  133. }
  134. /// Get melt quotes
  135. #[instrument(skip_all)]
  136. pub async fn melt_quotes(&self) -> Result<Vec<MeltQuote>, Error> {
  137. let quotes = self.localstore.get_melt_quotes().await?;
  138. Ok(quotes)
  139. }
  140. /// Remove melt quote
  141. #[instrument(skip(self))]
  142. pub async fn remove_melt_quote(&self, quote_id: &Uuid) -> Result<(), Error> {
  143. self.localstore.remove_melt_quote(quote_id).await?;
  144. Ok(())
  145. }
  146. /// Check melt has expected fees
  147. #[instrument(skip_all)]
  148. pub async fn check_melt_expected_ln_fees(
  149. &self,
  150. melt_quote: &MeltQuote,
  151. melt_request: &MeltBolt11Request<Uuid>,
  152. ) -> Result<Option<Amount>, Error> {
  153. let invoice = Bolt11Invoice::from_str(&melt_quote.request)?;
  154. let quote_msats = to_unit(melt_quote.amount, &melt_quote.unit, &CurrencyUnit::Msat)
  155. .expect("Quote unit is checked above that it can convert to msat");
  156. let invoice_amount_msats: Amount = invoice
  157. .amount_milli_satoshis()
  158. .ok_or(Error::InvoiceAmountUndefined)?
  159. .into();
  160. let partial_amount = match invoice_amount_msats > quote_msats {
  161. true => {
  162. let partial_msats = invoice_amount_msats - quote_msats;
  163. Some(
  164. to_unit(partial_msats, &CurrencyUnit::Msat, &melt_quote.unit)
  165. .map_err(|_| Error::UnitUnsupported)?,
  166. )
  167. }
  168. false => None,
  169. };
  170. let amount_to_pay = match partial_amount {
  171. Some(amount_to_pay) => amount_to_pay,
  172. None => to_unit(invoice_amount_msats, &CurrencyUnit::Msat, &melt_quote.unit)
  173. .map_err(|_| Error::UnitUnsupported)?,
  174. };
  175. let inputs_amount_quote_unit = melt_request.proofs_amount().map_err(|_| {
  176. tracing::error!("Proof inputs in melt quote overflowed");
  177. Error::AmountOverflow
  178. })?;
  179. if amount_to_pay + melt_quote.fee_reserve > inputs_amount_quote_unit {
  180. tracing::debug!(
  181. "Not enough inputs provided: {} msats needed {} msats",
  182. inputs_amount_quote_unit,
  183. amount_to_pay
  184. );
  185. return Err(Error::TransactionUnbalanced(
  186. inputs_amount_quote_unit.into(),
  187. amount_to_pay.into(),
  188. melt_quote.fee_reserve.into(),
  189. ));
  190. }
  191. Ok(partial_amount)
  192. }
  193. /// Verify melt request is valid
  194. #[instrument(skip_all)]
  195. pub async fn verify_melt_request(
  196. &self,
  197. melt_request: &MeltBolt11Request<Uuid>,
  198. ) -> Result<MeltQuote, Error> {
  199. let state = self
  200. .localstore
  201. .update_melt_quote_state(&melt_request.quote, MeltQuoteState::Pending)
  202. .await?;
  203. match state {
  204. MeltQuoteState::Unpaid | MeltQuoteState::Failed => Ok(()),
  205. MeltQuoteState::Pending => Err(Error::PendingQuote),
  206. MeltQuoteState::Paid => Err(Error::PaidQuote),
  207. MeltQuoteState::Unknown => Err(Error::UnknownPaymentState),
  208. }?;
  209. let ys = melt_request.inputs.ys()?;
  210. // Ensure proofs are unique and not being double spent
  211. if melt_request.inputs.len() != ys.iter().collect::<HashSet<_>>().len() {
  212. return Err(Error::DuplicateProofs);
  213. }
  214. self.localstore
  215. .add_proofs(melt_request.inputs.clone(), Some(melt_request.quote))
  216. .await?;
  217. self.check_ys_spendable(&ys, State::Pending).await?;
  218. for proof in &melt_request.inputs {
  219. self.verify_proof(proof).await?;
  220. }
  221. let quote = self
  222. .localstore
  223. .get_melt_quote(&melt_request.quote)
  224. .await?
  225. .ok_or(Error::UnknownQuote)?;
  226. let proofs_total = melt_request.proofs_amount()?;
  227. let fee = self.get_proofs_fee(&melt_request.inputs).await?;
  228. let required_total = quote.amount + quote.fee_reserve + fee;
  229. // Check that the inputs proofs are greater then total.
  230. // Transaction does not need to be balanced as wallet may not want change.
  231. if proofs_total < required_total {
  232. tracing::info!(
  233. "Swap request unbalanced: {}, outputs {}, fee {}",
  234. proofs_total,
  235. quote.amount,
  236. fee
  237. );
  238. return Err(Error::TransactionUnbalanced(
  239. proofs_total.into(),
  240. quote.amount.into(),
  241. (fee + quote.fee_reserve).into(),
  242. ));
  243. }
  244. let input_keyset_ids: HashSet<Id> =
  245. melt_request.inputs.iter().map(|p| p.keyset_id).collect();
  246. let mut keyset_units = HashSet::with_capacity(input_keyset_ids.capacity());
  247. for id in input_keyset_ids {
  248. let keyset = self
  249. .localstore
  250. .get_keyset_info(&id)
  251. .await?
  252. .ok_or(Error::UnknownKeySet)?;
  253. keyset_units.insert(keyset.unit);
  254. }
  255. let EnforceSigFlag { sig_flag, .. } = enforce_sig_flag(melt_request.inputs.clone());
  256. if sig_flag.eq(&SigFlag::SigAll) {
  257. return Err(Error::SigAllUsedInMelt);
  258. }
  259. if let Some(outputs) = &melt_request.outputs {
  260. let output_keysets_ids: HashSet<Id> = outputs.iter().map(|b| b.keyset_id).collect();
  261. for id in output_keysets_ids {
  262. let keyset = self
  263. .localstore
  264. .get_keyset_info(&id)
  265. .await?
  266. .ok_or(Error::UnknownKeySet)?;
  267. // Get the active keyset for the unit
  268. let active_keyset_id = self
  269. .localstore
  270. .get_active_keyset_id(&keyset.unit)
  271. .await?
  272. .ok_or(Error::InactiveKeyset)?;
  273. // Check output is for current active keyset
  274. if id.ne(&active_keyset_id) {
  275. return Err(Error::InactiveKeyset);
  276. }
  277. keyset_units.insert(keyset.unit);
  278. }
  279. }
  280. // Check that all input and output proofs are the same unit
  281. if keyset_units.len().gt(&1) {
  282. return Err(Error::MultipleUnits);
  283. }
  284. tracing::debug!("Verified melt quote: {}", melt_request.quote);
  285. Ok(quote)
  286. }
  287. /// Process unpaid melt request
  288. /// In the event that a melt request fails and the lighthing payment is not
  289. /// made The [`Proofs`] should be returned to an unspent state and the
  290. /// quote should be unpaid
  291. #[instrument(skip_all)]
  292. pub async fn process_unpaid_melt(
  293. &self,
  294. melt_request: &MeltBolt11Request<Uuid>,
  295. ) -> Result<(), Error> {
  296. let input_ys = melt_request.inputs.ys()?;
  297. self.localstore
  298. .update_proofs_states(&input_ys, State::Unspent)
  299. .await?;
  300. self.localstore
  301. .update_melt_quote_state(&melt_request.quote, MeltQuoteState::Unpaid)
  302. .await?;
  303. if let Ok(Some(quote)) = self.localstore.get_melt_quote(&melt_request.quote).await {
  304. self.pubsub_manager
  305. .melt_quote_status(&quote, None, None, MeltQuoteState::Unpaid);
  306. }
  307. for public_key in input_ys {
  308. self.pubsub_manager
  309. .proof_state((public_key, State::Unspent));
  310. }
  311. Ok(())
  312. }
  313. /// Melt Bolt11
  314. #[instrument(skip_all)]
  315. pub async fn melt_bolt11(
  316. &self,
  317. melt_request: &MeltBolt11Request<Uuid>,
  318. ) -> Result<MeltQuoteBolt11Response<Uuid>, Error> {
  319. use std::sync::Arc;
  320. async fn check_payment_state(
  321. ln: Arc<dyn MintLightning<Err = cdk_lightning::Error> + Send + Sync>,
  322. melt_quote: &MeltQuote,
  323. ) -> anyhow::Result<PayInvoiceResponse> {
  324. match ln
  325. .check_outgoing_payment(&melt_quote.request_lookup_id)
  326. .await
  327. {
  328. Ok(response) => Ok(response),
  329. Err(check_err) => {
  330. // If we cannot check the status of the payment we keep the proofs stuck as pending.
  331. tracing::error!(
  332. "Could not check the status of payment for {},. Proofs stuck as pending",
  333. melt_quote.id
  334. );
  335. tracing::error!("Checking payment error: {}", check_err);
  336. bail!("Could not check payment status")
  337. }
  338. }
  339. }
  340. let quote = match self.verify_melt_request(melt_request).await {
  341. Ok(quote) => quote,
  342. Err(err) => {
  343. tracing::debug!("Error attempting to verify melt quote: {}", err);
  344. if let Err(err) = self.process_unpaid_melt(melt_request).await {
  345. tracing::error!(
  346. "Could not reset melt quote {} state: {}",
  347. melt_request.quote,
  348. err
  349. );
  350. }
  351. return Err(err);
  352. }
  353. };
  354. let settled_internally_amount =
  355. match self.handle_internal_melt_mint(&quote, melt_request).await {
  356. Ok(amount) => amount,
  357. Err(err) => {
  358. tracing::error!("Attempting to settle internally failed");
  359. if let Err(err) = self.process_unpaid_melt(melt_request).await {
  360. tracing::error!(
  361. "Could not reset melt quote {} state: {}",
  362. melt_request.quote,
  363. err
  364. );
  365. }
  366. return Err(err);
  367. }
  368. };
  369. let (preimage, amount_spent_quote_unit) = match settled_internally_amount {
  370. Some(amount_spent) => (None, amount_spent),
  371. None => {
  372. // If the quote unit is SAT or MSAT we can check that the expected fees are
  373. // provided. We also check if the quote is less then the invoice
  374. // amount in the case that it is a mmp However, if the quote is not
  375. // of a bitcoin unit we cannot do these checks as the mint
  376. // is unaware of a conversion rate. In this case it is assumed that the quote is
  377. // correct and the mint should pay the full invoice amount if inputs
  378. // > `then quote.amount` are included. This is checked in the
  379. // `verify_melt` method.
  380. let partial_amount = match quote.unit {
  381. CurrencyUnit::Sat | CurrencyUnit::Msat => {
  382. match self.check_melt_expected_ln_fees(&quote, melt_request).await {
  383. Ok(amount) => amount,
  384. Err(err) => {
  385. tracing::error!("Fee is not expected: {}", err);
  386. if let Err(err) = self.process_unpaid_melt(melt_request).await {
  387. tracing::error!("Could not reset melt quote state: {}", err);
  388. }
  389. return Err(Error::Internal);
  390. }
  391. }
  392. }
  393. _ => None,
  394. };
  395. let ln = match self
  396. .ln
  397. .get(&LnKey::new(quote.unit.clone(), PaymentMethod::Bolt11))
  398. {
  399. Some(ln) => ln,
  400. None => {
  401. tracing::info!("Could not get ln backend for {}, bolt11 ", quote.unit);
  402. if let Err(err) = self.process_unpaid_melt(melt_request).await {
  403. tracing::error!("Could not reset melt quote state: {}", err);
  404. }
  405. return Err(Error::UnitUnsupported);
  406. }
  407. };
  408. let pre = match ln
  409. .pay_invoice(quote.clone(), partial_amount, Some(quote.fee_reserve))
  410. .await
  411. {
  412. Ok(pay)
  413. if pay.status == MeltQuoteState::Unknown
  414. || pay.status == MeltQuoteState::Failed =>
  415. {
  416. let check_response = check_payment_state(Arc::clone(ln), &quote)
  417. .await
  418. .map_err(|_| Error::Internal)?;
  419. if check_response.status == MeltQuoteState::Paid {
  420. tracing::warn!("Pay invoice returned {} but check returned {}. Proofs stuck as pending", pay.status.to_string(), check_response.status.to_string());
  421. return Err(Error::Internal);
  422. }
  423. check_response
  424. }
  425. Ok(pay) => pay,
  426. Err(err) => {
  427. // If the error is that the invoice was already paid we do not want to hold
  428. // hold the proofs as pending to we reset them and return an error.
  429. if matches!(err, cdk_lightning::Error::InvoiceAlreadyPaid) {
  430. tracing::debug!("Invoice already paid, resetting melt quote");
  431. if let Err(err) = self.process_unpaid_melt(melt_request).await {
  432. tracing::error!("Could not reset melt quote state: {}", err);
  433. }
  434. return Err(Error::RequestAlreadyPaid);
  435. }
  436. tracing::error!("Error returned attempting to pay: {} {}", quote.id, err);
  437. let check_response = check_payment_state(Arc::clone(ln), &quote)
  438. .await
  439. .map_err(|_| Error::Internal)?;
  440. // If there error is something else we want to check the status of the payment ensure it is not pending or has been made.
  441. if check_response.status == MeltQuoteState::Paid {
  442. tracing::warn!("Pay invoice returned an error but check returned {}. Proofs stuck as pending", check_response.status.to_string());
  443. return Err(Error::Internal);
  444. }
  445. check_response
  446. }
  447. };
  448. match pre.status {
  449. MeltQuoteState::Paid => (),
  450. MeltQuoteState::Unpaid | MeltQuoteState::Unknown | MeltQuoteState::Failed => {
  451. tracing::info!(
  452. "Lightning payment for quote {} failed.",
  453. melt_request.quote
  454. );
  455. if let Err(err) = self.process_unpaid_melt(melt_request).await {
  456. tracing::error!("Could not reset melt quote state: {}", err);
  457. }
  458. return Err(Error::PaymentFailed);
  459. }
  460. MeltQuoteState::Pending => {
  461. tracing::warn!(
  462. "LN payment pending, proofs are stuck as pending for quote: {}",
  463. melt_request.quote
  464. );
  465. return Err(Error::PendingQuote);
  466. }
  467. }
  468. // Convert from unit of backend to quote unit
  469. // Note: this should never fail since these conversions happen earlier and would fail there.
  470. // Since it will not fail and even if it does the ln payment has already been paid, proofs should still be burned
  471. let amount_spent =
  472. to_unit(pre.total_spent, &pre.unit, &quote.unit).unwrap_or_default();
  473. let payment_lookup_id = pre.payment_lookup_id;
  474. if payment_lookup_id != quote.request_lookup_id {
  475. tracing::info!(
  476. "Payment lookup id changed post payment from {} to {}",
  477. quote.request_lookup_id,
  478. payment_lookup_id
  479. );
  480. let mut melt_quote = quote;
  481. melt_quote.request_lookup_id = payment_lookup_id;
  482. if let Err(err) = self.localstore.add_melt_quote(melt_quote).await {
  483. tracing::warn!("Could not update payment lookup id: {}", err);
  484. }
  485. }
  486. (pre.payment_preimage, amount_spent)
  487. }
  488. };
  489. // If we made it here the payment has been made.
  490. // We process the melt burning the inputs and returning change
  491. let res = self
  492. .process_melt_request(melt_request, preimage, amount_spent_quote_unit)
  493. .await
  494. .map_err(|err| {
  495. tracing::error!("Could not process melt request: {}", err);
  496. err
  497. })?;
  498. Ok(res)
  499. }
  500. /// Process melt request marking [`Proofs`] as spent
  501. /// The melt request must be verifyed using [`Self::verify_melt_request`]
  502. /// before calling [`Self::process_melt_request`]
  503. #[instrument(skip_all)]
  504. pub async fn process_melt_request(
  505. &self,
  506. melt_request: &MeltBolt11Request<Uuid>,
  507. payment_preimage: Option<String>,
  508. total_spent: Amount,
  509. ) -> Result<MeltQuoteBolt11Response<Uuid>, Error> {
  510. tracing::debug!("Processing melt quote: {}", melt_request.quote);
  511. let quote = self
  512. .localstore
  513. .get_melt_quote(&melt_request.quote)
  514. .await?
  515. .ok_or(Error::UnknownQuote)?;
  516. let input_ys = melt_request.inputs.ys()?;
  517. self.localstore
  518. .update_proofs_states(&input_ys, State::Spent)
  519. .await?;
  520. self.localstore
  521. .update_melt_quote_state(&melt_request.quote, MeltQuoteState::Paid)
  522. .await?;
  523. self.pubsub_manager.melt_quote_status(
  524. &quote,
  525. payment_preimage.clone(),
  526. None,
  527. MeltQuoteState::Paid,
  528. );
  529. for public_key in input_ys {
  530. self.pubsub_manager.proof_state((public_key, State::Spent));
  531. }
  532. let mut change = None;
  533. // Check if there is change to return
  534. if melt_request.proofs_amount()? > total_spent {
  535. // Check if wallet provided change outputs
  536. if let Some(outputs) = melt_request.outputs.clone() {
  537. let blinded_messages: Vec<PublicKey> =
  538. outputs.iter().map(|b| b.blinded_secret).collect();
  539. if self
  540. .localstore
  541. .get_blind_signatures(&blinded_messages)
  542. .await?
  543. .iter()
  544. .flatten()
  545. .next()
  546. .is_some()
  547. {
  548. tracing::info!("Output has already been signed");
  549. return Err(Error::BlindedMessageAlreadySigned);
  550. }
  551. let change_target = melt_request.proofs_amount()? - total_spent;
  552. let mut amounts = change_target.split();
  553. let mut change_sigs = Vec::with_capacity(amounts.len());
  554. if outputs.len().lt(&amounts.len()) {
  555. tracing::debug!(
  556. "Providing change requires {} blinded messages, but only {} provided",
  557. amounts.len(),
  558. outputs.len()
  559. );
  560. // In the case that not enough outputs are provided to return all change
  561. // Reverse sort the amounts so that the most amount of change possible is
  562. // returned. The rest is burnt
  563. amounts.sort_by(|a, b| b.cmp(a));
  564. }
  565. let mut outputs = outputs;
  566. for (amount, blinded_message) in amounts.iter().zip(&mut outputs) {
  567. blinded_message.amount = *amount;
  568. let blinded_signature = self.blind_sign(blinded_message).await?;
  569. change_sigs.push(blinded_signature)
  570. }
  571. self.localstore
  572. .add_blind_signatures(
  573. &outputs[0..change_sigs.len()]
  574. .iter()
  575. .map(|o| o.blinded_secret)
  576. .collect::<Vec<PublicKey>>(),
  577. &change_sigs,
  578. Some(quote.id),
  579. )
  580. .await?;
  581. change = Some(change_sigs);
  582. }
  583. }
  584. Ok(MeltQuoteBolt11Response {
  585. amount: quote.amount,
  586. paid: Some(true),
  587. payment_preimage,
  588. change,
  589. quote: quote.id,
  590. fee_reserve: quote.fee_reserve,
  591. state: MeltQuoteState::Paid,
  592. expiry: quote.expiry,
  593. })
  594. }
  595. }