lib.rs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. //! Axum server for Mint
  2. #![doc = include_str!("../README.md")]
  3. use std::sync::Arc;
  4. use anyhow::Result;
  5. #[cfg(feature = "auth")]
  6. use auth::create_auth_router;
  7. use axum::middleware::from_fn;
  8. use axum::response::Response;
  9. use axum::routing::{get, post};
  10. use axum::Router;
  11. use cache::HttpCache;
  12. use cdk::mint::Mint;
  13. use router_handlers::*;
  14. mod metrics;
  15. #[cfg(feature = "auth")]
  16. mod auth;
  17. mod bolt12_router;
  18. pub mod cache;
  19. mod router_handlers;
  20. mod ws;
  21. #[cfg(feature = "swagger")]
  22. mod swagger_imports {
  23. pub use cdk::amount::Amount;
  24. pub use cdk::error::{ErrorCode, ErrorResponse};
  25. pub use cdk::nuts::nut00::{
  26. BlindSignature, BlindedMessage, CurrencyUnit, PaymentMethod, Proof, Witness,
  27. };
  28. pub use cdk::nuts::nut01::{Keys, KeysResponse, PublicKey, SecretKey};
  29. pub use cdk::nuts::nut02::{KeySet, KeySetInfo, KeysetResponse};
  30. pub use cdk::nuts::nut03::{SwapRequest, SwapResponse};
  31. pub use cdk::nuts::nut04::{MintMethodSettings, MintRequest, MintResponse};
  32. pub use cdk::nuts::nut05::{MeltMethodSettings, MeltRequest};
  33. pub use cdk::nuts::nut06::{ContactInfo, MintInfo, MintVersion, Nuts, SupportedSettings};
  34. pub use cdk::nuts::nut07::{CheckStateRequest, CheckStateResponse, ProofState, State};
  35. pub use cdk::nuts::nut09::{RestoreRequest, RestoreResponse};
  36. pub use cdk::nuts::nut11::P2PKWitness;
  37. pub use cdk::nuts::nut12::{BlindSignatureDleq, ProofDleq};
  38. pub use cdk::nuts::nut14::HTLCWitness;
  39. pub use cdk::nuts::nut15::{Mpp, MppMethodSettings};
  40. pub use cdk::nuts::nut23::{
  41. MeltQuoteBolt11Request, MeltQuoteBolt11Response, MintQuoteBolt11Request,
  42. MintQuoteBolt11Response,
  43. };
  44. #[cfg(feature = "auth")]
  45. pub use cdk::nuts::MintAuthRequest;
  46. pub use cdk::nuts::{nut04, nut05, nut15, MeltQuoteState, MintQuoteState};
  47. }
  48. #[cfg(feature = "swagger")]
  49. use swagger_imports::*;
  50. use crate::bolt12_router::{
  51. cache_post_melt_bolt12, cache_post_mint_bolt12, get_check_mint_bolt12_quote,
  52. post_melt_bolt12_quote, post_mint_bolt12_quote,
  53. };
  54. /// CDK Mint State
  55. #[derive(Clone)]
  56. pub struct MintState {
  57. mint: Arc<Mint>,
  58. cache: Arc<cache::HttpCache>,
  59. }
  60. #[cfg(feature = "swagger")]
  61. macro_rules! define_api_doc {
  62. (
  63. schemas: [$($schema:ty),* $(,)?]
  64. $(, auth_schemas: [$($auth_schema:ty),* $(,)?])?
  65. $(, paths: [$($path:path),* $(,)?])?
  66. $(, auth_paths: [$($auth_path:path),* $(,)?])?
  67. ) => {
  68. #[derive(utoipa::OpenApi)]
  69. #[openapi(
  70. components(schemas(
  71. $($schema,)*
  72. $($($auth_schema,)*)?
  73. )),
  74. info(description = "Cashu CDK mint APIs", title = "cdk-mintd"),
  75. paths(
  76. get_keys,
  77. get_keyset_pubkeys,
  78. get_keysets,
  79. get_mint_info,
  80. post_mint_bolt11_quote,
  81. get_check_mint_bolt11_quote,
  82. post_mint_bolt11,
  83. post_melt_bolt11_quote,
  84. get_check_melt_bolt11_quote,
  85. post_melt_bolt11,
  86. post_swap,
  87. post_check,
  88. post_restore
  89. $(,$($path,)*)?
  90. $(,$($auth_path,)*)?
  91. )
  92. )]
  93. /// Swagger api docs
  94. pub struct ApiDoc;
  95. };
  96. }
  97. // Configuration without auth feature
  98. #[cfg(all(feature = "swagger", not(feature = "auth")))]
  99. define_api_doc! {
  100. schemas: [
  101. Amount,
  102. BlindedMessage,
  103. BlindSignature,
  104. BlindSignatureDleq,
  105. CheckStateRequest,
  106. CheckStateResponse,
  107. ContactInfo,
  108. CurrencyUnit,
  109. ErrorCode,
  110. ErrorResponse,
  111. HTLCWitness,
  112. Keys,
  113. KeysResponse,
  114. KeysetResponse,
  115. KeySet,
  116. KeySetInfo,
  117. MeltRequest<String>,
  118. MeltQuoteBolt11Request,
  119. MeltQuoteBolt11Response<String>,
  120. MeltQuoteState,
  121. MeltMethodSettings,
  122. MintRequest<String>,
  123. MintResponse,
  124. MintInfo,
  125. MintQuoteBolt11Request,
  126. MintQuoteBolt11Response<String>,
  127. MintQuoteState,
  128. MintMethodSettings,
  129. MintVersion,
  130. Mpp,
  131. MppMethodSettings,
  132. Nuts,
  133. P2PKWitness,
  134. PaymentMethod,
  135. Proof,
  136. ProofDleq,
  137. ProofState,
  138. PublicKey,
  139. RestoreRequest,
  140. RestoreResponse,
  141. SecretKey,
  142. State,
  143. SupportedSettings,
  144. SwapRequest,
  145. SwapResponse,
  146. Witness,
  147. nut04::Settings,
  148. nut05::Settings,
  149. nut15::Settings
  150. ]
  151. }
  152. // Configuration with auth feature
  153. #[cfg(all(feature = "swagger", feature = "auth"))]
  154. define_api_doc! {
  155. schemas: [
  156. Amount,
  157. BlindedMessage,
  158. BlindSignature,
  159. BlindSignatureDleq,
  160. CheckStateRequest,
  161. CheckStateResponse,
  162. ContactInfo,
  163. CurrencyUnit,
  164. ErrorCode,
  165. ErrorResponse,
  166. HTLCWitness,
  167. Keys,
  168. KeysResponse,
  169. KeysetResponse,
  170. KeySet,
  171. KeySetInfo,
  172. MeltRequest<String>,
  173. MeltQuoteBolt11Request,
  174. MeltQuoteBolt11Response<String>,
  175. MeltQuoteState,
  176. MeltMethodSettings,
  177. MintRequest<String>,
  178. MintResponse,
  179. MintInfo,
  180. MintQuoteBolt11Request,
  181. MintQuoteBolt11Response<String>,
  182. MintQuoteState,
  183. MintMethodSettings,
  184. MintVersion,
  185. Mpp,
  186. MppMethodSettings,
  187. Nuts,
  188. P2PKWitness,
  189. PaymentMethod,
  190. Proof,
  191. ProofDleq,
  192. ProofState,
  193. PublicKey,
  194. RestoreRequest,
  195. RestoreResponse,
  196. SecretKey,
  197. State,
  198. SupportedSettings,
  199. SwapRequest,
  200. SwapResponse,
  201. Witness,
  202. nut04::Settings,
  203. nut05::Settings,
  204. nut15::Settings
  205. ],
  206. auth_schemas: [MintAuthRequest],
  207. auth_paths: [
  208. crate::auth::get_auth_keysets,
  209. crate::auth::get_blind_auth_keys,
  210. crate::auth::post_mint_auth
  211. ]
  212. }
  213. /// Create mint [`Router`] with required endpoints for cashu mint with the default cache
  214. pub async fn create_mint_router(mint: Arc<Mint>, include_bolt12: bool) -> Result<Router> {
  215. create_mint_router_with_custom_cache(mint, Default::default(), include_bolt12).await
  216. }
  217. async fn cors_middleware(
  218. req: axum::http::Request<axum::body::Body>,
  219. next: axum::middleware::Next,
  220. ) -> Response {
  221. #[cfg(feature = "auth")]
  222. let allowed_headers = "Content-Type, Clear-auth, Blind-auth";
  223. #[cfg(not(feature = "auth"))]
  224. let allowed_headers = "Content-Type";
  225. // Handle preflight requests
  226. if req.method() == axum::http::Method::OPTIONS {
  227. let mut response = Response::new("".into());
  228. response.headers_mut().insert(
  229. "Access-Control-Allow-Origin",
  230. "*".parse().expect("Valid header value"),
  231. );
  232. response.headers_mut().insert(
  233. "Access-Control-Allow-Methods",
  234. "GET, POST, OPTIONS".parse().expect("Valid header value"),
  235. );
  236. response.headers_mut().insert(
  237. "Access-Control-Allow-Headers",
  238. allowed_headers.parse().expect("Valid header value"),
  239. );
  240. return response;
  241. }
  242. // Call the next handler
  243. let mut response = next.run(req).await;
  244. response.headers_mut().insert(
  245. "Access-Control-Allow-Origin",
  246. "*".parse().expect("Valid header value"),
  247. );
  248. response.headers_mut().insert(
  249. "Access-Control-Allow-Methods",
  250. "GET, POST, OPTIONS".parse().expect("Valid header value"),
  251. );
  252. response.headers_mut().insert(
  253. "Access-Control-Allow-Headers",
  254. allowed_headers.parse().expect("Valid header value"),
  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. }