server.rs 26 KB

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