main.rs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689
  1. //! CDK MINTD
  2. #![warn(missing_docs)]
  3. #![warn(rustdoc::bare_urls)]
  4. use std::collections::HashMap;
  5. use std::env;
  6. use std::net::SocketAddr;
  7. use std::path::PathBuf;
  8. use std::str::FromStr;
  9. use std::sync::Arc;
  10. use anyhow::{anyhow, bail, Result};
  11. use axum::Router;
  12. use bip39::Mnemonic;
  13. use cdk::cdk_database::{self, MintAuthDatabase};
  14. use cdk::mint::{MintBuilder, MintMeltLimits};
  15. // Feature-gated imports
  16. #[cfg(any(
  17. feature = "cln",
  18. feature = "lnbits",
  19. feature = "lnd",
  20. feature = "fakewallet",
  21. feature = "grpc-processor"
  22. ))]
  23. use cdk::nuts::nut17::SupportedMethods;
  24. use cdk::nuts::nut19::{CachedEndpoint, Method as NUT19Method, Path as NUT19Path};
  25. #[cfg(any(
  26. feature = "cln",
  27. feature = "lnbits",
  28. feature = "lnd",
  29. feature = "fakewallet"
  30. ))]
  31. use cdk::nuts::CurrencyUnit;
  32. use cdk::nuts::{
  33. AuthRequired, ContactInfo, MintVersion, PaymentMethod, ProtectedEndpoint, RoutePath,
  34. };
  35. use cdk::types::QuoteTTL;
  36. use cdk_axum::cache::HttpCache;
  37. #[cfg(feature = "management-rpc")]
  38. use cdk_mint_rpc::MintRPCServer;
  39. use cdk_mintd::cli::CLIArgs;
  40. use cdk_mintd::config::{self, DatabaseEngine, LnBackend};
  41. use cdk_mintd::env_vars::ENV_WORK_DIR;
  42. use cdk_mintd::setup::LnBackendSetup;
  43. use cdk_sqlite::mint::MintSqliteAuthDatabase;
  44. use cdk_sqlite::MintSqliteDatabase;
  45. use clap::Parser;
  46. use tokio::sync::Notify;
  47. use tower::ServiceBuilder;
  48. use tower_http::compression::CompressionLayer;
  49. use tower_http::decompression::RequestDecompressionLayer;
  50. use tower_http::trace::TraceLayer;
  51. use tracing_subscriber::EnvFilter;
  52. #[cfg(feature = "swagger")]
  53. use utoipa::OpenApi;
  54. const CARGO_PKG_VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION");
  55. // Ensure at least one lightning backend is enabled at compile time
  56. #[cfg(not(any(
  57. feature = "cln",
  58. feature = "lnbits",
  59. feature = "lnd",
  60. feature = "fakewallet",
  61. feature = "grpc-processor"
  62. )))]
  63. compile_error!(
  64. "At least one lightning backend feature must be enabled: cln, lnbits, lnd, fakewallet, or grpc-processor"
  65. );
  66. #[tokio::main]
  67. async fn main() -> anyhow::Result<()> {
  68. let default_filter = "debug";
  69. let sqlx_filter = "sqlx=warn";
  70. let hyper_filter = "hyper=warn";
  71. let h2_filter = "h2=warn";
  72. let tower_http = "tower_http=warn";
  73. let env_filter = EnvFilter::new(format!(
  74. "{default_filter},{sqlx_filter},{hyper_filter},{h2_filter},{tower_http}"
  75. ));
  76. tracing_subscriber::fmt().with_env_filter(env_filter).init();
  77. let args = CLIArgs::parse();
  78. let work_dir = if let Some(work_dir) = args.work_dir {
  79. tracing::info!("Using work dir from cmd arg");
  80. work_dir
  81. } else if let Ok(env_work_dir) = env::var(ENV_WORK_DIR) {
  82. tracing::info!("Using work dir from env var");
  83. env_work_dir.into()
  84. } else {
  85. work_dir()?
  86. };
  87. tracing::info!("Using work dir: {}", work_dir.display());
  88. // get config file name from args
  89. let config_file_arg = match args.config {
  90. Some(c) => c,
  91. None => work_dir.join("config.toml"),
  92. };
  93. let mut settings = if config_file_arg.exists() {
  94. config::Settings::new(Some(config_file_arg))
  95. } else {
  96. tracing::info!("Config file does not exist. Attempting to read env vars");
  97. config::Settings::default()
  98. };
  99. // This check for any settings defined in ENV VARs
  100. // ENV VARS will take **priority** over those in the config
  101. let settings = settings.from_env()?;
  102. let mut mint_builder = match settings.database.engine {
  103. DatabaseEngine::Sqlite => {
  104. let sql_db_path = work_dir.join("cdk-mintd.sqlite");
  105. #[cfg(not(feature = "sqlcipher"))]
  106. let sqlite_db = MintSqliteDatabase::new(&sql_db_path).await?;
  107. #[cfg(feature = "sqlcipher")]
  108. let sqlite_db = MintSqliteDatabase::new(&sql_db_path, args.password.clone()).await?;
  109. let db = Arc::new(sqlite_db);
  110. MintBuilder::new()
  111. .with_localstore(db.clone())
  112. .with_keystore(db)
  113. }
  114. };
  115. let mut contact_info: Option<Vec<ContactInfo>> = None;
  116. if let Some(nostr_contact) = &settings.mint_info.contact_nostr_public_key {
  117. let nostr_contact = ContactInfo::new("nostr".to_string(), nostr_contact.to_string());
  118. contact_info = match contact_info {
  119. Some(mut vec) => {
  120. vec.push(nostr_contact);
  121. Some(vec)
  122. }
  123. None => Some(vec![nostr_contact]),
  124. };
  125. }
  126. if let Some(email_contact) = &settings.mint_info.contact_email {
  127. let email_contact = ContactInfo::new("email".to_string(), email_contact.to_string());
  128. contact_info = match contact_info {
  129. Some(mut vec) => {
  130. vec.push(email_contact);
  131. Some(vec)
  132. }
  133. None => Some(vec![email_contact]),
  134. };
  135. }
  136. let mint_version = MintVersion::new(
  137. "cdk-mintd".to_string(),
  138. CARGO_PKG_VERSION.unwrap_or("Unknown").to_string(),
  139. );
  140. let mut ln_routers = vec![];
  141. let mint_melt_limits = MintMeltLimits {
  142. mint_min: settings.ln.min_mint,
  143. mint_max: settings.ln.max_mint,
  144. melt_min: settings.ln.min_melt,
  145. melt_max: settings.ln.max_melt,
  146. };
  147. tracing::debug!("Ln backend: {:?}", settings.ln.ln_backend);
  148. match settings.ln.ln_backend {
  149. #[cfg(feature = "cln")]
  150. LnBackend::Cln => {
  151. let cln_settings = settings
  152. .cln
  153. .clone()
  154. .expect("Config checked at load that cln is some");
  155. let cln = cln_settings
  156. .setup(&mut ln_routers, &settings, CurrencyUnit::Msat)
  157. .await?;
  158. let cln = Arc::new(cln);
  159. mint_builder = mint_builder
  160. .add_ln_backend(
  161. CurrencyUnit::Sat,
  162. PaymentMethod::Bolt11,
  163. mint_melt_limits,
  164. cln.clone(),
  165. )
  166. .await?;
  167. if let Some(input_fee) = settings.info.input_fee_ppk {
  168. mint_builder = mint_builder.set_unit_fee(&CurrencyUnit::Sat, input_fee)?;
  169. }
  170. let nut17_supported = SupportedMethods::default_bolt11(CurrencyUnit::Sat);
  171. mint_builder = mint_builder.add_supported_websockets(nut17_supported);
  172. }
  173. #[cfg(feature = "lnbits")]
  174. LnBackend::LNbits => {
  175. let lnbits_settings = settings.clone().lnbits.expect("Checked on config load");
  176. let lnbits = lnbits_settings
  177. .setup(&mut ln_routers, &settings, CurrencyUnit::Sat)
  178. .await?;
  179. mint_builder = mint_builder
  180. .add_ln_backend(
  181. CurrencyUnit::Sat,
  182. PaymentMethod::Bolt11,
  183. mint_melt_limits,
  184. Arc::new(lnbits),
  185. )
  186. .await?;
  187. if let Some(input_fee) = settings.info.input_fee_ppk {
  188. mint_builder = mint_builder.set_unit_fee(&CurrencyUnit::Sat, input_fee)?;
  189. }
  190. let nut17_supported = SupportedMethods::default_bolt11(CurrencyUnit::Sat);
  191. mint_builder = mint_builder.add_supported_websockets(nut17_supported);
  192. }
  193. #[cfg(feature = "lnd")]
  194. LnBackend::Lnd => {
  195. let lnd_settings = settings.clone().lnd.expect("Checked at config load");
  196. let lnd = lnd_settings
  197. .setup(&mut ln_routers, &settings, CurrencyUnit::Msat)
  198. .await?;
  199. mint_builder = mint_builder
  200. .add_ln_backend(
  201. CurrencyUnit::Sat,
  202. PaymentMethod::Bolt11,
  203. mint_melt_limits,
  204. Arc::new(lnd),
  205. )
  206. .await?;
  207. if let Some(input_fee) = settings.info.input_fee_ppk {
  208. mint_builder = mint_builder.set_unit_fee(&CurrencyUnit::Sat, input_fee)?;
  209. }
  210. let nut17_supported = SupportedMethods::default_bolt11(CurrencyUnit::Sat);
  211. mint_builder = mint_builder.add_supported_websockets(nut17_supported);
  212. }
  213. #[cfg(feature = "fakewallet")]
  214. LnBackend::FakeWallet => {
  215. let fake_wallet = settings.clone().fake_wallet.expect("Fake wallet defined");
  216. tracing::info!("Using fake wallet: {:?}", fake_wallet);
  217. for unit in fake_wallet.clone().supported_units {
  218. let fake = fake_wallet
  219. .setup(&mut ln_routers, &settings, CurrencyUnit::Sat)
  220. .await
  221. .expect("hhh");
  222. let fake = Arc::new(fake);
  223. mint_builder = mint_builder
  224. .add_ln_backend(
  225. unit.clone(),
  226. PaymentMethod::Bolt11,
  227. mint_melt_limits,
  228. fake.clone(),
  229. )
  230. .await?;
  231. if let Some(input_fee) = settings.info.input_fee_ppk {
  232. mint_builder = mint_builder.set_unit_fee(&unit, input_fee)?;
  233. }
  234. let nut17_supported = SupportedMethods::default_bolt11(unit);
  235. mint_builder = mint_builder.add_supported_websockets(nut17_supported);
  236. }
  237. }
  238. #[cfg(feature = "grpc-processor")]
  239. LnBackend::GrpcProcessor => {
  240. let grpc_processor = settings
  241. .clone()
  242. .grpc_processor
  243. .expect("grpc processor config defined");
  244. tracing::info!(
  245. "Attempting to start with gRPC payment processor at {}:{}.",
  246. grpc_processor.addr,
  247. grpc_processor.port
  248. );
  249. tracing::info!("{:?}", grpc_processor);
  250. for unit in grpc_processor.clone().supported_units {
  251. tracing::debug!("Adding unit: {:?}", unit);
  252. let processor = grpc_processor
  253. .setup(&mut ln_routers, &settings, unit.clone())
  254. .await?;
  255. mint_builder = mint_builder
  256. .add_ln_backend(
  257. unit.clone(),
  258. PaymentMethod::Bolt11,
  259. mint_melt_limits,
  260. Arc::new(processor),
  261. )
  262. .await?;
  263. if let Some(input_fee) = settings.info.input_fee_ppk {
  264. mint_builder = mint_builder.set_unit_fee(&unit, input_fee)?;
  265. }
  266. let nut17_supported = SupportedMethods::default_bolt11(unit);
  267. mint_builder = mint_builder.add_supported_websockets(nut17_supported);
  268. }
  269. }
  270. LnBackend::None => {
  271. tracing::error!(
  272. "Pyament backend was not set or feature disabled. {:?}",
  273. settings.ln.ln_backend
  274. );
  275. bail!("Ln backend must be")
  276. }
  277. };
  278. if let Some(long_description) = &settings.mint_info.description_long {
  279. mint_builder = mint_builder.with_long_description(long_description.to_string());
  280. }
  281. if let Some(contact_info) = contact_info {
  282. for info in contact_info {
  283. mint_builder = mint_builder.add_contact_info(info);
  284. }
  285. }
  286. if let Some(pubkey) = settings.mint_info.pubkey {
  287. mint_builder = mint_builder.with_pubkey(pubkey);
  288. }
  289. if let Some(icon_url) = &settings.mint_info.icon_url {
  290. mint_builder = mint_builder.with_icon_url(icon_url.to_string());
  291. }
  292. if let Some(motd) = settings.mint_info.motd {
  293. mint_builder = mint_builder.with_motd(motd);
  294. }
  295. if let Some(tos_url) = &settings.mint_info.tos_url {
  296. mint_builder = mint_builder.with_tos_url(tos_url.to_string());
  297. }
  298. mint_builder = mint_builder
  299. .with_name(settings.mint_info.name)
  300. .with_version(mint_version.clone())
  301. .with_description(settings.mint_info.description);
  302. mint_builder = if let Some(signatory_url) = settings.info.signatory_url {
  303. tracing::info!(
  304. "Connecting to remote signatory to {} with certs {:?}",
  305. signatory_url,
  306. settings.info.signatory_certs
  307. );
  308. mint_builder.with_signatory(Arc::new(
  309. cdk_signatory::SignatoryRpcClient::new(signatory_url, settings.info.signatory_certs)
  310. .await?,
  311. ))
  312. } else if let Some(mnemonic) = settings
  313. .info
  314. .mnemonic
  315. .map(|s| Mnemonic::from_str(&s))
  316. .transpose()?
  317. {
  318. mint_builder.with_seed(mnemonic.to_seed_normalized("").to_vec())
  319. } else {
  320. bail!("No seed nor remote signatory set");
  321. };
  322. let cached_endpoints = vec![
  323. CachedEndpoint::new(NUT19Method::Post, NUT19Path::MintBolt11),
  324. CachedEndpoint::new(NUT19Method::Post, NUT19Path::MeltBolt11),
  325. CachedEndpoint::new(NUT19Method::Post, NUT19Path::Swap),
  326. ];
  327. let cache: HttpCache = settings.info.http_cache.into();
  328. mint_builder = mint_builder.add_cache(Some(cache.ttl.as_secs()), cached_endpoints);
  329. // Add auth to mint
  330. if let Some(auth_settings) = settings.auth {
  331. tracing::info!("Auth settings are defined. {:?}", auth_settings);
  332. let auth_localstore: Arc<dyn MintAuthDatabase<Err = cdk_database::Error> + Send + Sync> =
  333. match settings.database.engine {
  334. DatabaseEngine::Sqlite => {
  335. let sql_db_path = work_dir.join("cdk-mintd-auth.sqlite");
  336. #[cfg(not(feature = "sqlcipher"))]
  337. let sqlite_db = MintSqliteAuthDatabase::new(&sql_db_path).await?;
  338. #[cfg(feature = "sqlcipher")]
  339. let sqlite_db =
  340. MintSqliteAuthDatabase::new(&sql_db_path, args.password).await?;
  341. Arc::new(sqlite_db)
  342. }
  343. };
  344. mint_builder = mint_builder.with_auth_localstore(auth_localstore.clone());
  345. let mint_blind_auth_endpoint =
  346. ProtectedEndpoint::new(cdk::nuts::Method::Post, RoutePath::MintBlindAuth);
  347. mint_builder = mint_builder.set_clear_auth_settings(
  348. auth_settings.openid_discovery,
  349. auth_settings.openid_client_id,
  350. );
  351. let mut protected_endpoints = HashMap::new();
  352. protected_endpoints.insert(mint_blind_auth_endpoint, AuthRequired::Clear);
  353. let mut blind_auth_endpoints = vec![];
  354. let mut unprotected_endpoints = vec![];
  355. {
  356. let mint_quote_protected_endpoint = ProtectedEndpoint::new(
  357. cdk::nuts::Method::Post,
  358. cdk::nuts::RoutePath::MintQuoteBolt11,
  359. );
  360. let mint_protected_endpoint =
  361. ProtectedEndpoint::new(cdk::nuts::Method::Post, cdk::nuts::RoutePath::MintBolt11);
  362. if auth_settings.enabled_mint {
  363. protected_endpoints.insert(mint_quote_protected_endpoint, AuthRequired::Blind);
  364. protected_endpoints.insert(mint_protected_endpoint, AuthRequired::Blind);
  365. blind_auth_endpoints.push(mint_quote_protected_endpoint);
  366. blind_auth_endpoints.push(mint_protected_endpoint);
  367. } else {
  368. unprotected_endpoints.push(mint_protected_endpoint);
  369. unprotected_endpoints.push(mint_quote_protected_endpoint);
  370. }
  371. }
  372. {
  373. let melt_quote_protected_endpoint = ProtectedEndpoint::new(
  374. cdk::nuts::Method::Post,
  375. cdk::nuts::RoutePath::MeltQuoteBolt11,
  376. );
  377. let melt_protected_endpoint =
  378. ProtectedEndpoint::new(cdk::nuts::Method::Post, cdk::nuts::RoutePath::MeltBolt11);
  379. if auth_settings.enabled_melt {
  380. protected_endpoints.insert(melt_quote_protected_endpoint, AuthRequired::Blind);
  381. protected_endpoints.insert(melt_protected_endpoint, AuthRequired::Blind);
  382. blind_auth_endpoints.push(melt_quote_protected_endpoint);
  383. blind_auth_endpoints.push(melt_protected_endpoint);
  384. } else {
  385. unprotected_endpoints.push(melt_quote_protected_endpoint);
  386. unprotected_endpoints.push(melt_protected_endpoint);
  387. }
  388. }
  389. {
  390. let swap_protected_endpoint =
  391. ProtectedEndpoint::new(cdk::nuts::Method::Post, cdk::nuts::RoutePath::Swap);
  392. if auth_settings.enabled_swap {
  393. protected_endpoints.insert(swap_protected_endpoint, AuthRequired::Blind);
  394. blind_auth_endpoints.push(swap_protected_endpoint);
  395. } else {
  396. unprotected_endpoints.push(swap_protected_endpoint);
  397. }
  398. }
  399. {
  400. let check_mint_protected_endpoint = ProtectedEndpoint::new(
  401. cdk::nuts::Method::Get,
  402. cdk::nuts::RoutePath::MintQuoteBolt11,
  403. );
  404. if auth_settings.enabled_check_mint_quote {
  405. protected_endpoints.insert(check_mint_protected_endpoint, AuthRequired::Blind);
  406. blind_auth_endpoints.push(check_mint_protected_endpoint);
  407. } else {
  408. unprotected_endpoints.push(check_mint_protected_endpoint);
  409. }
  410. }
  411. {
  412. let check_melt_protected_endpoint = ProtectedEndpoint::new(
  413. cdk::nuts::Method::Get,
  414. cdk::nuts::RoutePath::MeltQuoteBolt11,
  415. );
  416. if auth_settings.enabled_check_melt_quote {
  417. protected_endpoints.insert(check_melt_protected_endpoint, AuthRequired::Blind);
  418. blind_auth_endpoints.push(check_melt_protected_endpoint);
  419. } else {
  420. unprotected_endpoints.push(check_melt_protected_endpoint);
  421. }
  422. }
  423. {
  424. let restore_protected_endpoint =
  425. ProtectedEndpoint::new(cdk::nuts::Method::Post, cdk::nuts::RoutePath::Restore);
  426. if auth_settings.enabled_restore {
  427. protected_endpoints.insert(restore_protected_endpoint, AuthRequired::Blind);
  428. blind_auth_endpoints.push(restore_protected_endpoint);
  429. } else {
  430. unprotected_endpoints.push(restore_protected_endpoint);
  431. }
  432. }
  433. {
  434. let state_protected_endpoint =
  435. ProtectedEndpoint::new(cdk::nuts::Method::Post, cdk::nuts::RoutePath::Checkstate);
  436. if auth_settings.enabled_check_proof_state {
  437. protected_endpoints.insert(state_protected_endpoint, AuthRequired::Blind);
  438. blind_auth_endpoints.push(state_protected_endpoint);
  439. } else {
  440. unprotected_endpoints.push(state_protected_endpoint);
  441. }
  442. }
  443. mint_builder = mint_builder.set_blind_auth_settings(auth_settings.mint_max_bat);
  444. auth_localstore
  445. .remove_protected_endpoints(unprotected_endpoints)
  446. .await?;
  447. auth_localstore
  448. .add_protected_endpoints(protected_endpoints)
  449. .await?;
  450. }
  451. let mint = mint_builder.build().await?;
  452. tracing::debug!("Mint built from builder.");
  453. let mint = Arc::new(mint);
  454. // Check the status of any mint quotes that are pending
  455. // In the event that the mint server is down but the ln node is not
  456. // it is possible that a mint quote was paid but the mint has not been updated
  457. // this will check and update the mint state of those quotes
  458. mint.check_pending_mint_quotes().await?;
  459. // Checks the status of all pending melt quotes
  460. // Pending melt quotes where the payment has gone through inputs are burnt
  461. // Pending melt quotes where the payment has **failed** inputs are reset to unspent
  462. mint.check_pending_melt_quotes().await?;
  463. let listen_addr = settings.info.listen_host;
  464. let listen_port = settings.info.listen_port;
  465. let v1_service =
  466. cdk_axum::create_mint_router_with_custom_cache(Arc::clone(&mint), cache).await?;
  467. let mut mint_service = Router::new()
  468. .merge(v1_service)
  469. .layer(
  470. ServiceBuilder::new()
  471. .layer(RequestDecompressionLayer::new())
  472. .layer(CompressionLayer::new()),
  473. )
  474. .layer(TraceLayer::new_for_http());
  475. #[cfg(feature = "swagger")]
  476. {
  477. if settings.info.enable_swagger_ui.unwrap_or(false) {
  478. mint_service = mint_service.merge(
  479. utoipa_swagger_ui::SwaggerUi::new("/swagger-ui")
  480. .url("/api-docs/openapi.json", cdk_axum::ApiDocV1::openapi()),
  481. );
  482. }
  483. }
  484. for router in ln_routers {
  485. mint_service = mint_service.merge(router);
  486. }
  487. let shutdown = Arc::new(Notify::new());
  488. let mint_clone = Arc::clone(&mint);
  489. tokio::spawn({
  490. let shutdown = Arc::clone(&shutdown);
  491. async move { mint_clone.wait_for_paid_invoices(shutdown).await }
  492. });
  493. #[cfg(feature = "management-rpc")]
  494. let mut rpc_enabled = false;
  495. #[cfg(not(feature = "management-rpc"))]
  496. let rpc_enabled = false;
  497. #[cfg(feature = "management-rpc")]
  498. let mut rpc_server: Option<cdk_mint_rpc::MintRPCServer> = None;
  499. #[cfg(feature = "management-rpc")]
  500. {
  501. if let Some(rpc_settings) = settings.mint_management_rpc {
  502. if rpc_settings.enabled {
  503. let addr = rpc_settings.address.unwrap_or("127.0.0.1".to_string());
  504. let port = rpc_settings.port.unwrap_or(8086);
  505. let mut mint_rpc = MintRPCServer::new(&addr, port, mint.clone())?;
  506. let tls_dir = rpc_settings.tls_dir_path.unwrap_or(work_dir.join("tls"));
  507. if !tls_dir.exists() {
  508. tracing::error!("TLS directory does not exist: {}", tls_dir.display());
  509. bail!("Cannot start RPC server: TLS directory does not exist");
  510. }
  511. mint_rpc.start(Some(tls_dir)).await?;
  512. rpc_server = Some(mint_rpc);
  513. rpc_enabled = true;
  514. }
  515. }
  516. }
  517. if rpc_enabled {
  518. if mint.mint_info().await.is_err() {
  519. tracing::info!("Mint info not set on mint, setting.");
  520. mint.set_mint_info(mint_builder.mint_info).await?;
  521. mint.set_quote_ttl(QuoteTTL::new(10_000, 10_000)).await?;
  522. } else {
  523. if mint.localstore.get_quote_ttl().await.is_err() {
  524. mint.set_quote_ttl(QuoteTTL::new(10_000, 10_000)).await?;
  525. }
  526. let mut stored_mint_info = mint.mint_info().await?;
  527. stored_mint_info.version = Some(mint_version);
  528. mint.set_mint_info(stored_mint_info).await?;
  529. tracing::info!("Mint info already set, not using config file settings.");
  530. }
  531. } else {
  532. tracing::warn!("RPC not enabled, using mint info from config.");
  533. mint.set_mint_info(mint_builder.mint_info).await?;
  534. mint.set_quote_ttl(QuoteTTL::new(10_000, 10_000)).await?;
  535. }
  536. let socket_addr = SocketAddr::from_str(&format!("{listen_addr}:{listen_port}"))?;
  537. let listener = tokio::net::TcpListener::bind(socket_addr).await?;
  538. tracing::debug!("listening on {}", listener.local_addr().unwrap());
  539. let axum_result = axum::serve(listener, mint_service).with_graceful_shutdown(shutdown_signal());
  540. match axum_result.await {
  541. Ok(_) => {
  542. tracing::info!("Axum server stopped with okay status");
  543. }
  544. Err(err) => {
  545. tracing::warn!("Axum server stopped with error");
  546. tracing::error!("{}", err);
  547. bail!("Axum exited with error")
  548. }
  549. }
  550. shutdown.notify_waiters();
  551. #[cfg(feature = "management-rpc")]
  552. {
  553. if let Some(rpc_server) = rpc_server {
  554. rpc_server.stop().await?;
  555. }
  556. }
  557. Ok(())
  558. }
  559. async fn shutdown_signal() {
  560. tokio::signal::ctrl_c()
  561. .await
  562. .expect("failed to install CTRL+C handler");
  563. tracing::info!("Shutdown signal received");
  564. }
  565. fn work_dir() -> Result<PathBuf> {
  566. let home_dir = home::home_dir().ok_or(anyhow!("Unknown home dir"))?;
  567. let dir = home_dir.join(".cdk-mintd");
  568. std::fs::create_dir_all(&dir)?;
  569. Ok(dir)
  570. }