lib.rs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. //! Axum server for Mint
  2. #![doc = include_str!("../README.md")]
  3. #![warn(missing_docs)]
  4. #![warn(rustdoc::bare_urls)]
  5. use std::sync::Arc;
  6. use anyhow::Result;
  7. #[cfg(feature = "auth")]
  8. use auth::create_auth_router;
  9. use axum::middleware::from_fn;
  10. use axum::response::Response;
  11. use axum::routing::{get, post};
  12. use axum::Router;
  13. use cache::HttpCache;
  14. use cdk::mint::Mint;
  15. use router_handlers::*;
  16. mod metrics;
  17. #[cfg(feature = "auth")]
  18. mod auth;
  19. mod bolt12_router;
  20. pub mod cache;
  21. mod router_handlers;
  22. mod ws;
  23. #[cfg(feature = "swagger")]
  24. mod swagger_imports {
  25. pub use cdk::amount::Amount;
  26. pub use cdk::error::{ErrorCode, ErrorResponse};
  27. pub use cdk::nuts::nut00::{
  28. BlindSignature, BlindedMessage, CurrencyUnit, PaymentMethod, Proof, Witness,
  29. };
  30. pub use cdk::nuts::nut01::{Keys, KeysResponse, PublicKey, SecretKey};
  31. pub use cdk::nuts::nut02::{KeySet, KeySetInfo, KeysetResponse};
  32. pub use cdk::nuts::nut03::{SwapRequest, SwapResponse};
  33. pub use cdk::nuts::nut04::{MintMethodSettings, MintRequest, MintResponse};
  34. pub use cdk::nuts::nut05::{MeltMethodSettings, MeltRequest};
  35. pub use cdk::nuts::nut06::{ContactInfo, MintInfo, MintVersion, Nuts, SupportedSettings};
  36. pub use cdk::nuts::nut07::{CheckStateRequest, CheckStateResponse, ProofState, State};
  37. pub use cdk::nuts::nut09::{RestoreRequest, RestoreResponse};
  38. pub use cdk::nuts::nut11::P2PKWitness;
  39. pub use cdk::nuts::nut12::{BlindSignatureDleq, ProofDleq};
  40. pub use cdk::nuts::nut14::HTLCWitness;
  41. pub use cdk::nuts::nut15::{Mpp, MppMethodSettings};
  42. pub use cdk::nuts::nut23::{
  43. MeltQuoteBolt11Request, MeltQuoteBolt11Response, MintQuoteBolt11Request,
  44. MintQuoteBolt11Response,
  45. };
  46. #[cfg(feature = "auth")]
  47. pub use cdk::nuts::MintAuthRequest;
  48. pub use cdk::nuts::{nut04, nut05, nut15, MeltQuoteState, MintQuoteState};
  49. }
  50. #[cfg(feature = "swagger")]
  51. use swagger_imports::*;
  52. use crate::bolt12_router::{
  53. cache_post_melt_bolt12, cache_post_mint_bolt12, get_check_mint_bolt12_quote,
  54. post_melt_bolt12_quote, post_mint_bolt12_quote,
  55. };
  56. /// CDK Mint State
  57. #[derive(Clone)]
  58. pub struct MintState {
  59. mint: Arc<Mint>,
  60. cache: Arc<cache::HttpCache>,
  61. }
  62. #[cfg(feature = "swagger")]
  63. macro_rules! define_api_doc {
  64. (
  65. schemas: [$($schema:ty),* $(,)?]
  66. $(, auth_schemas: [$($auth_schema:ty),* $(,)?])?
  67. $(, paths: [$($path:path),* $(,)?])?
  68. $(, auth_paths: [$($auth_path:path),* $(,)?])?
  69. ) => {
  70. #[derive(utoipa::OpenApi)]
  71. #[openapi(
  72. components(schemas(
  73. $($schema,)*
  74. $($($auth_schema,)*)?
  75. )),
  76. info(description = "Cashu CDK mint APIs", title = "cdk-mintd"),
  77. paths(
  78. get_keys,
  79. get_keyset_pubkeys,
  80. get_keysets,
  81. get_mint_info,
  82. post_mint_bolt11_quote,
  83. get_check_mint_bolt11_quote,
  84. post_mint_bolt11,
  85. post_melt_bolt11_quote,
  86. get_check_melt_bolt11_quote,
  87. post_melt_bolt11,
  88. post_swap,
  89. post_check,
  90. post_restore
  91. $(,$($path,)*)?
  92. $(,$($auth_path,)*)?
  93. )
  94. )]
  95. /// Swagger api docs
  96. pub struct ApiDoc;
  97. };
  98. }
  99. // Configuration without auth feature
  100. #[cfg(all(feature = "swagger", not(feature = "auth")))]
  101. define_api_doc! {
  102. schemas: [
  103. Amount,
  104. BlindedMessage,
  105. BlindSignature,
  106. BlindSignatureDleq,
  107. CheckStateRequest,
  108. CheckStateResponse,
  109. ContactInfo,
  110. CurrencyUnit,
  111. ErrorCode,
  112. ErrorResponse,
  113. HTLCWitness,
  114. Keys,
  115. KeysResponse,
  116. KeysetResponse,
  117. KeySet,
  118. KeySetInfo,
  119. MeltRequest<String>,
  120. MeltQuoteBolt11Request,
  121. MeltQuoteBolt11Response<String>,
  122. MeltQuoteState,
  123. MeltMethodSettings,
  124. MintRequest<String>,
  125. MintResponse,
  126. MintInfo,
  127. MintQuoteBolt11Request,
  128. MintQuoteBolt11Response<String>,
  129. MintQuoteState,
  130. MintMethodSettings,
  131. MintVersion,
  132. Mpp,
  133. MppMethodSettings,
  134. Nuts,
  135. P2PKWitness,
  136. PaymentMethod,
  137. Proof,
  138. ProofDleq,
  139. ProofState,
  140. PublicKey,
  141. RestoreRequest,
  142. RestoreResponse,
  143. SecretKey,
  144. State,
  145. SupportedSettings,
  146. SwapRequest,
  147. SwapResponse,
  148. Witness,
  149. nut04::Settings,
  150. nut05::Settings,
  151. nut15::Settings
  152. ]
  153. }
  154. // Configuration with auth feature
  155. #[cfg(all(feature = "swagger", feature = "auth"))]
  156. define_api_doc! {
  157. schemas: [
  158. Amount,
  159. BlindedMessage,
  160. BlindSignature,
  161. BlindSignatureDleq,
  162. CheckStateRequest,
  163. CheckStateResponse,
  164. ContactInfo,
  165. CurrencyUnit,
  166. ErrorCode,
  167. ErrorResponse,
  168. HTLCWitness,
  169. Keys,
  170. KeysResponse,
  171. KeysetResponse,
  172. KeySet,
  173. KeySetInfo,
  174. MeltRequest<String>,
  175. MeltQuoteBolt11Request,
  176. MeltQuoteBolt11Response<String>,
  177. MeltQuoteState,
  178. MeltMethodSettings,
  179. MintRequest<String>,
  180. MintResponse,
  181. MintInfo,
  182. MintQuoteBolt11Request,
  183. MintQuoteBolt11Response<String>,
  184. MintQuoteState,
  185. MintMethodSettings,
  186. MintVersion,
  187. Mpp,
  188. MppMethodSettings,
  189. Nuts,
  190. P2PKWitness,
  191. PaymentMethod,
  192. Proof,
  193. ProofDleq,
  194. ProofState,
  195. PublicKey,
  196. RestoreRequest,
  197. RestoreResponse,
  198. SecretKey,
  199. State,
  200. SupportedSettings,
  201. SwapRequest,
  202. SwapResponse,
  203. Witness,
  204. nut04::Settings,
  205. nut05::Settings,
  206. nut15::Settings
  207. ],
  208. auth_schemas: [MintAuthRequest],
  209. auth_paths: [
  210. crate::auth::get_auth_keysets,
  211. crate::auth::get_blind_auth_keys,
  212. crate::auth::post_mint_auth
  213. ]
  214. }
  215. /// Create mint [`Router`] with required endpoints for cashu mint with the default cache
  216. pub async fn create_mint_router(mint: Arc<Mint>, include_bolt12: bool) -> Result<Router> {
  217. create_mint_router_with_custom_cache(mint, Default::default(), include_bolt12).await
  218. }
  219. async fn cors_middleware(
  220. req: axum::http::Request<axum::body::Body>,
  221. next: axum::middleware::Next,
  222. ) -> Response {
  223. #[cfg(feature = "auth")]
  224. let allowed_headers = "Content-Type, Clear-auth, Blind-auth";
  225. #[cfg(not(feature = "auth"))]
  226. let allowed_headers = "Content-Type";
  227. // Handle preflight requests
  228. if req.method() == axum::http::Method::OPTIONS {
  229. let mut response = Response::new("".into());
  230. response
  231. .headers_mut()
  232. .insert("Access-Control-Allow-Origin", "*".parse().unwrap());
  233. response.headers_mut().insert(
  234. "Access-Control-Allow-Methods",
  235. "GET, POST, OPTIONS".parse().unwrap(),
  236. );
  237. response.headers_mut().insert(
  238. "Access-Control-Allow-Headers",
  239. allowed_headers.parse().unwrap(),
  240. );
  241. return response;
  242. }
  243. // Call the next handler
  244. let mut response = next.run(req).await;
  245. response
  246. .headers_mut()
  247. .insert("Access-Control-Allow-Origin", "*".parse().unwrap());
  248. response.headers_mut().insert(
  249. "Access-Control-Allow-Methods",
  250. "GET, POST, OPTIONS".parse().unwrap(),
  251. );
  252. response.headers_mut().insert(
  253. "Access-Control-Allow-Headers",
  254. allowed_headers.parse().unwrap(),
  255. );
  256. response
  257. }
  258. /// Create mint [`Router`] with required endpoints for cashu mint with a custom
  259. /// backend for cache
  260. pub async fn create_mint_router_with_custom_cache(
  261. mint: Arc<Mint>,
  262. cache: HttpCache,
  263. include_bolt12: bool,
  264. ) -> Result<Router> {
  265. let state = MintState {
  266. mint,
  267. cache: Arc::new(cache),
  268. };
  269. let v1_router = Router::new()
  270. .route("/keys", get(get_keys))
  271. .route("/keysets", get(get_keysets))
  272. .route("/keys/{keyset_id}", get(get_keyset_pubkeys))
  273. .route("/swap", post(cache_post_swap))
  274. .route("/mint/quote/bolt11", post(post_mint_bolt11_quote))
  275. .route(
  276. "/mint/quote/bolt11/{quote_id}",
  277. get(get_check_mint_bolt11_quote),
  278. )
  279. .route("/mint/bolt11", post(cache_post_mint_bolt11))
  280. .route("/melt/quote/bolt11", post(post_melt_bolt11_quote))
  281. .route("/ws", get(ws_handler))
  282. .route(
  283. "/melt/quote/bolt11/{quote_id}",
  284. get(get_check_melt_bolt11_quote),
  285. )
  286. .route("/melt/bolt11", post(cache_post_melt_bolt11))
  287. .route("/checkstate", post(post_check))
  288. .route("/info", get(get_mint_info))
  289. .route("/restore", post(post_restore));
  290. let mint_router = Router::new().nest("/v1", v1_router);
  291. #[cfg(feature = "auth")]
  292. let mint_router = {
  293. let auth_router = create_auth_router(state.clone());
  294. mint_router.nest("/v1", auth_router)
  295. };
  296. // Conditionally create and merge bolt12_router
  297. let mint_router = if include_bolt12 {
  298. let bolt12_router = create_bolt12_router(state.clone());
  299. mint_router.nest("/v1", bolt12_router)
  300. } else {
  301. mint_router
  302. };
  303. #[cfg(feature = "prometheus")]
  304. let mint_router = mint_router.layer(axum::middleware::from_fn_with_state(
  305. state.clone(),
  306. metrics::global_metrics_middleware,
  307. ));
  308. let mint_router = mint_router
  309. .layer(from_fn(cors_middleware))
  310. .with_state(state);
  311. Ok(mint_router)
  312. }
  313. fn create_bolt12_router(state: MintState) -> Router<MintState> {
  314. Router::new()
  315. .route("/melt/quote/bolt12", post(post_melt_bolt12_quote))
  316. .route(
  317. "/melt/quote/bolt12/{quote_id}",
  318. get(get_check_melt_bolt11_quote),
  319. )
  320. .route("/melt/bolt12", post(cache_post_melt_bolt12))
  321. .route("/mint/quote/bolt12", post(post_mint_bolt12_quote))
  322. .route(
  323. "/mint/quote/bolt12/{quote_id}",
  324. get(get_check_mint_bolt12_quote),
  325. )
  326. .route("/mint/bolt12", post(cache_post_mint_bolt12))
  327. .with_state(state)
  328. }