server.rs 18 KB

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