lib.rs 9.8 KB

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