server.rs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763
  1. use std::net::SocketAddr;
  2. use std::path::PathBuf;
  3. use std::str::FromStr;
  4. use std::sync::Arc;
  5. use cdk::mint::{Mint, MintQuote};
  6. use cdk::nuts::nut04::MintMethodSettings;
  7. use cdk::nuts::nut05::MeltMethodSettings;
  8. use cdk::nuts::{CurrencyUnit, MintQuoteState, PaymentMethod};
  9. use cdk::types::QuoteTTL;
  10. use cdk::Amount;
  11. use cdk_common::payment::WaitPaymentResponse;
  12. use thiserror::Error;
  13. use tokio::sync::Notify;
  14. use tokio::task::JoinHandle;
  15. use tokio::time::Duration;
  16. use tonic::transport::{Certificate, Identity, Server, ServerTlsConfig};
  17. use tonic::{Request, Response, Status};
  18. use crate::cdk_mint_server::{CdkMint, CdkMintServer};
  19. use crate::{
  20. ContactInfo, GetInfoRequest, GetInfoResponse, GetQuoteTtlRequest, GetQuoteTtlResponse,
  21. RotateNextKeysetRequest, RotateNextKeysetResponse, UpdateContactRequest,
  22. UpdateDescriptionRequest, UpdateIconUrlRequest, UpdateMotdRequest, UpdateNameRequest,
  23. UpdateNut04QuoteRequest, UpdateNut04Request, UpdateNut05Request, UpdateQuoteTtlRequest,
  24. UpdateResponse, UpdateUrlRequest,
  25. };
  26. /// Error
  27. #[derive(Debug, Error)]
  28. pub enum Error {
  29. /// Parse error
  30. #[error(transparent)]
  31. Parse(#[from] std::net::AddrParseError),
  32. /// Transport error
  33. #[error(transparent)]
  34. Transport(#[from] tonic::transport::Error),
  35. /// Io error
  36. #[error(transparent)]
  37. Io(#[from] std::io::Error),
  38. }
  39. /// CDK Mint RPC Server
  40. #[derive(Clone)]
  41. #[allow(missing_debug_implementations)]
  42. pub struct MintRPCServer {
  43. socket_addr: SocketAddr,
  44. mint: Arc<Mint>,
  45. shutdown: Arc<Notify>,
  46. handle: Option<Arc<JoinHandle<Result<(), Error>>>>,
  47. }
  48. impl MintRPCServer {
  49. /// Creates a new MintRPCServer instance
  50. ///
  51. /// # Arguments
  52. /// * `addr` - The address to bind to
  53. /// * `port` - The port to listen on
  54. /// * `mint` - The Mint instance to serve
  55. pub fn new(addr: &str, port: u16, mint: Arc<Mint>) -> Result<Self, Error> {
  56. Ok(Self {
  57. socket_addr: format!("{addr}:{port}").parse()?,
  58. mint,
  59. shutdown: Arc::new(Notify::new()),
  60. handle: None,
  61. })
  62. }
  63. /// Starts the RPC server
  64. ///
  65. /// # Arguments
  66. /// * `tls_dir` - Optional directory containing TLS certificates
  67. ///
  68. /// If TLS directory is provided, it must contain:
  69. /// - server.pem: Server certificate
  70. /// - server.key: Server private key
  71. /// - ca.pem: CA certificate for client authentication
  72. pub async fn start(&mut self, tls_dir: Option<PathBuf>) -> Result<(), Error> {
  73. tracing::info!("Starting RPC server {}", self.socket_addr);
  74. #[cfg(not(target_arch = "wasm32"))]
  75. if rustls::crypto::CryptoProvider::get_default().is_none() {
  76. let _ = rustls::crypto::ring::default_provider().install_default();
  77. }
  78. let server = match tls_dir {
  79. Some(tls_dir) => {
  80. tracing::info!("TLS configuration found, starting secure server");
  81. let server_pem_path = tls_dir.join("server.pem");
  82. let server_key_path = tls_dir.join("server.key");
  83. let ca_pem_path = tls_dir.join("ca.pem");
  84. if !server_pem_path.exists() {
  85. tracing::error!(
  86. "Server certificate file does not exist: {}",
  87. server_pem_path.display()
  88. );
  89. return Err(Error::Io(std::io::Error::new(
  90. std::io::ErrorKind::NotFound,
  91. format!(
  92. "Server certificate file not found: {}",
  93. server_pem_path.display()
  94. ),
  95. )));
  96. }
  97. if !server_key_path.exists() {
  98. tracing::error!(
  99. "Server key file does not exist: {}",
  100. server_key_path.display()
  101. );
  102. return Err(Error::Io(std::io::Error::new(
  103. std::io::ErrorKind::NotFound,
  104. format!("Server key file not found: {}", server_key_path.display()),
  105. )));
  106. }
  107. if !ca_pem_path.exists() {
  108. tracing::error!(
  109. "CA certificate file does not exist: {}",
  110. ca_pem_path.display()
  111. );
  112. return Err(Error::Io(std::io::Error::new(
  113. std::io::ErrorKind::NotFound,
  114. format!("CA certificate file not found: {}", ca_pem_path.display()),
  115. )));
  116. }
  117. let cert = std::fs::read_to_string(&server_pem_path)?;
  118. let key = std::fs::read_to_string(&server_key_path)?;
  119. let client_ca_cert = std::fs::read_to_string(&ca_pem_path)?;
  120. let client_ca_cert = Certificate::from_pem(client_ca_cert);
  121. let server_identity = Identity::from_pem(cert, key);
  122. let tls_config = ServerTlsConfig::new()
  123. .identity(server_identity)
  124. .client_ca_root(client_ca_cert);
  125. Server::builder()
  126. .tls_config(tls_config)?
  127. .add_service(CdkMintServer::new(self.clone()))
  128. }
  129. None => {
  130. tracing::warn!("No valid TLS configuration found, starting insecure server");
  131. Server::builder().add_service(CdkMintServer::new(self.clone()))
  132. }
  133. };
  134. let shutdown = self.shutdown.clone();
  135. let addr = self.socket_addr;
  136. self.handle = Some(Arc::new(tokio::spawn(async move {
  137. let server = server.serve_with_shutdown(addr, async {
  138. shutdown.notified().await;
  139. });
  140. server.await?;
  141. Ok(())
  142. })));
  143. Ok(())
  144. }
  145. /// Stops the RPC server gracefully
  146. pub async fn stop(&self) -> Result<(), Error> {
  147. self.shutdown.notify_one();
  148. if let Some(handle) = &self.handle {
  149. while !handle.is_finished() {
  150. tracing::info!("Waitning for mint rpc server to stop");
  151. tokio::time::sleep(Duration::from_millis(100)).await;
  152. }
  153. }
  154. tracing::info!("Mint rpc server stopped");
  155. Ok(())
  156. }
  157. }
  158. impl Drop for MintRPCServer {
  159. fn drop(&mut self) {
  160. tracing::debug!("Dropping mint rpc server");
  161. self.shutdown.notify_one();
  162. }
  163. }
  164. #[tonic::async_trait]
  165. impl CdkMint for MintRPCServer {
  166. /// Returns information about the mint
  167. async fn get_info(
  168. &self,
  169. _request: Request<GetInfoRequest>,
  170. ) -> Result<Response<GetInfoResponse>, Status> {
  171. let info = self
  172. .mint
  173. .mint_info()
  174. .await
  175. .map_err(|err| Status::internal(err.to_string()))?;
  176. let total_issued = self
  177. .mint
  178. .total_issued()
  179. .await
  180. .map_err(|err| Status::internal(err.to_string()))?;
  181. let total_issued: Amount = Amount::try_sum(total_issued.values().cloned())
  182. .map_err(|_| Status::internal("Overflow".to_string()))?;
  183. let total_redeemed = self
  184. .mint
  185. .total_redeemed()
  186. .await
  187. .map_err(|err| Status::internal(err.to_string()))?;
  188. let total_redeemed: Amount = Amount::try_sum(total_redeemed.values().cloned())
  189. .map_err(|_| Status::internal("Overflow".to_string()))?;
  190. let contact = info
  191. .contact
  192. .unwrap_or_default()
  193. .into_iter()
  194. .map(|c| ContactInfo {
  195. method: c.method,
  196. info: c.info,
  197. })
  198. .collect();
  199. Ok(Response::new(GetInfoResponse {
  200. name: info.name,
  201. description: info.description,
  202. long_description: info.description_long,
  203. version: info.version.map(|v| v.to_string()),
  204. contact,
  205. motd: info.motd,
  206. icon_url: info.icon_url,
  207. urls: info.urls.unwrap_or_default(),
  208. total_issued: total_issued.into(),
  209. total_redeemed: total_redeemed.into(),
  210. }))
  211. }
  212. /// Updates the mint's message of the day
  213. async fn update_motd(
  214. &self,
  215. request: Request<UpdateMotdRequest>,
  216. ) -> Result<Response<UpdateResponse>, Status> {
  217. let motd = request.into_inner().motd;
  218. let mut info = self
  219. .mint
  220. .mint_info()
  221. .await
  222. .map_err(|err| Status::internal(err.to_string()))?;
  223. info.motd = Some(motd);
  224. self.mint
  225. .set_mint_info(info)
  226. .await
  227. .map_err(|err| Status::internal(err.to_string()))?;
  228. Ok(Response::new(UpdateResponse {}))
  229. }
  230. /// Updates the mint's short description
  231. async fn update_short_description(
  232. &self,
  233. request: Request<UpdateDescriptionRequest>,
  234. ) -> Result<Response<UpdateResponse>, Status> {
  235. let description = request.into_inner().description;
  236. let mut info = self
  237. .mint
  238. .mint_info()
  239. .await
  240. .map_err(|err| Status::internal(err.to_string()))?;
  241. info.description = Some(description);
  242. self.mint
  243. .set_mint_info(info)
  244. .await
  245. .map_err(|err| Status::internal(err.to_string()))?;
  246. Ok(Response::new(UpdateResponse {}))
  247. }
  248. /// Updates the mint's long description
  249. async fn update_long_description(
  250. &self,
  251. request: Request<UpdateDescriptionRequest>,
  252. ) -> Result<Response<UpdateResponse>, Status> {
  253. let description = request.into_inner().description;
  254. let mut info = self
  255. .mint
  256. .mint_info()
  257. .await
  258. .map_err(|err| Status::internal(err.to_string()))?;
  259. info.description_long = Some(description);
  260. self.mint
  261. .set_mint_info(info)
  262. .await
  263. .map_err(|err| Status::internal(err.to_string()))?;
  264. Ok(Response::new(UpdateResponse {}))
  265. }
  266. /// Updates the mint's name
  267. async fn update_name(
  268. &self,
  269. request: Request<UpdateNameRequest>,
  270. ) -> Result<Response<UpdateResponse>, Status> {
  271. let name = request.into_inner().name;
  272. let mut info = self
  273. .mint
  274. .mint_info()
  275. .await
  276. .map_err(|err| Status::internal(err.to_string()))?;
  277. info.name = Some(name);
  278. self.mint
  279. .set_mint_info(info)
  280. .await
  281. .map_err(|err| Status::internal(err.to_string()))?;
  282. Ok(Response::new(UpdateResponse {}))
  283. }
  284. /// Updates the mint's icon URL
  285. async fn update_icon_url(
  286. &self,
  287. request: Request<UpdateIconUrlRequest>,
  288. ) -> Result<Response<UpdateResponse>, Status> {
  289. let icon_url = request.into_inner().icon_url;
  290. let mut info = self
  291. .mint
  292. .mint_info()
  293. .await
  294. .map_err(|err| Status::internal(err.to_string()))?;
  295. info.icon_url = Some(icon_url);
  296. self.mint
  297. .set_mint_info(info)
  298. .await
  299. .map_err(|err| Status::internal(err.to_string()))?;
  300. Ok(Response::new(UpdateResponse {}))
  301. }
  302. /// Adds a URL to the mint's list of URLs
  303. async fn add_url(
  304. &self,
  305. request: Request<UpdateUrlRequest>,
  306. ) -> Result<Response<UpdateResponse>, Status> {
  307. let url = request.into_inner().url;
  308. let mut info = self
  309. .mint
  310. .mint_info()
  311. .await
  312. .map_err(|err| Status::internal(err.to_string()))?;
  313. let mut urls = info.urls.unwrap_or_default();
  314. urls.push(url);
  315. info.urls = Some(urls.clone());
  316. self.mint
  317. .set_mint_info(info)
  318. .await
  319. .map_err(|err| Status::internal(err.to_string()))?;
  320. Ok(Response::new(UpdateResponse {}))
  321. }
  322. /// Removes a URL from the mint's list of URLs
  323. async fn remove_url(
  324. &self,
  325. request: Request<UpdateUrlRequest>,
  326. ) -> Result<Response<UpdateResponse>, Status> {
  327. let url = request.into_inner().url;
  328. let mut info = self
  329. .mint
  330. .mint_info()
  331. .await
  332. .map_err(|err| Status::internal(err.to_string()))?;
  333. let urls = info.urls;
  334. let mut urls = urls.clone().unwrap_or_default();
  335. urls.retain(|u| u != &url);
  336. let urls = if urls.is_empty() { None } else { Some(urls) };
  337. info.urls = urls;
  338. self.mint
  339. .set_mint_info(info)
  340. .await
  341. .map_err(|err| Status::internal(err.to_string()))?;
  342. Ok(Response::new(UpdateResponse {}))
  343. }
  344. /// Adds a contact method to the mint's contact information
  345. async fn add_contact(
  346. &self,
  347. request: Request<UpdateContactRequest>,
  348. ) -> Result<Response<UpdateResponse>, Status> {
  349. let request_inner = request.into_inner();
  350. let mut info = self
  351. .mint
  352. .mint_info()
  353. .await
  354. .map_err(|err| Status::internal(err.to_string()))?;
  355. info.contact
  356. .get_or_insert_with(Vec::new)
  357. .push(cdk::nuts::ContactInfo::new(
  358. request_inner.method,
  359. request_inner.info,
  360. ));
  361. self.mint
  362. .set_mint_info(info)
  363. .await
  364. .map_err(|err| Status::internal(err.to_string()))?;
  365. Ok(Response::new(UpdateResponse {}))
  366. }
  367. /// Removes a contact method from the mint's contact information
  368. async fn remove_contact(
  369. &self,
  370. request: Request<UpdateContactRequest>,
  371. ) -> Result<Response<UpdateResponse>, Status> {
  372. let request_inner = request.into_inner();
  373. let mut info = self
  374. .mint
  375. .mint_info()
  376. .await
  377. .map_err(|err| Status::internal(err.to_string()))?;
  378. if let Some(contact) = info.contact.as_mut() {
  379. let contact_info =
  380. cdk::nuts::ContactInfo::new(request_inner.method, request_inner.info);
  381. contact.retain(|x| x != &contact_info);
  382. self.mint
  383. .set_mint_info(info)
  384. .await
  385. .map_err(|err| Status::internal(err.to_string()))?;
  386. }
  387. Ok(Response::new(UpdateResponse {}))
  388. }
  389. /// Updates the mint's NUT-04 (mint) settings
  390. async fn update_nut04(
  391. &self,
  392. request: Request<UpdateNut04Request>,
  393. ) -> Result<Response<UpdateResponse>, Status> {
  394. let mut info = self
  395. .mint
  396. .mint_info()
  397. .await
  398. .map_err(|err| Status::internal(err.to_string()))?;
  399. let mut nut04_settings = info.nuts.nut04.clone();
  400. let request_inner = request.into_inner();
  401. let unit = CurrencyUnit::from_str(&request_inner.unit)
  402. .map_err(|_| Status::invalid_argument("Invalid unit".to_string()))?;
  403. let payment_method = PaymentMethod::from_str(&request_inner.method)
  404. .map_err(|_| Status::invalid_argument("Invalid method".to_string()))?;
  405. self.mint
  406. .get_payment_processor(unit.clone(), payment_method.clone())
  407. .map_err(|_| Status::invalid_argument("Unit payment method pair is not supported"))?;
  408. let current_nut04_settings = nut04_settings.remove_settings(&unit, &payment_method);
  409. let mut methods = nut04_settings.methods.clone();
  410. // Create options from the request
  411. let options = if let Some(options) = request_inner.options {
  412. Some(cdk::nuts::nut04::MintMethodOptions::Bolt11 {
  413. description: options.description,
  414. })
  415. } else if let Some(current_settings) = current_nut04_settings.as_ref() {
  416. current_settings.options.clone()
  417. } else {
  418. None
  419. };
  420. let updated_method_settings = MintMethodSettings {
  421. method: payment_method,
  422. unit,
  423. min_amount: request_inner
  424. .min_amount
  425. .map(Amount::from)
  426. .or_else(|| current_nut04_settings.as_ref().and_then(|s| s.min_amount)),
  427. max_amount: request_inner
  428. .max_amount
  429. .map(Amount::from)
  430. .or_else(|| current_nut04_settings.as_ref().and_then(|s| s.max_amount)),
  431. options,
  432. };
  433. methods.push(updated_method_settings);
  434. nut04_settings.methods = methods;
  435. if let Some(disabled) = request_inner.disabled {
  436. nut04_settings.disabled = disabled;
  437. }
  438. info.nuts.nut04 = nut04_settings;
  439. self.mint
  440. .set_mint_info(info)
  441. .await
  442. .map_err(|err| Status::internal(err.to_string()))?;
  443. Ok(Response::new(UpdateResponse {}))
  444. }
  445. /// Updates the mint's NUT-05 (melt) settings
  446. async fn update_nut05(
  447. &self,
  448. request: Request<UpdateNut05Request>,
  449. ) -> Result<Response<UpdateResponse>, Status> {
  450. let mut info = self
  451. .mint
  452. .mint_info()
  453. .await
  454. .map_err(|err| Status::internal(err.to_string()))?;
  455. let mut nut05_settings = info.nuts.nut05.clone();
  456. let request_inner = request.into_inner();
  457. let unit = CurrencyUnit::from_str(&request_inner.unit)
  458. .map_err(|_| Status::invalid_argument("Invalid unit".to_string()))?;
  459. let payment_method = PaymentMethod::from_str(&request_inner.method)
  460. .map_err(|_| Status::invalid_argument("Invalid method".to_string()))?;
  461. self.mint
  462. .get_payment_processor(unit.clone(), payment_method.clone())
  463. .map_err(|_| Status::invalid_argument("Unit payment method pair is not supported"))?;
  464. let current_nut05_settings = nut05_settings.remove_settings(&unit, &payment_method);
  465. let mut methods = nut05_settings.methods;
  466. // Create options from the request
  467. let options = if let Some(options) = request_inner.options {
  468. Some(cdk::nuts::nut05::MeltMethodOptions::Bolt11 {
  469. amountless: options.amountless,
  470. })
  471. } else if let Some(current_settings) = current_nut05_settings.as_ref() {
  472. current_settings.options.clone()
  473. } else {
  474. None
  475. };
  476. let updated_method_settings = MeltMethodSettings {
  477. method: payment_method,
  478. unit,
  479. min_amount: request_inner
  480. .min_amount
  481. .map(Amount::from)
  482. .or_else(|| current_nut05_settings.as_ref().and_then(|s| s.min_amount)),
  483. max_amount: request_inner
  484. .max_amount
  485. .map(Amount::from)
  486. .or_else(|| current_nut05_settings.as_ref().and_then(|s| s.max_amount)),
  487. options,
  488. };
  489. methods.push(updated_method_settings);
  490. nut05_settings.methods = methods;
  491. if let Some(disabled) = request_inner.disabled {
  492. nut05_settings.disabled = disabled;
  493. }
  494. info.nuts.nut05 = nut05_settings;
  495. self.mint
  496. .set_mint_info(info)
  497. .await
  498. .map_err(|err| Status::internal(err.to_string()))?;
  499. Ok(Response::new(UpdateResponse {}))
  500. }
  501. /// Updates the mint's quote time-to-live settings
  502. async fn update_quote_ttl(
  503. &self,
  504. request: Request<UpdateQuoteTtlRequest>,
  505. ) -> Result<Response<UpdateResponse>, Status> {
  506. let current_ttl = self
  507. .mint
  508. .quote_ttl()
  509. .await
  510. .map_err(|err| Status::internal(err.to_string()))?;
  511. let request = request.into_inner();
  512. let quote_ttl = QuoteTTL {
  513. mint_ttl: request.mint_ttl.unwrap_or(current_ttl.mint_ttl),
  514. melt_ttl: request.melt_ttl.unwrap_or(current_ttl.melt_ttl),
  515. };
  516. self.mint
  517. .set_quote_ttl(quote_ttl)
  518. .await
  519. .map_err(|err| Status::internal(err.to_string()))?;
  520. Ok(Response::new(UpdateResponse {}))
  521. }
  522. /// Gets the mint's quote time-to-live settings
  523. async fn get_quote_ttl(
  524. &self,
  525. _request: Request<GetQuoteTtlRequest>,
  526. ) -> Result<Response<GetQuoteTtlResponse>, Status> {
  527. let ttl = self
  528. .mint
  529. .quote_ttl()
  530. .await
  531. .map_err(|err| Status::internal(err.to_string()))?;
  532. Ok(Response::new(GetQuoteTtlResponse {
  533. mint_ttl: ttl.mint_ttl,
  534. melt_ttl: ttl.melt_ttl,
  535. }))
  536. }
  537. /// Updates a specific NUT-04 quote's state
  538. async fn update_nut04_quote(
  539. &self,
  540. request: Request<UpdateNut04QuoteRequest>,
  541. ) -> Result<Response<UpdateNut04QuoteRequest>, Status> {
  542. let request = request.into_inner();
  543. let quote_id = request
  544. .quote_id
  545. .parse()
  546. .map_err(|_| Status::invalid_argument("Invalid quote id".to_string()))?;
  547. let state = MintQuoteState::from_str(&request.state)
  548. .map_err(|_| Status::invalid_argument("Invalid quote state".to_string()))?;
  549. let mint_quote = self
  550. .mint
  551. .localstore()
  552. .get_mint_quote(&quote_id)
  553. .await
  554. .map_err(|_| Status::invalid_argument("Could not find quote".to_string()))?
  555. .ok_or(Status::invalid_argument("Could not find quote".to_string()))?;
  556. match state {
  557. MintQuoteState::Paid => {
  558. // Create a dummy payment response
  559. let response = WaitPaymentResponse {
  560. payment_id: mint_quote.request_lookup_id.to_string(),
  561. payment_amount: mint_quote.clone().amount.unwrap_or(cdk::Amount::new(
  562. mint_quote.amount_paid().value(),
  563. mint_quote.unit.clone(),
  564. )),
  565. payment_identifier: mint_quote.request_lookup_id.clone(),
  566. };
  567. let localstore = self.mint.localstore();
  568. let mut tx = localstore
  569. .begin_transaction()
  570. .await
  571. .map_err(|_| Status::internal("Could not start db transaction".to_string()))?;
  572. // Re-fetch the mint quote within the transaction to lock it
  573. let mut mint_quote = tx
  574. .get_mint_quote(&quote_id)
  575. .await
  576. .map_err(|_| {
  577. Status::internal("Could not get quote in transaction".to_string())
  578. })?
  579. .ok_or(Status::invalid_argument(
  580. "Quote not found in transaction".to_string(),
  581. ))?;
  582. self.mint
  583. .pay_mint_quote(&mut tx, &mut mint_quote, response)
  584. .await
  585. .map_err(|_| Status::internal("Could not process payment".to_string()))?;
  586. tx.commit()
  587. .await
  588. .map_err(|_| Status::internal("Could not commit db transaction".to_string()))?;
  589. }
  590. _ => {
  591. // Create a new quote with the same values
  592. let quote = MintQuote::new(
  593. Some(mint_quote.id.clone()), // id
  594. mint_quote.request.clone(), // request
  595. mint_quote.unit.clone(), // unit
  596. mint_quote.amount.clone(), // amount
  597. mint_quote.expiry, // expiry
  598. mint_quote.request_lookup_id.clone(), // request_lookup_id
  599. mint_quote.pubkey, // pubkey
  600. mint_quote.amount_issued(), // amount_issued
  601. mint_quote.amount_paid(), // amount_paid
  602. mint_quote.payment_method.clone(), // method
  603. 0, // created_at
  604. vec![], // blinded_messages
  605. vec![], // payment_ids
  606. None, // extra_json
  607. );
  608. let mint_store = self.mint.localstore();
  609. let mut tx = mint_store
  610. .begin_transaction()
  611. .await
  612. .map_err(|_| Status::internal("Could not update quote".to_string()))?;
  613. tx.add_mint_quote(quote.clone())
  614. .await
  615. .map_err(|_| Status::internal("Could not update quote".to_string()))?;
  616. tx.commit()
  617. .await
  618. .map_err(|_| Status::internal("Could not update quote".to_string()))?;
  619. }
  620. }
  621. let mint_quote = self
  622. .mint
  623. .localstore()
  624. .get_mint_quote(&quote_id)
  625. .await
  626. .map_err(|_| Status::invalid_argument("Could not find quote".to_string()))?
  627. .ok_or(Status::invalid_argument("Could not find quote".to_string()))?;
  628. Ok(Response::new(UpdateNut04QuoteRequest {
  629. state: mint_quote.state().to_string(),
  630. quote_id: mint_quote.id.to_string(),
  631. }))
  632. }
  633. /// Rotates to the next keyset for the specified currency unit
  634. async fn rotate_next_keyset(
  635. &self,
  636. request: Request<RotateNextKeysetRequest>,
  637. ) -> Result<Response<RotateNextKeysetResponse>, Status> {
  638. let request = request.into_inner();
  639. let unit = CurrencyUnit::from_str(&request.unit)
  640. .map_err(|_| Status::invalid_argument("Invalid unit".to_string()))?;
  641. let amounts = request.amounts;
  642. let keyset_info = self
  643. .mint
  644. .rotate_keyset(unit, amounts, request.input_fee_ppk.unwrap_or(0))
  645. .await
  646. .map_err(|_| Status::invalid_argument("Could not rotate keyset".to_string()))?;
  647. Ok(Response::new(RotateNextKeysetResponse {
  648. id: keyset_info.id.to_string(),
  649. unit: keyset_info.unit.to_string(),
  650. amounts: keyset_info.amounts,
  651. input_fee_ppk: keyset_info.input_fee_ppk,
  652. }))
  653. }
  654. }