server.rs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626
  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;
  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 thiserror::Error;
  12. use tokio::sync::Notify;
  13. use tokio::task::JoinHandle;
  14. use tokio::time::Duration;
  15. use tonic::transport::{Certificate, Identity, Server, ServerTlsConfig};
  16. use tonic::{Request, Response, Status};
  17. use crate::cdk_mint_server::{CdkMint, CdkMintServer};
  18. use crate::{
  19. ContactInfo, GetInfoRequest, GetInfoResponse, RotateNextKeysetRequest,
  20. RotateNextKeysetResponse, UpdateContactRequest, UpdateDescriptionRequest, UpdateIconUrlRequest,
  21. UpdateMotdRequest, UpdateNameRequest, UpdateNut04QuoteRequest, UpdateNut04Request,
  22. UpdateNut05Request, UpdateQuoteTtlRequest, UpdateResponse, UpdateUrlRequest,
  23. };
  24. /// Error
  25. #[derive(Debug, Error)]
  26. pub enum Error {
  27. /// Parse error
  28. #[error(transparent)]
  29. Parse(#[from] std::net::AddrParseError),
  30. /// Transport error
  31. #[error(transparent)]
  32. Transport(#[from] tonic::transport::Error),
  33. /// Io error
  34. #[error(transparent)]
  35. Io(#[from] std::io::Error),
  36. }
  37. /// CDK Mint RPC Server
  38. #[derive(Clone)]
  39. pub struct MintRPCServer {
  40. socket_addr: SocketAddr,
  41. mint: Arc<Mint>,
  42. shutdown: Arc<Notify>,
  43. handle: Option<Arc<JoinHandle<Result<(), Error>>>>,
  44. }
  45. impl MintRPCServer {
  46. pub fn new(addr: &str, port: u16, mint: Arc<Mint>) -> Result<Self, Error> {
  47. Ok(Self {
  48. socket_addr: format!("{addr}:{port}").parse()?,
  49. mint,
  50. shutdown: Arc::new(Notify::new()),
  51. handle: None,
  52. })
  53. }
  54. pub async fn start(&mut self, tls_dir: Option<PathBuf>) -> Result<(), Error> {
  55. tracing::info!("Starting RPC server {}", self.socket_addr);
  56. let server = match tls_dir {
  57. Some(tls_dir) => {
  58. tracing::info!("TLS configuration found, starting secure server");
  59. let server_pem_path = tls_dir.join("server.pem");
  60. let server_key_path = tls_dir.join("server.key");
  61. let ca_pem_path = tls_dir.join("ca.pem");
  62. if !server_pem_path.exists() {
  63. tracing::error!(
  64. "Server certificate file does not exist: {}",
  65. server_pem_path.display()
  66. );
  67. return Err(Error::Io(std::io::Error::new(
  68. std::io::ErrorKind::NotFound,
  69. format!(
  70. "Server certificate file not found: {}",
  71. server_pem_path.display()
  72. ),
  73. )));
  74. }
  75. if !server_key_path.exists() {
  76. tracing::error!(
  77. "Server key file does not exist: {}",
  78. server_key_path.display()
  79. );
  80. return Err(Error::Io(std::io::Error::new(
  81. std::io::ErrorKind::NotFound,
  82. format!("Server key file not found: {}", server_key_path.display()),
  83. )));
  84. }
  85. if !ca_pem_path.exists() {
  86. tracing::error!(
  87. "CA certificate file does not exist: {}",
  88. ca_pem_path.display()
  89. );
  90. return Err(Error::Io(std::io::Error::new(
  91. std::io::ErrorKind::NotFound,
  92. format!("CA certificate file not found: {}", ca_pem_path.display()),
  93. )));
  94. }
  95. let cert = std::fs::read_to_string(&server_pem_path)?;
  96. let key = std::fs::read_to_string(&server_key_path)?;
  97. let client_ca_cert = std::fs::read_to_string(&ca_pem_path)?;
  98. let client_ca_cert = Certificate::from_pem(client_ca_cert);
  99. let server_identity = Identity::from_pem(cert, key);
  100. let tls_config = ServerTlsConfig::new()
  101. .identity(server_identity)
  102. .client_ca_root(client_ca_cert);
  103. Server::builder()
  104. .tls_config(tls_config)?
  105. .add_service(CdkMintServer::new(self.clone()))
  106. }
  107. None => {
  108. tracing::warn!("No valid TLS configuration found, starting insecure server");
  109. Server::builder().add_service(CdkMintServer::new(self.clone()))
  110. }
  111. };
  112. let shutdown = self.shutdown.clone();
  113. let addr = self.socket_addr;
  114. self.handle = Some(Arc::new(tokio::spawn(async move {
  115. let server = server.serve_with_shutdown(addr, async {
  116. shutdown.notified().await;
  117. });
  118. server.await?;
  119. Ok(())
  120. })));
  121. Ok(())
  122. }
  123. pub async fn stop(&self) -> Result<(), Error> {
  124. self.shutdown.notify_one();
  125. if let Some(handle) = &self.handle {
  126. while !handle.is_finished() {
  127. tracing::info!("Waitning for mint rpc server to stop");
  128. tokio::time::sleep(Duration::from_millis(100)).await;
  129. }
  130. }
  131. tracing::info!("Mint rpc server stopped");
  132. Ok(())
  133. }
  134. }
  135. impl Drop for MintRPCServer {
  136. fn drop(&mut self) {
  137. tracing::debug!("Dropping mint rpc server");
  138. self.shutdown.notify_one();
  139. }
  140. }
  141. #[tonic::async_trait]
  142. impl CdkMint for MintRPCServer {
  143. async fn get_info(
  144. &self,
  145. _request: Request<GetInfoRequest>,
  146. ) -> Result<Response<GetInfoResponse>, Status> {
  147. let info = self
  148. .mint
  149. .mint_info()
  150. .await
  151. .map_err(|err| Status::internal(err.to_string()))?;
  152. let total_issued = self
  153. .mint
  154. .total_issued()
  155. .await
  156. .map_err(|err| Status::internal(err.to_string()))?;
  157. let total_issued: Amount = Amount::try_sum(total_issued.values().cloned())
  158. .map_err(|_| Status::internal("Overflow".to_string()))?;
  159. let total_redeemed = self
  160. .mint
  161. .total_redeemed()
  162. .await
  163. .map_err(|err| Status::internal(err.to_string()))?;
  164. let total_redeemed: Amount = Amount::try_sum(total_redeemed.values().cloned())
  165. .map_err(|_| Status::internal("Overflow".to_string()))?;
  166. let contact = info
  167. .contact
  168. .unwrap_or_default()
  169. .into_iter()
  170. .map(|c| ContactInfo {
  171. method: c.method,
  172. info: c.info,
  173. })
  174. .collect();
  175. Ok(Response::new(GetInfoResponse {
  176. name: info.name,
  177. description: info.description,
  178. long_description: info.description_long,
  179. version: info.version.map(|v| v.to_string()),
  180. contact,
  181. motd: info.motd,
  182. icon_url: info.icon_url,
  183. urls: info.urls.unwrap_or_default(),
  184. total_issued: total_issued.into(),
  185. total_redeemed: total_redeemed.into(),
  186. }))
  187. }
  188. async fn update_motd(
  189. &self,
  190. request: Request<UpdateMotdRequest>,
  191. ) -> Result<Response<UpdateResponse>, Status> {
  192. let motd = request.into_inner().motd;
  193. let mut info = self
  194. .mint
  195. .mint_info()
  196. .await
  197. .map_err(|err| Status::internal(err.to_string()))?;
  198. info.motd = Some(motd);
  199. self.mint
  200. .set_mint_info(info)
  201. .await
  202. .map_err(|err| Status::internal(err.to_string()))?;
  203. Ok(Response::new(UpdateResponse {}))
  204. }
  205. async fn update_short_description(
  206. &self,
  207. request: Request<UpdateDescriptionRequest>,
  208. ) -> Result<Response<UpdateResponse>, Status> {
  209. let description = request.into_inner().description;
  210. let mut info = self
  211. .mint
  212. .mint_info()
  213. .await
  214. .map_err(|err| Status::internal(err.to_string()))?;
  215. info.description = Some(description);
  216. self.mint
  217. .set_mint_info(info)
  218. .await
  219. .map_err(|err| Status::internal(err.to_string()))?;
  220. Ok(Response::new(UpdateResponse {}))
  221. }
  222. async fn update_long_description(
  223. &self,
  224. request: Request<UpdateDescriptionRequest>,
  225. ) -> Result<Response<UpdateResponse>, Status> {
  226. let description = request.into_inner().description;
  227. let mut info = self
  228. .mint
  229. .mint_info()
  230. .await
  231. .map_err(|err| Status::internal(err.to_string()))?;
  232. info.description = Some(description);
  233. self.mint
  234. .set_mint_info(info)
  235. .await
  236. .map_err(|err| Status::internal(err.to_string()))?;
  237. Ok(Response::new(UpdateResponse {}))
  238. }
  239. async fn update_name(
  240. &self,
  241. request: Request<UpdateNameRequest>,
  242. ) -> Result<Response<UpdateResponse>, Status> {
  243. let name = request.into_inner().name;
  244. let mut info = self
  245. .mint
  246. .mint_info()
  247. .await
  248. .map_err(|err| Status::internal(err.to_string()))?;
  249. info.name = Some(name);
  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. async fn update_icon_url(
  257. &self,
  258. request: Request<UpdateIconUrlRequest>,
  259. ) -> Result<Response<UpdateResponse>, Status> {
  260. let icon_url = request.into_inner().icon_url;
  261. let mut info = self
  262. .mint
  263. .mint_info()
  264. .await
  265. .map_err(|err| Status::internal(err.to_string()))?;
  266. info.icon_url = Some(icon_url);
  267. self.mint
  268. .set_mint_info(info)
  269. .await
  270. .map_err(|err| Status::internal(err.to_string()))?;
  271. Ok(Response::new(UpdateResponse {}))
  272. }
  273. async fn add_url(
  274. &self,
  275. request: Request<UpdateUrlRequest>,
  276. ) -> Result<Response<UpdateResponse>, Status> {
  277. let url = request.into_inner().url;
  278. let mut info = self
  279. .mint
  280. .mint_info()
  281. .await
  282. .map_err(|err| Status::internal(err.to_string()))?;
  283. let urls = info.urls;
  284. urls.clone().unwrap_or_default().push(url);
  285. info.urls = urls;
  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. async fn remove_url(
  293. &self,
  294. request: Request<UpdateUrlRequest>,
  295. ) -> Result<Response<UpdateResponse>, Status> {
  296. let url = request.into_inner().url;
  297. let mut info = self
  298. .mint
  299. .mint_info()
  300. .await
  301. .map_err(|err| Status::internal(err.to_string()))?;
  302. let urls = info.urls;
  303. urls.clone().unwrap_or_default().push(url);
  304. info.urls = urls;
  305. self.mint
  306. .set_mint_info(info)
  307. .await
  308. .map_err(|err| Status::internal(err.to_string()))?;
  309. Ok(Response::new(UpdateResponse {}))
  310. }
  311. async fn add_contact(
  312. &self,
  313. request: Request<UpdateContactRequest>,
  314. ) -> Result<Response<UpdateResponse>, Status> {
  315. let request_inner = request.into_inner();
  316. let mut info = self
  317. .mint
  318. .mint_info()
  319. .await
  320. .map_err(|err| Status::internal(err.to_string()))?;
  321. info.contact
  322. .get_or_insert_with(Vec::new)
  323. .push(cdk::nuts::ContactInfo::new(
  324. request_inner.method,
  325. request_inner.info,
  326. ));
  327. self.mint
  328. .set_mint_info(info)
  329. .await
  330. .map_err(|err| Status::internal(err.to_string()))?;
  331. Ok(Response::new(UpdateResponse {}))
  332. }
  333. async fn remove_contact(
  334. &self,
  335. request: Request<UpdateContactRequest>,
  336. ) -> Result<Response<UpdateResponse>, Status> {
  337. let request_inner = request.into_inner();
  338. let mut info = self
  339. .mint
  340. .mint_info()
  341. .await
  342. .map_err(|err| Status::internal(err.to_string()))?;
  343. if let Some(contact) = info.contact.as_mut() {
  344. let contact_info =
  345. cdk::nuts::ContactInfo::new(request_inner.method, request_inner.info);
  346. contact.retain(|x| x != &contact_info);
  347. self.mint
  348. .set_mint_info(info)
  349. .await
  350. .map_err(|err| Status::internal(err.to_string()))?;
  351. }
  352. Ok(Response::new(UpdateResponse {}))
  353. }
  354. async fn update_nut04(
  355. &self,
  356. request: Request<UpdateNut04Request>,
  357. ) -> Result<Response<UpdateResponse>, Status> {
  358. let mut info = self
  359. .mint
  360. .mint_info()
  361. .await
  362. .map_err(|err| Status::internal(err.to_string()))?;
  363. let mut nut04_settings = info.nuts.nut04.clone();
  364. let request_inner = request.into_inner();
  365. let unit = CurrencyUnit::from_str(&request_inner.unit)
  366. .map_err(|_| Status::invalid_argument("Invalid unit".to_string()))?;
  367. let payment_method = PaymentMethod::from_str(&request_inner.method)
  368. .map_err(|_| Status::invalid_argument("Invalid method".to_string()))?;
  369. let current_nut04_settings = nut04_settings.remove_settings(&unit, &payment_method);
  370. let mut methods = nut04_settings.methods.clone();
  371. let updated_method_settings = MintMethodSettings {
  372. method: payment_method,
  373. unit,
  374. min_amount: request_inner
  375. .min
  376. .map(Amount::from)
  377. .or_else(|| current_nut04_settings.as_ref().and_then(|s| s.min_amount)),
  378. max_amount: request_inner
  379. .max
  380. .map(Amount::from)
  381. .or_else(|| current_nut04_settings.as_ref().and_then(|s| s.max_amount)),
  382. description: request_inner.description.unwrap_or(
  383. current_nut04_settings
  384. .map(|c| c.description)
  385. .unwrap_or_default(),
  386. ),
  387. };
  388. methods.push(updated_method_settings);
  389. nut04_settings.methods = methods;
  390. if let Some(disabled) = request_inner.disabled {
  391. nut04_settings.disabled = disabled;
  392. }
  393. info.nuts.nut04 = nut04_settings;
  394. self.mint
  395. .set_mint_info(info)
  396. .await
  397. .map_err(|err| Status::internal(err.to_string()))?;
  398. Ok(Response::new(UpdateResponse {}))
  399. }
  400. async fn update_nut05(
  401. &self,
  402. request: Request<UpdateNut05Request>,
  403. ) -> Result<Response<UpdateResponse>, Status> {
  404. let mut info = self
  405. .mint
  406. .mint_info()
  407. .await
  408. .map_err(|err| Status::internal(err.to_string()))?;
  409. let mut nut05_settings = info.nuts.nut05.clone();
  410. let request_inner = request.into_inner();
  411. let unit = CurrencyUnit::from_str(&request_inner.unit)
  412. .map_err(|_| Status::invalid_argument("Invalid unit".to_string()))?;
  413. let payment_method = PaymentMethod::from_str(&request_inner.method)
  414. .map_err(|_| Status::invalid_argument("Invalid method".to_string()))?;
  415. let current_nut05_settings = nut05_settings.remove_settings(&unit, &payment_method);
  416. let mut methods = nut05_settings.methods;
  417. let updated_method_settings = MeltMethodSettings {
  418. method: payment_method,
  419. unit,
  420. min_amount: request_inner
  421. .min
  422. .map(Amount::from)
  423. .or_else(|| current_nut05_settings.as_ref().and_then(|s| s.min_amount)),
  424. max_amount: request_inner
  425. .max
  426. .map(Amount::from)
  427. .or_else(|| current_nut05_settings.as_ref().and_then(|s| s.max_amount)),
  428. };
  429. methods.push(updated_method_settings);
  430. nut05_settings.methods = methods;
  431. if let Some(disabled) = request_inner.disabled {
  432. nut05_settings.disabled = disabled;
  433. }
  434. info.nuts.nut05 = nut05_settings;
  435. self.mint
  436. .set_mint_info(info)
  437. .await
  438. .map_err(|err| Status::internal(err.to_string()))?;
  439. Ok(Response::new(UpdateResponse {}))
  440. }
  441. async fn update_quote_ttl(
  442. &self,
  443. request: Request<UpdateQuoteTtlRequest>,
  444. ) -> Result<Response<UpdateResponse>, Status> {
  445. let current_ttl = self
  446. .mint
  447. .quote_ttl()
  448. .await
  449. .map_err(|err| Status::internal(err.to_string()))?;
  450. let request = request.into_inner();
  451. let quote_ttl = QuoteTTL {
  452. mint_ttl: request.mint_ttl.unwrap_or(current_ttl.mint_ttl),
  453. melt_ttl: request.melt_ttl.unwrap_or(current_ttl.melt_ttl),
  454. };
  455. self.mint
  456. .set_quote_ttl(quote_ttl)
  457. .await
  458. .map_err(|err| Status::internal(err.to_string()))?;
  459. Ok(Response::new(UpdateResponse {}))
  460. }
  461. async fn update_nut04_quote(
  462. &self,
  463. request: Request<UpdateNut04QuoteRequest>,
  464. ) -> Result<Response<UpdateNut04QuoteRequest>, Status> {
  465. let request = request.into_inner();
  466. let quote_id = request
  467. .quote_id
  468. .parse()
  469. .map_err(|_| Status::invalid_argument("Invalid quote id".to_string()))?;
  470. let state = MintQuoteState::from_str(&request.state)
  471. .map_err(|_| Status::invalid_argument("Invalid quote state".to_string()))?;
  472. let mint_quote = self
  473. .mint
  474. .localstore
  475. .get_mint_quote(&quote_id)
  476. .await
  477. .map_err(|_| Status::invalid_argument("Could not find quote".to_string()))?
  478. .ok_or(Status::invalid_argument("Could not find quote".to_string()))?;
  479. match state {
  480. MintQuoteState::Paid => {
  481. self.mint
  482. .pay_mint_quote(&mint_quote)
  483. .await
  484. .map_err(|_| Status::internal("Could not find quote".to_string()))?;
  485. }
  486. _ => {
  487. let mut mint_quote = mint_quote;
  488. mint_quote.state = state;
  489. self.mint
  490. .update_mint_quote(mint_quote)
  491. .await
  492. .map_err(|_| Status::internal("Could not update quote".to_string()))?;
  493. }
  494. }
  495. let mint_quote = self
  496. .mint
  497. .localstore
  498. .get_mint_quote(&quote_id)
  499. .await
  500. .map_err(|_| Status::invalid_argument("Could not find quote".to_string()))?
  501. .ok_or(Status::invalid_argument("Could not find quote".to_string()))?;
  502. Ok(Response::new(UpdateNut04QuoteRequest {
  503. state: mint_quote.state.to_string(),
  504. quote_id: mint_quote.id.to_string(),
  505. }))
  506. }
  507. async fn rotate_next_keyset(
  508. &self,
  509. request: Request<RotateNextKeysetRequest>,
  510. ) -> Result<Response<RotateNextKeysetResponse>, Status> {
  511. let request = request.into_inner();
  512. let unit = CurrencyUnit::from_str(&request.unit)
  513. .map_err(|_| Status::invalid_argument("Invalid unit".to_string()))?;
  514. let keyset_info = self
  515. .mint
  516. .rotate_next_keyset(
  517. unit,
  518. request.max_order.map(|a| a as u8).unwrap_or(32),
  519. request.input_fee_ppk.unwrap_or(0),
  520. )
  521. .await
  522. .map_err(|_| Status::invalid_argument("Could not rotate keyset".to_string()))?;
  523. Ok(Response::new(RotateNextKeysetResponse {
  524. id: keyset_info.id.to_string(),
  525. unit: keyset_info.unit.to_string(),
  526. max_order: keyset_info.max_order.into(),
  527. input_fee_ppk: keyset_info.input_fee_ppk,
  528. }))
  529. }
  530. }