server.rs 25 KB

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