From 0558320dfe9bd704f9308f617ec437a0bb84896b Mon Sep 17 00:00:00 2001 From: ok300 <106775972+ok300@users.noreply.github.com> Date: Sat, 5 Oct 2024 11:53:34 +0200 Subject: [PATCH] mintd: add utoipa swagger UI --- .github/workflows/ci.yml | 2 + crates/cdk-axum/Cargo.toml | 4 + crates/cdk-axum/src/lib.rs | 111 ++++++++++++++- crates/cdk-axum/src/router_handlers.rs | 174 +++++++++++++++++++++++- crates/cdk-mintd/Cargo.toml | 5 + crates/cdk-mintd/example.config.toml | 1 + crates/cdk-mintd/src/config.rs | 7 + crates/cdk-mintd/src/main.rs | 14 +- crates/cdk/Cargo.toml | 2 + crates/cdk/src/amount.rs | 1 + crates/cdk/src/error.rs | 4 +- crates/cdk/src/nuts/nut00/mod.rs | 10 ++ crates/cdk/src/nuts/nut01/mod.rs | 2 + crates/cdk/src/nuts/nut01/public_key.rs | 2 + crates/cdk/src/nuts/nut01/secret_key.rs | 2 + crates/cdk/src/nuts/nut02.rs | 5 + crates/cdk/src/nuts/nut03.rs | 3 + crates/cdk/src/nuts/nut04.rs | 9 ++ crates/cdk/src/nuts/nut05.rs | 11 +- crates/cdk/src/nuts/nut06.rs | 5 + crates/cdk/src/nuts/nut07.rs | 8 +- crates/cdk/src/nuts/nut09.rs | 2 + crates/cdk/src/nuts/nut11/mod.rs | 1 + crates/cdk/src/nuts/nut12.rs | 7 + crates/cdk/src/nuts/nut14/mod.rs | 1 + crates/cdk/src/nuts/nut15.rs | 3 + flake.nix | 2 +- 27 files changed, 389 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cd3956f8c..37149ba2b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,6 +46,7 @@ jobs: -p cdk --no-default-features, -p cdk --no-default-features --features wallet, -p cdk --no-default-features --features mint, + -p cdk --no-default-features --features "mint swagger", -p cdk-redb, -p cdk-sqlite, -p cdk-axum, @@ -143,6 +144,7 @@ jobs: -p cdk --no-default-features, -p cdk --no-default-features --features wallet, -p cdk --no-default-features --features mint, + -p cdk --no-default-features --features "mint swagger", -p cdk-axum, -p cdk-strike, -p cdk-lnbits, diff --git a/crates/cdk-axum/Cargo.toml b/crates/cdk-axum/Cargo.toml index 45a71d4b5..241da9627 100644 --- a/crates/cdk-axum/Cargo.toml +++ b/crates/cdk-axum/Cargo.toml @@ -15,7 +15,11 @@ axum = "0.6.20" cdk = { path = "../cdk", version = "0.4.0", default-features = false, features = ["mint"] } tokio = { version = "1", default-features = false } tracing = { version = "0.1", default-features = false, features = ["attributes", "log"] } +utoipa = { version = "4", features = ["preserve_order", "preserve_path_order"], optional = true } futures = { version = "0.3.28", default-features = false } moka = { version = "0.11.1", features = ["future"] } serde_json = "1" paste = "1.0.15" + +[features] +swagger = ["cdk/swagger", "dep:utoipa"] \ No newline at end of file diff --git a/crates/cdk-axum/src/lib.rs b/crates/cdk-axum/src/lib.rs index 35078446d..9083163b0 100644 --- a/crates/cdk-axum/src/lib.rs +++ b/crates/cdk-axum/src/lib.rs @@ -4,6 +4,7 @@ #![warn(rustdoc::bare_urls)] use std::sync::Arc; +use std::time::Duration; use anyhow::Result; use axum::routing::{get, post}; @@ -11,10 +12,42 @@ use axum::Router; use cdk::mint::Mint; use moka::future::Cache; use router_handlers::*; -use std::time::Duration; mod router_handlers; +#[cfg(feature = "swagger")] +mod swagger_imports { + pub use cdk::amount::Amount; + pub use cdk::error::{ErrorCode, ErrorResponse}; + pub use cdk::nuts::nut00::{ + BlindSignature, BlindedMessage, CurrencyUnit, PaymentMethod, Proof, Witness, + }; + pub use cdk::nuts::nut01::{Keys, KeysResponse, PublicKey, SecretKey}; + pub use cdk::nuts::nut02::{Id, KeySet, KeySetInfo, KeySetVersion, KeysetResponse}; + pub use cdk::nuts::nut03::{SwapRequest, SwapResponse}; + pub use cdk::nuts::nut04; + pub use cdk::nuts::nut04::{ + MintBolt11Request, MintBolt11Response, MintMethodSettings, MintQuoteBolt11Request, + MintQuoteBolt11Response, + }; + pub use cdk::nuts::nut05; + pub use cdk::nuts::nut05::{ + MeltBolt11Request, MeltMethodSettings, MeltQuoteBolt11Request, MeltQuoteBolt11Response, + }; + pub use cdk::nuts::nut06::{ContactInfo, MintInfo, MintVersion, Nuts, SupportedSettings}; + pub use cdk::nuts::nut07::{CheckStateRequest, CheckStateResponse, ProofState, State}; + pub use cdk::nuts::nut09::{RestoreRequest, RestoreResponse}; + pub use cdk::nuts::nut11::P2PKWitness; + pub use cdk::nuts::nut12::{BlindSignatureDleq, ProofDleq}; + pub use cdk::nuts::nut14::HTLCWitness; + pub use cdk::nuts::nut15; + pub use cdk::nuts::nut15::{Mpp, MppMethodSettings}; + pub use cdk::nuts::{MeltQuoteState, MintQuoteState}; +} + +#[cfg(feature = "swagger")] +use swagger_imports::*; + /// CDK Mint State #[derive(Clone)] pub struct MintState { @@ -22,6 +55,82 @@ pub struct MintState { cache: Cache, } +#[cfg(feature = "swagger")] +#[derive(utoipa::OpenApi)] +#[openapi( + components(schemas( + Amount, + BlindedMessage, + BlindSignature, + BlindSignatureDleq, + CheckStateRequest, + CheckStateResponse, + ContactInfo, + CurrencyUnit, + ErrorCode, + ErrorResponse, + HTLCWitness, + Id, + Keys, + KeysResponse, + KeysetResponse, + KeySet, + KeySetInfo, + KeySetVersion, + MeltBolt11Request, + MeltQuoteBolt11Request, + MeltQuoteBolt11Response, + MeltQuoteState, + MeltMethodSettings, + MintBolt11Request, + MintBolt11Response, + MintInfo, + MintQuoteBolt11Request, + MintQuoteBolt11Response, + MintQuoteState, + MintMethodSettings, + MintVersion, + Mpp, + MppMethodSettings, + Nuts, + P2PKWitness, + PaymentMethod, + Proof, + ProofDleq, + ProofState, + PublicKey, + RestoreRequest, + RestoreResponse, + SecretKey, + State, + SupportedSettings, + SwapRequest, + SwapResponse, + Witness, + nut04::Settings, + nut05::Settings, + nut15::Settings + )), + info(description = "Cashu CDK mint APIs", title = "cdk-mintd",), + paths( + get_keys, + get_keyset_pubkeys, + get_keysets, + get_mint_info, + get_mint_bolt11_quote, + get_check_mint_bolt11_quote, + post_mint_bolt11, + get_melt_bolt11_quote, + get_check_melt_bolt11_quote, + post_melt_bolt11, + post_swap, + post_check, + post_restore + ) +)] +/// OpenAPI spec for the mint's v1 APIs +pub struct ApiDocV1; + /// Create mint [`Router`] with required endpoints for cashu mint pub async fn create_mint_router(mint: Arc, cache_ttl: u64, cache_tti: u64) -> Result { let state = MintState { diff --git a/crates/cdk-axum/src/router_handlers.rs b/crates/cdk-axum/src/router_handlers.rs index 0ee99d896..c4ba781ab 100644 --- a/crates/cdk-axum/src/router_handlers.rs +++ b/crates/cdk-axum/src/router_handlers.rs @@ -49,6 +49,17 @@ post_cache_wrapper!(post_swap, SwapRequest, SwapResponse); post_cache_wrapper!(post_mint_bolt11, MintBolt11Request, MintBolt11Response); post_cache_wrapper!(post_melt_bolt11, MeltBolt11Request, MeltQuoteBolt11Response); +#[cfg_attr(feature = "swagger", utoipa::path( + get, + context_path = "/v1", + path = "/keys", + responses( + (status = 200, description = "Successful response", body = KeysResponse, content_type = "application/json") + ) +))] +/// Get the public keys of the newest mint keyset +/// +/// This endpoint returns a dictionary of all supported token values of the mint and their associated public key. pub async fn get_keys(State(state): State) -> Result, Response> { let pubkeys = state.mint.pubkeys().await.map_err(|err| { tracing::error!("Could not get keys: {}", err); @@ -58,6 +69,21 @@ pub async fn get_keys(State(state): State) -> Result, Path(keyset_id): Path, @@ -70,15 +96,40 @@ pub async fn get_keyset_pubkeys( Ok(Json(pubkeys)) } +#[cfg_attr(feature = "swagger", utoipa::path( + get, + context_path = "/v1", + path = "/keysets", + responses( + (status = 200, description = "Successful response", body = KeysetResponse, content_type = "application/json"), + (status = 500, description = "Server error", body = ErrorResponse, content_type = "application/json") + ) +))] +/// Get all active keyset IDs of the mint +/// +/// This endpoint returns a list of keysets that the mint currently supports and will accept tokens from. pub async fn get_keysets(State(state): State) -> Result, Response> { - let mint = state.mint.keysets().await.map_err(|err| { - tracing::error!("Could not get keyset: {}", err); + let keysets = state.mint.keysets().await.map_err(|err| { + tracing::error!("Could not get keysets: {}", err); into_response(err) })?; - Ok(Json(mint)) + Ok(Json(keysets)) } +#[cfg_attr(feature = "swagger", utoipa::path( + post, + context_path = "/v1", + path = "/mint/quote/bolt11", + request_body(content = MintQuoteBolt11Request, description = "Request params", content_type = "application/json"), + responses( + (status = 200, description = "Successful response", body = MintQuoteBolt11Response, content_type = "application/json"), + (status = 500, description = "Server error", body = ErrorResponse, content_type = "application/json") + ) +))] +/// Request a quote for minting of new tokens +/// +/// Request minting of new tokens. The mint responds with a Lightning invoice. This endpoint can be used for a Lightning invoice UX flow. pub async fn get_mint_bolt11_quote( State(state): State, Json(payload): Json, @@ -92,6 +143,21 @@ pub async fn get_mint_bolt11_quote( Ok(Json(quote)) } +#[cfg_attr(feature = "swagger", utoipa::path( + get, + context_path = "/v1", + path = "/mint/quote/bolt11/{quote_id}", + params( + ("quote_id" = String, description = "The quote ID"), + ), + responses( + (status = 200, description = "Successful response", body = MintQuoteBolt11Response, content_type = "application/json"), + (status = 500, description = "Server error", body = ErrorResponse, content_type = "application/json") + ) +))] +/// Get mint quote by ID +/// +/// Get mint quote state. pub async fn get_check_mint_bolt11_quote( State(state): State, Path(quote_id): Path, @@ -108,6 +174,21 @@ pub async fn get_check_mint_bolt11_quote( Ok(Json(quote)) } +#[cfg_attr(feature = "swagger", utoipa::path( + post, + context_path = "/v1", + path = "/mint/bolt11", + request_body(content = MintBolt11Request, description = "Request params", content_type = "application/json"), + responses( + (status = 200, description = "Successful response", body = MintBolt11Response, content_type = "application/json"), + (status = 500, description = "Server error", body = ErrorResponse, content_type = "application/json") + ) +))] +/// Mint tokens by paying a BOLT11 Lightning invoice. +/// +/// Requests the minting of tokens belonging to a paid payment request. +/// +/// Call this endpoint after `POST /v1/mint/quote`. pub async fn post_mint_bolt11( State(state): State, Json(payload): Json, @@ -124,6 +205,17 @@ pub async fn post_mint_bolt11( Ok(Json(res)) } +#[cfg_attr(feature = "swagger", utoipa::path( + post, + context_path = "/v1", + path = "/melt/quote/bolt11", + request_body(content = MeltQuoteBolt11Request, description = "Quote params", content_type = "application/json"), + responses( + (status = 200, description = "Successful response", body = MeltQuoteBolt11Response, content_type = "application/json"), + (status = 500, description = "Server error", body = ErrorResponse, content_type = "application/json") + ) +))] +/// Request a quote for melting tokens pub async fn get_melt_bolt11_quote( State(state): State, Json(payload): Json, @@ -137,6 +229,21 @@ pub async fn get_melt_bolt11_quote( Ok(Json(quote)) } +#[cfg_attr(feature = "swagger", utoipa::path( + get, + context_path = "/v1", + path = "/melt/quote/bolt11/{quote_id}", + params( + ("quote_id" = String, description = "The quote ID"), + ), + responses( + (status = 200, description = "Successful response", body = MeltQuoteBolt11Response, content_type = "application/json"), + (status = 500, description = "Server error", body = ErrorResponse, content_type = "application/json") + ) +))] +/// Get melt quote by ID +/// +/// Get melt quote state. pub async fn get_check_melt_bolt11_quote( State(state): State, Path(quote_id): Path, @@ -153,6 +260,19 @@ pub async fn get_check_melt_bolt11_quote( Ok(Json(quote)) } +#[cfg_attr(feature = "swagger", utoipa::path( + post, + context_path = "/v1", + path = "/melt/bolt11", + request_body(content = MeltBolt11Request, description = "Melt params", content_type = "application/json"), + responses( + (status = 200, description = "Successful response", body = MeltQuoteBolt11Response, content_type = "application/json"), + (status = 500, description = "Server error", body = ErrorResponse, content_type = "application/json") + ) +))] +/// Melt tokens for a Bitcoin payment that the mint will make for the user in exchange +/// +/// Requests tokens to be destroyed and sent out via Lightning. pub async fn post_melt_bolt11( State(state): State, Json(payload): Json, @@ -166,6 +286,19 @@ pub async fn post_melt_bolt11( Ok(Json(res)) } +#[cfg_attr(feature = "swagger", utoipa::path( + post, + context_path = "/v1", + path = "/checkstate", + request_body(content = CheckStateRequest, description = "State params", content_type = "application/json"), + responses( + (status = 200, description = "Successful response", body = CheckStateResponse, content_type = "application/json"), + (status = 500, description = "Server error", body = ErrorResponse, content_type = "application/json") + ) +))] +/// Check whether a proof is spent already or is pending in a transaction +/// +/// Check whether a secret has been spent already or not. pub async fn post_check( State(state): State, Json(payload): Json, @@ -178,10 +311,34 @@ pub async fn post_check( Ok(Json(state)) } +#[cfg_attr(feature = "swagger", utoipa::path( + get, + context_path = "/v1", + path = "/info", + responses( + (status = 200, description = "Successful response", body = MintInfo) + ) +))] +/// Mint information, operator contact information, and other info pub async fn get_mint_info(State(state): State) -> Result, Response> { Ok(Json(state.mint.mint_info().clone().time(unix_time()))) } +#[cfg_attr(feature = "swagger", utoipa::path( + post, + context_path = "/v1", + path = "/swap", + request_body(content = SwapRequest, description = "Swap params", content_type = "application/json"), + responses( + (status = 200, description = "Successful response", body = SwapResponse, content_type = "application/json"), + (status = 500, description = "Server error", body = ErrorResponse, content_type = "application/json") + ) +))] +/// Swap inputs for outputs of the same value +/// +/// Requests a set of Proofs to be swapped for another set of BlindSignatures. +/// +/// This endpoint can be used by Alice to swap a set of proofs before making a payment to Carol. It can then used by Carol to redeem the tokens for new proofs. pub async fn post_swap( State(state): State, Json(payload): Json, @@ -197,6 +354,17 @@ pub async fn post_swap( Ok(Json(swap_response)) } +#[cfg_attr(feature = "swagger", utoipa::path( + post, + context_path = "/v1", + path = "/restore", + request_body(content = RestoreRequest, description = "Restore params", content_type = "application/json"), + responses( + (status = 200, description = "Successful response", body = RestoreResponse, content_type = "application/json"), + (status = 500, description = "Server error", body = ErrorResponse, content_type = "application/json") + ) +))] +/// Restores blind signature for a set of outputs. pub async fn post_restore( State(state): State, Json(payload): Json, diff --git a/crates/cdk-mintd/Cargo.toml b/crates/cdk-mintd/Cargo.toml index c61db669c..34640215e 100644 --- a/crates/cdk-mintd/Cargo.toml +++ b/crates/cdk-mintd/Cargo.toml @@ -34,3 +34,8 @@ tower-http = { version = "0.4.4", features = ["cors"] } lightning-invoice = { version = "0.32.0", features = ["serde", "std"] } home = "0.5.5" url = "2.3" +utoipa = { version = "4", optional = true } +utoipa-swagger-ui = { version = "4", features = ["axum"], optional = true } + +[features] +swagger = ["cdk-axum/swagger", "dep:utoipa", "dep:utoipa-swagger-ui"] \ No newline at end of file diff --git a/crates/cdk-mintd/example.config.toml b/crates/cdk-mintd/example.config.toml index 7878d1254..448f1f7a6 100644 --- a/crates/cdk-mintd/example.config.toml +++ b/crates/cdk-mintd/example.config.toml @@ -4,6 +4,7 @@ listen_host = "127.0.0.1" listen_port = 8085 mnemonic = "" # input_fee_ppk = 0 +# enable_swagger_ui = false diff --git a/crates/cdk-mintd/src/config.rs b/crates/cdk-mintd/src/config.rs index 756c2d148..5e94f69bc 100644 --- a/crates/cdk-mintd/src/config.rs +++ b/crates/cdk-mintd/src/config.rs @@ -15,6 +15,12 @@ pub struct Info { pub seconds_to_cache_requests_for: Option, pub seconds_to_extend_cache_by: Option, pub input_fee_ppk: Option, + + /// When this is set to true, the mint exposes a Swagger UI for it's API at + /// `[listen_host]:[listen_port]/swagger-ui` + /// + /// This requires `mintd` was built with the `swagger` feature flag. + pub enable_swagger_ui: Option, } #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)] @@ -94,6 +100,7 @@ pub struct Database { pub engine: DatabaseEngine, } +/// CDK settings, derived from `config.toml` #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct Settings { pub info: Info, diff --git a/crates/cdk-mintd/src/main.rs b/crates/cdk-mintd/src/main.rs index 6462236b4..4adc2809d 100644 --- a/crates/cdk-mintd/src/main.rs +++ b/crates/cdk-mintd/src/main.rs @@ -36,6 +36,8 @@ use tokio::sync::{Mutex, Notify}; use tower_http::cors::CorsLayer; use tracing_subscriber::EnvFilter; use url::Url; +#[cfg(feature = "swagger")] +use utoipa::OpenApi; mod cli; mod config; @@ -451,7 +453,7 @@ async fn main() -> anyhow::Result<()> { // Checks the status of all pending melt quotes // Pending melt quotes where the payment has gone through inputs are burnt - // Pending melt quotes where the paynment has **failed** inputs are reset to unspent + // Pending melt quotes where the payment has **failed** inputs are reset to unspent check_pending_melt_quotes(Arc::clone(&mint), &ln_backends).await?; let listen_addr = settings.info.listen_host; @@ -475,6 +477,16 @@ async fn main() -> anyhow::Result<()> { .merge(v1_service) .layer(CorsLayer::permissive()); + #[cfg(feature = "swagger")] + { + if settings.info.enable_swagger_ui.unwrap_or(false) { + mint_service = mint_service.merge( + utoipa_swagger_ui::SwaggerUi::new("/swagger-ui") + .url("/api-docs/openapi.json", cdk_axum::ApiDocV1::openapi()), + ); + } + } + for router in ln_routers { mint_service = mint_service.merge(router); } diff --git a/crates/cdk/Cargo.toml b/crates/cdk/Cargo.toml index 6df40fdb4..b72e50d23 100644 --- a/crates/cdk/Cargo.toml +++ b/crates/cdk/Cargo.toml @@ -13,6 +13,7 @@ license = "MIT" [features] default = ["mint", "wallet"] mint = ["dep:futures"] +swagger = ["mint", "dep:utoipa"] wallet = ["dep:reqwest"] bench = [] @@ -39,6 +40,7 @@ tracing = { version = "0.1", default-features = false, features = ["attributes", thiserror = "1" futures = { version = "0.3.28", default-features = false, optional = true } url = "2.3" +utoipa = { version = "4", optional = true } uuid = { version = "1", features = ["v4"] } # -Z minimal-versions diff --git a/crates/cdk/src/amount.rs b/crates/cdk/src/amount.rs index 33181dd31..d2c599d04 100644 --- a/crates/cdk/src/amount.rs +++ b/crates/cdk/src/amount.rs @@ -27,6 +27,7 @@ pub enum Error { /// Amount can be any unit #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] #[serde(transparent)] pub struct Amount(u64); diff --git a/crates/cdk/src/error.rs b/crates/cdk/src/error.rs index 5a39d7312..5ca14821b 100644 --- a/crates/cdk/src/error.rs +++ b/crates/cdk/src/error.rs @@ -242,8 +242,9 @@ pub enum Error { /// CDK Error Response /// -/// See NUT defenation in [00](https://github.com/cashubtc/nuts/blob/main/00.md) +/// See NUT definition in [00](https://github.com/cashubtc/nuts/blob/main/00.md) #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub struct ErrorResponse { /// Error Code pub code: ErrorCode, @@ -399,6 +400,7 @@ impl From for Error { /// Possible Error Codes #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub enum ErrorCode { /// Token is already spent TokenAlreadySpent, diff --git a/crates/cdk/src/nuts/nut00/mod.rs b/crates/cdk/src/nuts/nut00/mod.rs index 5b03a185b..57117bf01 100644 --- a/crates/cdk/src/nuts/nut00/mod.rs +++ b/crates/cdk/src/nuts/nut00/mod.rs @@ -103,6 +103,7 @@ pub enum Error { /// Blinded Message (also called `output`) #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub struct BlindedMessage { /// Amount /// @@ -117,6 +118,7 @@ pub struct BlindedMessage { /// /// The blinded secret message generated by the sender. #[serde(rename = "B_")] + #[cfg_attr(feature = "swagger", schema(value_type = String))] pub blinded_secret: PublicKey, /// Witness /// @@ -146,6 +148,7 @@ impl BlindedMessage { /// Blind Signature (also called `promise`) #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub struct BlindSignature { /// Amount /// @@ -160,6 +163,7 @@ pub struct BlindSignature { /// /// The blinded signature on the secret message `B_` of [BlindedMessage]. #[serde(rename = "C_")] + #[cfg_attr(feature = "swagger", schema(value_type = String))] pub c: PublicKey, /// DLEQ Proof /// @@ -183,6 +187,7 @@ impl PartialOrd for BlindSignature { /// Witness #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(untagged)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub enum Witness { /// P2PK Witness #[serde(with = "serde_p2pk_witness")] @@ -226,6 +231,7 @@ impl Witness { /// Proofs #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub struct Proof { /// Amount pub amount: Amount, @@ -233,9 +239,11 @@ pub struct Proof { #[serde(rename = "id")] pub keyset_id: Id, /// Secret message + #[cfg_attr(feature = "swagger", schema(value_type = String))] pub secret: Secret, /// Unblinded signature #[serde(rename = "C")] + #[cfg_attr(feature = "swagger", schema(value_type = String))] pub c: PublicKey, /// Witness #[serde(skip_serializing_if = "Option::is_none")] @@ -360,6 +368,7 @@ where /// Currency Unit #[non_exhaustive] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub enum CurrencyUnit { /// Sat #[default] @@ -436,6 +445,7 @@ impl<'de> Deserialize<'de> for CurrencyUnit { /// Payment Method #[non_exhaustive] #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub enum PaymentMethod { /// Bolt11 payment type #[default] diff --git a/crates/cdk/src/nuts/nut01/mod.rs b/crates/cdk/src/nuts/nut01/mod.rs index 12819222c..bda245fd4 100644 --- a/crates/cdk/src/nuts/nut01/mod.rs +++ b/crates/cdk/src/nuts/nut01/mod.rs @@ -43,6 +43,7 @@ pub enum Error { /// /// See [NUT-01] #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub struct Keys(BTreeMap); impl From for Keys { @@ -85,6 +86,7 @@ impl Keys { /// Mint Public Keys [NUT-01] #[serde_as] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub struct KeysResponse { /// Keysets #[serde_as(as = "VecSkipError<_>")] diff --git a/crates/cdk/src/nuts/nut01/public_key.rs b/crates/cdk/src/nuts/nut01/public_key.rs index ddb58b81f..23e2f1dee 100644 --- a/crates/cdk/src/nuts/nut01/public_key.rs +++ b/crates/cdk/src/nuts/nut01/public_key.rs @@ -13,7 +13,9 @@ use crate::SECP256K1; /// PublicKey #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub struct PublicKey { + #[cfg_attr(feature = "swagger", schema(value_type = String))] inner: secp256k1::PublicKey, } diff --git a/crates/cdk/src/nuts/nut01/secret_key.rs b/crates/cdk/src/nuts/nut01/secret_key.rs index 11b7e00b3..23eca6e34 100644 --- a/crates/cdk/src/nuts/nut01/secret_key.rs +++ b/crates/cdk/src/nuts/nut01/secret_key.rs @@ -15,7 +15,9 @@ use crate::SECP256K1; /// SecretKey #[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub struct SecretKey { + #[cfg_attr(feature = "swagger", schema(value_type = String))] inner: secp256k1::SecretKey, } diff --git a/crates/cdk/src/nuts/nut02.rs b/crates/cdk/src/nuts/nut02.rs index 6a6656e8f..32e961909 100644 --- a/crates/cdk/src/nuts/nut02.rs +++ b/crates/cdk/src/nuts/nut02.rs @@ -53,6 +53,7 @@ pub enum Error { /// Keyset version #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub enum KeySetVersion { /// Current Version 00 Version00, @@ -88,6 +89,7 @@ impl fmt::Display for KeySetVersion { /// be stored in a Cashu token such that the token can be used to identify /// which mint or keyset it was generated from. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub struct Id { version: KeySetVersion, id: [u8; Self::BYTELEN], @@ -228,6 +230,7 @@ impl From<&Keys> for Id { /// Ids of mints keyset ids #[serde_as] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub struct KeysetResponse { /// set of public key ids that the mint generates #[serde_as(as = "VecSkipError<_>")] @@ -236,6 +239,7 @@ pub struct KeysetResponse { /// Keyset #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub struct KeySet { /// Keyset [`Id`] pub id: Id, @@ -271,6 +275,7 @@ impl From for KeySet { /// KeySetInfo #[derive(Debug, Clone, Hash, PartialEq, Eq, Deserialize, Serialize)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub struct KeySetInfo { /// Keyset [`Id`] pub id: Id, diff --git a/crates/cdk/src/nuts/nut03.rs b/crates/cdk/src/nuts/nut03.rs index eca008ba4..535c89a9e 100644 --- a/crates/cdk/src/nuts/nut03.rs +++ b/crates/cdk/src/nuts/nut03.rs @@ -34,8 +34,10 @@ pub struct PreSwap { /// Split Request [NUT-06] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub struct SwapRequest { /// Proofs that are to be spent in `Split` + #[cfg_attr(feature = "swagger", schema(value_type = Vec))] pub inputs: Proofs, /// Blinded Messages for Mint to sign pub outputs: Vec, @@ -64,6 +66,7 @@ impl SwapRequest { /// Split Response [NUT-06] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub struct SwapResponse { /// Promises pub signatures: Vec, diff --git a/crates/cdk/src/nuts/nut04.rs b/crates/cdk/src/nuts/nut04.rs index fd60a57ac..3067628f6 100644 --- a/crates/cdk/src/nuts/nut04.rs +++ b/crates/cdk/src/nuts/nut04.rs @@ -26,6 +26,7 @@ pub enum Error { /// Mint quote request [NUT-04] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub struct MintQuoteBolt11Request { /// Amount pub amount: Amount, @@ -38,6 +39,7 @@ pub struct MintQuoteBolt11Request { /// Possible states of a quote #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Default, Serialize, Deserialize)] #[serde(rename_all = "UPPERCASE")] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema), schema(as = MintQuoteState))] pub enum QuoteState { /// Quote has not been paid #[default] @@ -79,6 +81,7 @@ impl FromStr for QuoteState { /// Mint quote response [NUT-04] #[derive(Debug, Clone, PartialEq, Eq, Serialize)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub struct MintQuoteBolt11Response { /// Quote Id pub quote: String, @@ -175,10 +178,13 @@ impl From for MintQuoteBolt11Response { /// Mint request [NUT-04] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub struct MintBolt11Request { /// Quote id + #[cfg_attr(feature = "swagger", schema(max_length = 1_000))] pub quote: String, /// Outputs + #[cfg_attr(feature = "swagger", schema(max_items = 1_000))] pub outputs: Vec, } @@ -196,6 +202,7 @@ impl MintBolt11Request { /// Mint response [NUT-04] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub struct MintBolt11Response { /// Blinded Signatures pub signatures: Vec, @@ -203,6 +210,7 @@ pub struct MintBolt11Response { /// Mint Method Settings #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub struct MintMethodSettings { /// Payment Method e.g. bolt11 pub method: PaymentMethod, @@ -221,6 +229,7 @@ pub struct MintMethodSettings { /// Mint Settings #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema), schema(as = nut04::Settings))] pub struct Settings { /// Methods to mint pub methods: Vec, diff --git a/crates/cdk/src/nuts/nut05.rs b/crates/cdk/src/nuts/nut05.rs index c459341d8..e7ac3153a 100644 --- a/crates/cdk/src/nuts/nut05.rs +++ b/crates/cdk/src/nuts/nut05.rs @@ -13,6 +13,7 @@ use super::nut00::{BlindSignature, BlindedMessage, CurrencyUnit, PaymentMethod, use super::nut15::Mpp; #[cfg(feature = "mint")] use crate::mint; +use crate::nuts::MeltQuoteState; use crate::{Amount, Bolt11Invoice}; /// NUT05 Error @@ -28,8 +29,10 @@ pub enum Error { /// Melt quote request [NUT-05] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub struct MeltQuoteBolt11Request { /// Bolt11 invoice to be paid + #[cfg_attr(feature = "swagger", schema(value_type = String))] pub request: Bolt11Invoice, /// Unit wallet would like to pay with pub unit: CurrencyUnit, @@ -40,6 +43,7 @@ pub struct MeltQuoteBolt11Request { /// Possible states of a quote #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Default, Serialize, Deserialize)] #[serde(rename_all = "UPPERCASE")] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema), schema(as = MeltQuoteState))] pub enum QuoteState { /// Quote has not been paid #[default] @@ -83,6 +87,7 @@ impl FromStr for QuoteState { /// Melt quote response [NUT-05] #[derive(Debug, Clone, PartialEq, Eq, Serialize)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub struct MeltQuoteBolt11Response { /// Quote Id pub quote: String, @@ -95,7 +100,7 @@ pub struct MeltQuoteBolt11Response { /// Deprecated pub paid: Option, /// Quote State - pub state: QuoteState, + pub state: MeltQuoteState, /// Unix timestamp until the quote is valid pub expiry: u64, /// Payment preimage @@ -209,10 +214,12 @@ impl From for MeltQuoteBolt11Response { /// Melt Bolt11 Request [NUT-05] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub struct MeltBolt11Request { /// Quote ID pub quote: String, /// Proofs + #[cfg_attr(feature = "swagger", schema(value_type = Vec))] pub inputs: Proofs, /// Blinded Message that can be used to return change [NUT-08] /// Amount field of BlindedMessages `SHOULD` be set to zero @@ -229,6 +236,7 @@ impl MeltBolt11Request { /// Melt Method Settings #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub struct MeltMethodSettings { /// Payment Method e.g. bolt11 pub method: PaymentMethod, @@ -266,6 +274,7 @@ impl Settings { /// Melt Settings #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema), schema(as = nut05::Settings))] pub struct Settings { /// Methods to melt pub methods: Vec, diff --git a/crates/cdk/src/nuts/nut06.rs b/crates/cdk/src/nuts/nut06.rs index e2d4465f0..358cc4ff4 100644 --- a/crates/cdk/src/nuts/nut06.rs +++ b/crates/cdk/src/nuts/nut06.rs @@ -9,6 +9,7 @@ use super::{nut04, nut05, nut15, MppMethodSettings}; /// Mint Version #[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub struct MintVersion { /// Mint Software name pub name: String, @@ -52,6 +53,7 @@ impl<'de> Deserialize<'de> for MintVersion { /// Mint Info [NIP-06] #[derive(Default, Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub struct MintInfo { /// name of the mint and should be recognizable #[serde(skip_serializing_if = "Option::is_none")] @@ -188,6 +190,7 @@ impl MintInfo { /// Supported nuts and settings #[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub struct Nuts { /// NUT04 Settings #[serde(default)] @@ -322,12 +325,14 @@ impl Nuts { /// Check state Settings #[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Hash, Serialize, Deserialize)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub struct SupportedSettings { supported: bool, } /// Contact Info #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub struct ContactInfo { /// Contact Method i.e. nostr pub method: String, diff --git a/crates/cdk/src/nuts/nut07.rs b/crates/cdk/src/nuts/nut07.rs index 3b69102d3..b56830ea1 100644 --- a/crates/cdk/src/nuts/nut07.rs +++ b/crates/cdk/src/nuts/nut07.rs @@ -21,6 +21,7 @@ pub enum Error { /// State of Proof #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "UPPERCASE")] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub enum State { /// Spent Spent, @@ -63,19 +64,23 @@ impl FromStr for State { } } -/// Check spendabale request [NUT-07] +/// Check spendable request [NUT-07] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub struct CheckStateRequest { /// Y's of the proofs to check #[serde(rename = "Ys")] + #[cfg_attr(feature = "swagger", schema(value_type = Vec, max_items = 1_000))] pub ys: Vec, } /// Proof state [NUT-07] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub struct ProofState { /// Y of proof #[serde(rename = "Y")] + #[cfg_attr(feature = "swagger", schema(value_type = String))] pub y: PublicKey, /// State of proof pub state: State, @@ -85,6 +90,7 @@ pub struct ProofState { /// Check Spendable Response [NUT-07] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub struct CheckStateResponse { /// Proof states pub states: Vec, diff --git a/crates/cdk/src/nuts/nut09.rs b/crates/cdk/src/nuts/nut09.rs index 672ef8de9..abcd8f85a 100644 --- a/crates/cdk/src/nuts/nut09.rs +++ b/crates/cdk/src/nuts/nut09.rs @@ -8,6 +8,7 @@ use super::nut00::{BlindSignature, BlindedMessage}; /// Restore Request [NUT-09] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub struct RestoreRequest { /// Outputs pub outputs: Vec, @@ -15,6 +16,7 @@ pub struct RestoreRequest { /// Restore Response [NUT-09] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub struct RestoreResponse { /// Outputs pub outputs: Vec, diff --git a/crates/cdk/src/nuts/nut11/mod.rs b/crates/cdk/src/nuts/nut11/mod.rs index 28d466ecd..6d407e2b1 100644 --- a/crates/cdk/src/nuts/nut11/mod.rs +++ b/crates/cdk/src/nuts/nut11/mod.rs @@ -88,6 +88,7 @@ pub enum Error { /// P2Pk Witness #[derive(Default, Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub struct P2PKWitness { /// Signatures pub signatures: Vec, diff --git a/crates/cdk/src/nuts/nut12.rs b/crates/cdk/src/nuts/nut12.rs index f3928a6e2..232368b04 100644 --- a/crates/cdk/src/nuts/nut12.rs +++ b/crates/cdk/src/nuts/nut12.rs @@ -41,10 +41,13 @@ pub enum Error { /// /// Defined in [NUT12](https://github.com/cashubtc/nuts/blob/main/12.md) #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub struct BlindSignatureDleq { /// e + #[cfg_attr(feature = "swagger", schema(value_type = String))] pub e: SecretKey, /// s + #[cfg_attr(feature = "swagger", schema(value_type = String))] pub s: SecretKey, } @@ -52,12 +55,16 @@ pub struct BlindSignatureDleq { /// /// Defined in [NUT12](https://github.com/cashubtc/nuts/blob/main/12.md) #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub struct ProofDleq { /// e + #[cfg_attr(feature = "swagger", schema(value_type = String))] pub e: SecretKey, /// s + #[cfg_attr(feature = "swagger", schema(value_type = String))] pub s: SecretKey, /// Blinding factor + #[cfg_attr(feature = "swagger", schema(value_type = String))] pub r: SecretKey, } diff --git a/crates/cdk/src/nuts/nut14/mod.rs b/crates/cdk/src/nuts/nut14/mod.rs index 7f53f41f5..07da54dc4 100644 --- a/crates/cdk/src/nuts/nut14/mod.rs +++ b/crates/cdk/src/nuts/nut14/mod.rs @@ -52,6 +52,7 @@ pub enum Error { /// HTLC Witness #[derive(Default, Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub struct HTLCWitness { /// Primage pub preimage: String, diff --git a/crates/cdk/src/nuts/nut15.rs b/crates/cdk/src/nuts/nut15.rs index e61a861b7..abaa5c422 100644 --- a/crates/cdk/src/nuts/nut15.rs +++ b/crates/cdk/src/nuts/nut15.rs @@ -10,6 +10,7 @@ use crate::Amount; /// Multi-part payment #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename = "lowercase")] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub struct Mpp { /// Amount pub amount: Amount, @@ -17,6 +18,7 @@ pub struct Mpp { /// Mpp Method Settings #[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] pub struct MppMethodSettings { /// Payment Method e.g. bolt11 pub method: PaymentMethod, @@ -28,6 +30,7 @@ pub struct MppMethodSettings { /// Mpp Settings #[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema), schema(as = nut15::Settings))] pub struct Settings { /// Method settings pub methods: Vec, diff --git a/flake.nix b/flake.nix index c1344873d..b40785577 100644 --- a/flake.nix +++ b/flake.nix @@ -135,7 +135,7 @@ cargo update -p serde_with --precise 3.1.0 cargo update -p regex --precise 1.9.6 cargo update -p backtrace --precise 0.3.58 - # For wasm32-unknown-unknown target + # For wasm32-unknown-unknown target cargo update -p bumpalo --precise 3.12.0 cargo update -p moka --precise 0.11.1 cargo update -p triomphe --precise 0.1.11