diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 77e11151..0ae7e940 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,6 +49,16 @@ jobs: matrix: build-args: [ + -p cashu, + -p cashu --no-default-features, + -p cashu --no-default-features --features wallet, + -p cashu --no-default-features --features mint, + -p cashu --no-default-features --features "mint swagger", + -p cdk-common, + -p cdk-common --no-default-features, + -p cdk-common --no-default-features --features wallet, + -p cdk-common --no-default-features --features mint, + -p cdk-common --no-default-features --features "mint swagger", -p cdk, -p cdk --no-default-features, -p cdk --no-default-features --features wallet, @@ -154,10 +164,9 @@ jobs: matrix: build-args: [ - -p cdk, - -p cdk --no-default-features, - -p cdk --no-default-features --features wallet, - -p cdk --no-default-features --features mint, + -p cashu --no-default-features --features "wallet mint", + -p cdk-common --no-default-features --features "wallet mint", + -p cdk --no-default-features --features "mint mint", -p cdk-axum, -p cdk-axum --no-default-features --features redis, -p cdk-strike, diff --git a/.gitignore b/.gitignore index 8bda7926..33311d9d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ **/target +.DS_Store .direnv .vscode/ .idea/ diff --git a/Cargo.lock b/Cargo.lock index be339a57..81140bfb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -682,6 +682,28 @@ dependencies = [ "serde_json", ] +[[package]] +name = "cashu" +version = "0.6.0" +dependencies = [ + "bip39", + "bitcoin 0.32.5", + "cbor-diag", + "ciborium", + "instant", + "lightning-invoice", + "once_cell", + "rand", + "serde", + "serde_json", + "serde_with", + "thiserror 2.0.11", + "tracing", + "url", + "utoipa", + "uuid", +] + [[package]] name = "cast" version = "0.3.0" @@ -718,9 +740,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.7" +version = "1.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7" +checksum = "ad0cf6e91fde44c773c6ee7ec6bba798504641a8bc2eb7e37a04ffbf4dfaa55a" dependencies = [ "jobserver", "libc", @@ -738,13 +760,12 @@ dependencies = [ "bip39", "bitcoin 0.32.5", "cbor-diag", + "cdk-common", "ciborium", "criterion", "futures", "getrandom", - "instant", "lightning-invoice", - "once_cell", "rand", "regex", "reqwest", @@ -820,6 +841,31 @@ dependencies = [ "uuid", ] +[[package]] +name = "cdk-common" +version = "0.6.0" +dependencies = [ + "anyhow", + "async-trait", + "bip39", + "bitcoin 0.32.5", + "cashu", + "cbor-diag", + "ciborium", + "futures", + "instant", + "lightning-invoice", + "rand", + "serde", + "serde_json", + "serde_with", + "thiserror 2.0.11", + "tracing", + "url", + "utoipa", + "uuid", +] + [[package]] name = "cdk-fake-wallet" version = "0.6.0" @@ -959,7 +1005,7 @@ name = "cdk-redb" version = "0.6.0" dependencies = [ "async-trait", - "cdk", + "cdk-common", "lightning-invoice", "redb", "serde", @@ -990,7 +1036,7 @@ version = "0.6.0" dependencies = [ "async-trait", "bitcoin 0.32.5", - "cdk", + "cdk-common", "lightning-invoice", "serde_json", "sqlx", @@ -1502,6 +1548,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "erased-serde" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24e2389d65ab4fab27dc2a5de7b191e1f6617d1f1c8855c0dc569c94a4cbb18d" +dependencies = [ + "serde", + "typeid", +] + [[package]] name = "errno" version = "0.3.10" @@ -2547,9 +2603,12 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "3d6ea2a48c204030ee31a7d7fc72c93294c92fe87ecb1789881c9543516e1a0d" +dependencies = [ + "value-bag", +] [[package]] name = "lru" @@ -2994,7 +3053,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" dependencies = [ "memchr", - "thiserror 2.0.10", + "thiserror 2.0.11", "ucd-trie", ] @@ -3203,9 +3262,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.92" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] @@ -3303,7 +3362,7 @@ dependencies = [ "rustc-hash", "rustls 0.23.21", "socket2 0.5.8", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", "tracing", ] @@ -3322,7 +3381,7 @@ dependencies = [ "rustls 0.23.21", "rustls-pki-types", "slab", - "thiserror 2.0.10", + "thiserror 2.0.11", "tinyvec", "tracing", "web-time", @@ -4017,6 +4076,15 @@ dependencies = [ "syn 2.0.96", ] +[[package]] +name = "serde_fmt" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d4ddca14104cd60529e8c7f7ba71a2c8acd8f7f5cfcdc2faf97eeb7c3010a4" +dependencies = [ + "serde", +] + [[package]] name = "serde_json" version = "1.0.135" @@ -4357,6 +4425,84 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "sval" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6dc0f9830c49db20e73273ffae9b5240f63c42e515af1da1fceefb69fceafd8" + +[[package]] +name = "sval_buffer" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "429922f7ad43c0ef8fd7309e14d750e38899e32eb7e8da656ea169dd28ee212f" +dependencies = [ + "sval", + "sval_ref", +] + +[[package]] +name = "sval_dynamic" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f16ff5d839396c11a30019b659b0976348f3803db0626f736764c473b50ff4" +dependencies = [ + "sval", +] + +[[package]] +name = "sval_fmt" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c01c27a80b6151b0557f9ccbe89c11db571dc5f68113690c1e028d7e974bae94" +dependencies = [ + "itoa", + "ryu", + "sval", +] + +[[package]] +name = "sval_json" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0deef63c70da622b2a8069d8600cf4b05396459e665862e7bdb290fd6cf3f155" +dependencies = [ + "itoa", + "ryu", + "sval", +] + +[[package]] +name = "sval_nested" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a39ce5976ae1feb814c35d290cf7cf8cd4f045782fe1548d6bc32e21f6156e9f" +dependencies = [ + "sval", + "sval_buffer", + "sval_ref", +] + +[[package]] +name = "sval_ref" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb7c6ee3751795a728bc9316a092023529ffea1783499afbc5c66f5fabebb1fa" +dependencies = [ + "sval", +] + +[[package]] +name = "sval_serde" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a5572d0321b68109a343634e3a5d576bf131b82180c6c442dee06349dfc652a" +dependencies = [ + "serde", + "sval", + "sval_nested", +] + [[package]] name = "syn" version = "1.0.109" @@ -4436,11 +4582,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.10" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3ac7f54ca534db81081ef1c1e7f6ea8a3ef428d2fc069097c079443d24124d3" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" dependencies = [ - "thiserror-impl 2.0.10", + "thiserror-impl 2.0.11", ] [[package]] @@ -4456,9 +4602,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.10" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e9465d30713b56a37ede7185763c3492a91be2f5fa68d958c44e41ab9248beb" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" dependencies = [ "proc-macro2", "quote", @@ -4945,6 +5091,12 @@ dependencies = [ "utf-8", ] +[[package]] +name = "typeid" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e13db2e0ccd5e14a544e8a246ba2312cd25223f616442d7f2cb0e3db614236e" + [[package]] name = "typenum" version = "1.17.0" @@ -5116,6 +5268,42 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "value-bag" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ef4c4aa54d5d05a279399bfa921ec387b7aba77caf7a682ae8d86785b8fdad2" +dependencies = [ + "value-bag-serde1", + "value-bag-sval2", +] + +[[package]] +name = "value-bag-serde1" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb773bd36fd59c7ca6e336c94454d9c66386416734817927ac93d81cb3c5b0b" +dependencies = [ + "erased-serde", + "serde", + "serde_fmt", +] + +[[package]] +name = "value-bag-sval2" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a916a702cac43a88694c97657d449775667bcd14b70419441d05b7fea4a83a" +dependencies = [ + "sval", + "sval_buffer", + "sval_dynamic", + "sval_fmt", + "sval_json", + "sval_ref", + "sval_serde", +] + [[package]] name = "vcpkg" version = "0.2.15" diff --git a/crates/cashu/Cargo.toml b/crates/cashu/Cargo.toml new file mode 100644 index 00000000..cf192195 --- /dev/null +++ b/crates/cashu/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "cashu" +version = "0.6.0" +edition = "2021" +description = "Cashu shared types and crypto utilities, used as the foundation for the CDK and their crates" +rust-version = "1.63.0" # MSRV + +[features] +default = ["mint", "wallet"] +swagger = ["dep:utoipa"] +mint = ["dep:uuid"] +wallet = [] +bench = [] + +[dependencies] +uuid = { version = "1", features = ["v4", "serde"], optional = true } +bitcoin = { version = "0.32.2", features = [ + "base64", + "serde", + "rand", + "rand-std", +] } +cbor-diag = "0.1.12" +ciborium = { version = "0.2.2", default-features = false, features = ["std"] } +once_cell = "1.20.2" +serde = { version = "1", features = ["derive"] } +lightning-invoice = { version = "0.32.0", features = ["serde", "std"] } +thiserror = "2" +tracing = "0.1" +url = "2.3" +utoipa = { version = "4", optional = true } +serde_json = "1" +serde_with = "3" + +[target.'cfg(target_arch = "wasm32")'.dependencies] +instant = { version = "0.1", features = ["wasm-bindgen", "inaccurate"] } + +[dev-dependencies] +rand = "0.8.5" +bip39 = "2.0" +uuid = { version = "1", features = ["v4", "serde"] } diff --git a/crates/cdk/src/amount.rs b/crates/cashu/src/amount.rs similarity index 100% rename from crates/cdk/src/amount.rs rename to crates/cashu/src/amount.rs diff --git a/crates/cdk/src/dhke.rs b/crates/cashu/src/dhke.rs similarity index 100% rename from crates/cdk/src/dhke.rs rename to crates/cashu/src/dhke.rs diff --git a/crates/cashu/src/lib.rs b/crates/cashu/src/lib.rs new file mode 100644 index 00000000..9ca08723 --- /dev/null +++ b/crates/cashu/src/lib.rs @@ -0,0 +1,18 @@ +//! CDK common types and traits +//! +pub mod amount; +pub mod dhke; +#[cfg(feature = "mint")] +pub mod mint; +pub mod mint_url; +pub mod nuts; +pub mod secret; +pub mod util; +#[cfg(feature = "wallet")] +pub mod wallet; + +pub use lightning_invoice::{self, Bolt11Invoice}; + +pub use self::amount::Amount; +pub use self::nuts::*; +pub use self::util::SECP256K1; diff --git a/crates/cdk/src/mint/types.rs b/crates/cashu/src/mint.rs similarity index 66% rename from crates/cdk/src/mint/types.rs rename to crates/cashu/src/mint.rs index 6fc685c3..a784a2e9 100644 --- a/crates/cdk/src/mint/types.rs +++ b/crates/cashu/src/mint.rs @@ -1,12 +1,12 @@ -//! Mint Types +//! Mint types +use bitcoin::bip32::DerivationPath; use serde::{Deserialize, Serialize}; use uuid::Uuid; -use super::{CurrencyUnit, PublicKey}; use crate::mint_url::MintUrl; use crate::nuts::{MeltQuoteState, MintQuoteState}; -use crate::Amount; +use crate::{Amount, CurrencyUnit, Id, KeySetInfo, PublicKey}; /// Mint Quote Info #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)] @@ -58,7 +58,7 @@ impl MintQuote { } } -/// Melt Quote Info +// Melt Quote Info #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)] pub struct MeltQuote { /// Quote id @@ -112,3 +112,45 @@ impl MeltQuote { } } } + +/// Mint Keyset Info +#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)] +pub struct MintKeySetInfo { + /// Keyset [`Id`] + pub id: Id, + /// Keyset [`CurrencyUnit`] + pub unit: CurrencyUnit, + /// Keyset active or inactive + /// Mint will only issue new [`BlindSignature`] on active keysets + pub active: bool, + /// Starting unix time Keyset is valid from + pub valid_from: u64, + /// When the Keyset is valid to + /// This is not shown to the wallet and can only be used internally + pub valid_to: Option, + /// [`DerivationPath`] keyset + pub derivation_path: DerivationPath, + /// DerivationPath index of Keyset + pub derivation_path_index: Option, + /// Max order of keyset + pub max_order: u8, + /// Input Fee ppk + #[serde(default = "default_fee")] + pub input_fee_ppk: u64, +} + +/// Default fee +pub fn default_fee() -> u64 { + 0 +} + +impl From for KeySetInfo { + fn from(keyset_info: MintKeySetInfo) -> Self { + Self { + id: keyset_info.id, + unit: keyset_info.unit, + active: keyset_info.active, + input_fee_ppk: keyset_info.input_fee_ppk, + } + } +} diff --git a/crates/cdk/src/mint_url.rs b/crates/cashu/src/mint_url.rs similarity index 100% rename from crates/cdk/src/mint_url.rs rename to crates/cashu/src/mint_url.rs diff --git a/crates/cdk/src/nuts/mod.rs b/crates/cashu/src/nuts/mod.rs similarity index 87% rename from crates/cdk/src/nuts/mod.rs rename to crates/cashu/src/nuts/mod.rs index c81c72ff..0e09da99 100644 --- a/crates/cdk/src/nuts/mod.rs +++ b/crates/cashu/src/nuts/mod.rs @@ -15,6 +15,7 @@ pub mod nut09; pub mod nut10; pub mod nut11; pub mod nut12; +#[cfg(feature = "wallet")] pub mod nut13; pub mod nut14; pub mod nut15; @@ -24,9 +25,11 @@ pub mod nut19; pub mod nut20; pub use nut00::{ - BlindSignature, BlindedMessage, CurrencyUnit, PaymentMethod, PreMint, PreMintSecrets, Proof, - Proofs, ProofsMethods, Token, TokenV3, TokenV4, Witness, + BlindSignature, BlindedMessage, CurrencyUnit, PaymentMethod, Proof, Proofs, ProofsMethods, + Token, TokenV3, TokenV4, Witness, }; +#[cfg(feature = "wallet")] +pub use nut00::{PreMint, PreMintSecrets}; pub use nut01::{Keys, KeysResponse, PublicKey, SecretKey}; #[cfg(feature = "mint")] pub use nut02::MintKeySet; @@ -50,7 +53,5 @@ pub use nut11::{Conditions, P2PKWitness, SigFlag, SpendingConditions}; pub use nut12::{BlindSignatureDleq, ProofDleq}; pub use nut14::HTLCWitness; pub use nut15::{Mpp, MppMethodSettings, Settings as NUT15Settings}; -#[cfg(feature = "mint")] -pub use nut17::PubSubManager; -pub use nut17::{NotificationPayload, SupportedSettings as Nut17SupportedSettings}; +pub use nut17::NotificationPayload; pub use nut18::{PaymentRequest, PaymentRequestPayload, Transport}; diff --git a/crates/cdk/src/nuts/nut00/mod.rs b/crates/cashu/src/nuts/nut00/mod.rs similarity index 97% rename from crates/cdk/src/nuts/nut00/mod.rs rename to crates/cashu/src/nuts/nut00/mod.rs index b16f6f1a..4d8bbb81 100644 --- a/crates/cdk/src/nuts/nut00/mod.rs +++ b/crates/cashu/src/nuts/nut00/mod.rs @@ -11,11 +11,18 @@ use std::string::FromUtf8Error; use serde::{de, Deserialize, Deserializer, Serialize}; use thiserror::Error; +#[cfg(feature = "wallet")] use super::nut10; +#[cfg(feature = "wallet")] use super::nut11::SpendingConditions; +#[cfg(feature = "wallet")] use crate::amount::SplitTarget; -use crate::dhke::{blind_message, hash_to_curve}; -use crate::nuts::nut01::{PublicKey, SecretKey}; +#[cfg(feature = "wallet")] +use crate::dhke::blind_message; +use crate::dhke::hash_to_curve; +use crate::nuts::nut01::PublicKey; +#[cfg(feature = "wallet")] +use crate::nuts::nut01::SecretKey; use crate::nuts::nut11::{serde_p2pk_witness, P2PKWitness}; use crate::nuts::nut12::BlindSignatureDleq; use crate::nuts::nut14::{serde_htlc_witness, HTLCWitness}; @@ -491,6 +498,7 @@ impl<'de> Deserialize<'de> for PaymentMethod { } /// PreMint +#[cfg(feature = "wallet")] #[derive(Debug, Clone, PartialEq, Eq, Serialize)] pub struct PreMint { /// Blinded message @@ -503,12 +511,14 @@ pub struct PreMint { pub amount: Amount, } +#[cfg(feature = "wallet")] impl Ord for PreMint { fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.amount.cmp(&other.amount) } } +#[cfg(feature = "wallet")] impl PartialOrd for PreMint { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) @@ -516,6 +526,7 @@ impl PartialOrd for PreMint { } /// Premint Secrets +#[cfg(feature = "wallet")] #[derive(Debug, Clone, PartialEq, Eq, Serialize)] pub struct PreMintSecrets { /// Secrets @@ -524,6 +535,7 @@ pub struct PreMintSecrets { pub keyset_id: Id, } +#[cfg(feature = "wallet")] impl PreMintSecrets { /// Create new [`PreMintSecrets`] pub fn new(keyset_id: Id) -> Self { @@ -712,6 +724,7 @@ impl PreMintSecrets { } // Implement Iterator for PreMintSecrets +#[cfg(feature = "wallet")] impl Iterator for PreMintSecrets { type Item = PreMint; @@ -721,12 +734,14 @@ impl Iterator for PreMintSecrets { } } +#[cfg(feature = "wallet")] impl Ord for PreMintSecrets { fn cmp(&self, other: &Self) -> Ordering { self.secrets.cmp(&other.secrets) } } +#[cfg(feature = "wallet")] impl PartialOrd for PreMintSecrets { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) @@ -753,6 +768,7 @@ mod tests { } #[test] + #[cfg(feature = "wallet")] fn test_blank_blinded_messages() { let b = PreMintSecrets::blank( Id::from_str("009a1f293253e41e").unwrap(), diff --git a/crates/cdk/src/nuts/nut00/token.rs b/crates/cashu/src/nuts/nut00/token.rs similarity index 98% rename from crates/cdk/src/nuts/nut00/token.rs rename to crates/cashu/src/nuts/nut00/token.rs index 724de0e7..2e4fc809 100644 --- a/crates/cdk/src/nuts/nut00/token.rs +++ b/crates/cashu/src/nuts/nut00/token.rs @@ -527,11 +527,11 @@ mod tests { } #[test] - fn test_token_v4_multi_keyset() -> anyhow::Result<()> { + fn test_token_v4_multi_keyset() { let token_str_multi_keysets = "cashuBo2F0gqJhaUgA_9SLj17PgGFwgaNhYQFhc3hAYWNjMTI0MzVlN2I4NDg0YzNjZjE4NTAxNDkyMThhZjkwZjcxNmE1MmJmNGE1ZWQzNDdlNDhlY2MxM2Y3NzM4OGFjWCECRFODGd5IXVW-07KaZCvuWHk3WrnnpiDhHki6SCQh88-iYWlIAK0mjE0fWCZhcIKjYWECYXN4QDEzMjNkM2Q0NzA3YTU4YWQyZTIzYWRhNGU5ZjFmNDlmNWE1YjRhYzdiNzA4ZWIwZDYxZjczOGY0ODMwN2U4ZWVhY1ghAjRWqhENhLSsdHrr2Cw7AFrKUL9Ffr1XN6RBT6w659lNo2FhAWFzeEA1NmJjYmNiYjdjYzY0MDZiM2ZhNWQ1N2QyMTc0ZjRlZmY4YjQ0MDJiMTc2OTI2ZDNhNTdkM2MzZGNiYjU5ZDU3YWNYIQJzEpxXGeWZN5qXSmJjY8MzxWyvwObQGr5G1YCCgHicY2FtdWh0dHA6Ly9sb2NhbGhvc3Q6MzMzOGF1Y3NhdA=="; let token = Token::from_str(token_str_multi_keysets).unwrap(); - let amount = token.value()?; + let amount = token.value().expect("valid amount"); assert_eq!(amount, Amount::from(4)); @@ -552,21 +552,19 @@ mod tests { assert_eq!("http://localhost:3338", &mint_url.to_string()); } _ => { - anyhow::bail!("Token should be a v4 token") + panic!("Token should be a v4 token") } } - - Ok(()) } #[test] - fn test_tokenv4_from_tokenv3() -> anyhow::Result<()> { + fn test_tokenv4_from_tokenv3() { let token_v3_str = "cashuAeyJ0b2tlbiI6W3sibWludCI6Imh0dHBzOi8vODMzMy5zcGFjZTozMzM4IiwicHJvb2ZzIjpbeyJhbW91bnQiOjIsImlkIjoiMDA5YTFmMjkzMjUzZTQxZSIsInNlY3JldCI6IjQwNzkxNWJjMjEyYmU2MWE3N2UzZTZkMmFlYjRjNzI3OTgwYmRhNTFjZDA2YTZhZmMyOWUyODYxNzY4YTc4MzciLCJDIjoiMDJiYzkwOTc5OTdkODFhZmIyY2M3MzQ2YjVlNDM0NWE5MzQ2YmQyYTUwNmViNzk1ODU5OGE3MmYwY2Y4NTE2M2VhIn0seyJhbW91bnQiOjgsImlkIjoiMDA5YTFmMjkzMjUzZTQxZSIsInNlY3JldCI6ImZlMTUxMDkzMTRlNjFkNzc1NmIwZjhlZTBmMjNhNjI0YWNhYTNmNGUwNDJmNjE0MzNjNzI4YzcwNTdiOTMxYmUiLCJDIjoiMDI5ZThlNTA1MGI4OTBhN2Q2YzA5NjhkYjE2YmMxZDVkNWZhMDQwZWExZGUyODRmNmVjNjlkNjEyOTlmNjcxMDU5In1dfV0sInVuaXQiOiJzYXQiLCJtZW1vIjoiVGhhbmsgeW91LiJ9"; - let token_v3 = TokenV3::from_str(token_v3_str)?; + let token_v3 = + TokenV3::from_str(token_v3_str).expect("TokenV3 should be created from string"); let token_v4 = TokenV4::try_from(token_v3).expect("TokenV3 should be converted to TokenV4"); let token_v4_expected = "cashuBpGFtd2h0dHBzOi8vODMzMy5zcGFjZTozMzM4YXVjc2F0YWRqVGhhbmsgeW91LmF0gaJhaUgAmh8pMlPkHmFwgqRhYQJhc3hANDA3OTE1YmMyMTJiZTYxYTc3ZTNlNmQyYWViNGM3Mjc5ODBiZGE1MWNkMDZhNmFmYzI5ZTI4NjE3NjhhNzgzN2FjWCECvJCXmX2Br7LMc0a15DRak0a9KlBut5WFmKcvDPhRY-phZPakYWEIYXN4QGZlMTUxMDkzMTRlNjFkNzc1NmIwZjhlZTBmMjNhNjI0YWNhYTNmNGUwNDJmNjE0MzNjNzI4YzcwNTdiOTMxYmVhY1ghAp6OUFC4kKfWwJaNsWvB1dX6BA6h3ihPbsadYSmfZxBZYWT2"; assert_eq!(token_v4.to_string(), token_v4_expected); - Ok(()) } #[test] diff --git a/crates/cdk/src/nuts/nut01/mod.rs b/crates/cashu/src/nuts/nut01/mod.rs similarity index 100% rename from crates/cdk/src/nuts/nut01/mod.rs rename to crates/cashu/src/nuts/nut01/mod.rs diff --git a/crates/cdk/src/nuts/nut01/public_key.rs b/crates/cashu/src/nuts/nut01/public_key.rs similarity index 100% rename from crates/cdk/src/nuts/nut01/public_key.rs rename to crates/cashu/src/nuts/nut01/public_key.rs diff --git a/crates/cdk/src/nuts/nut01/secret_key.rs b/crates/cashu/src/nuts/nut01/secret_key.rs similarity index 100% rename from crates/cdk/src/nuts/nut01/secret_key.rs rename to crates/cashu/src/nuts/nut01/secret_key.rs diff --git a/crates/cdk/src/nuts/nut02.rs b/crates/cashu/src/nuts/nut02.rs similarity index 99% rename from crates/cdk/src/nuts/nut02.rs rename to crates/cashu/src/nuts/nut02.rs index 5d63c56b..2138c1c5 100644 --- a/crates/cdk/src/nuts/nut02.rs +++ b/crates/cashu/src/nuts/nut02.rs @@ -9,9 +9,7 @@ use std::array::TryFromSliceError; use std::collections::BTreeMap; #[cfg(feature = "mint")] -use bitcoin::bip32::DerivationPath; -#[cfg(feature = "mint")] -use bitcoin::bip32::{ChildNumber, Xpriv}; +use bitcoin::bip32::{ChildNumber, DerivationPath, Xpriv}; use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::hashes::Hash; #[cfg(feature = "mint")] @@ -268,8 +266,8 @@ fn default_input_fee_ppk() -> u64 { 0 } -/// MintKeyset #[cfg(feature = "mint")] +/// MintKeyset #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct MintKeySet { /// Keyset [`Id`] diff --git a/crates/cdk/src/nuts/nut03.rs b/crates/cashu/src/nuts/nut03.rs similarity index 94% rename from crates/cdk/src/nuts/nut03.rs rename to crates/cashu/src/nuts/nut03.rs index cee5f063..0f12ad79 100644 --- a/crates/cdk/src/nuts/nut03.rs +++ b/crates/cashu/src/nuts/nut03.rs @@ -5,7 +5,9 @@ use serde::{Deserialize, Serialize}; use thiserror::Error; -use super::nut00::{BlindSignature, BlindedMessage, PreMintSecrets, Proofs}; +#[cfg(feature = "wallet")] +use super::nut00::PreMintSecrets; +use super::nut00::{BlindSignature, BlindedMessage, Proofs}; use crate::Amount; /// NUT03 Error @@ -20,6 +22,7 @@ pub enum Error { } /// Preswap information +#[cfg(feature = "wallet")] #[derive(Debug, Clone, PartialEq, Eq, Serialize)] pub struct PreSwap { /// Preswap mint secrets diff --git a/crates/cdk/src/nuts/nut04.rs b/crates/cashu/src/nuts/nut04.rs similarity index 100% rename from crates/cdk/src/nuts/nut04.rs rename to crates/cashu/src/nuts/nut04.rs diff --git a/crates/cdk/src/nuts/nut05.rs b/crates/cashu/src/nuts/nut05.rs similarity index 100% rename from crates/cdk/src/nuts/nut05.rs rename to crates/cashu/src/nuts/nut05.rs diff --git a/crates/cdk/src/nuts/nut06.rs b/crates/cashu/src/nuts/nut06.rs similarity index 99% rename from crates/cdk/src/nuts/nut06.rs rename to crates/cashu/src/nuts/nut06.rs index f001716b..d8cce86b 100644 --- a/crates/cdk/src/nuts/nut06.rs +++ b/crates/cashu/src/nuts/nut06.rs @@ -5,7 +5,6 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; use super::nut01::PublicKey; -#[cfg(feature = "mint")] use super::nut17::SupportedMethods; use super::nut19::CachedEndpoint; use super::{nut04, nut05, nut15, nut19, MppMethodSettings}; @@ -241,7 +240,6 @@ pub struct Nuts { /// NUT17 Settings #[serde(default)] #[serde(rename = "17")] - #[cfg(feature = "mint")] pub nut17: super::nut17::SupportedSettings, /// NUT19 Settings #[serde(default)] @@ -342,7 +340,6 @@ impl Nuts { } /// Nut17 settings - #[cfg(feature = "mint")] pub fn nut17(self, supported: Vec) -> Self { Self { nut17: super::nut17::SupportedSettings { supported }, diff --git a/crates/cdk/src/nuts/nut07.rs b/crates/cashu/src/nuts/nut07.rs similarity index 100% rename from crates/cdk/src/nuts/nut07.rs rename to crates/cashu/src/nuts/nut07.rs diff --git a/crates/cdk/src/nuts/nut08.rs b/crates/cashu/src/nuts/nut08.rs similarity index 100% rename from crates/cdk/src/nuts/nut08.rs rename to crates/cashu/src/nuts/nut08.rs diff --git a/crates/cdk/src/nuts/nut09.rs b/crates/cashu/src/nuts/nut09.rs similarity index 100% rename from crates/cdk/src/nuts/nut09.rs rename to crates/cashu/src/nuts/nut09.rs diff --git a/crates/cdk/src/nuts/nut10.rs b/crates/cashu/src/nuts/nut10.rs similarity index 100% rename from crates/cdk/src/nuts/nut10.rs rename to crates/cashu/src/nuts/nut10.rs diff --git a/crates/cdk/src/nuts/nut11/mod.rs b/crates/cashu/src/nuts/nut11/mod.rs similarity index 98% rename from crates/cdk/src/nuts/nut11/mod.rs rename to crates/cashu/src/nuts/nut11/mod.rs index 6d407e2b..b7b6b22d 100644 --- a/crates/cdk/src/nuts/nut11/mod.rs +++ b/crates/cashu/src/nuts/nut11/mod.rs @@ -2,9 +2,7 @@ //! //! -use std::collections::HashMap; -#[cfg(feature = "mint")] -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; use std::str::FromStr; use std::{fmt, vec}; @@ -18,9 +16,7 @@ use thiserror::Error; use super::nut00::Witness; use super::nut01::PublicKey; -#[cfg(feature = "mint")] -use super::Proofs; -use super::{Kind, Nut10Secret, Proof, SecretKey}; +use super::{Kind, Nut10Secret, Proof, Proofs, SecretKey}; use crate::nuts::nut00::BlindedMessage; use crate::secret::Secret; use crate::util::{hex, unix_time}; @@ -616,10 +612,9 @@ impl FromStr for SigFlag { } } -#[cfg(feature = "mint")] /// Get the signature flag that should be enforced for a set of proofs and the /// public keys that signatures are valid for -pub(crate) fn enforce_sig_flag(proofs: Proofs) -> EnforceSigFlag { +pub fn enforce_sig_flag(proofs: Proofs) -> EnforceSigFlag { let mut sig_flag = SigFlag::SigInputs; let mut pubkeys = HashSet::new(); let mut sigs_required = 1; @@ -658,10 +653,9 @@ pub(crate) fn enforce_sig_flag(proofs: Proofs) -> EnforceSigFlag { } } -#[cfg(feature = "mint")] /// Enforce Sigflag info #[derive(Debug, Clone, PartialEq, Eq)] -pub(crate) struct EnforceSigFlag { +pub struct EnforceSigFlag { /// Sigflag required for proofs pub sig_flag: SigFlag, /// Pubkeys that can sign for proofs diff --git a/crates/cdk/src/nuts/nut11/serde_p2pk_witness.rs b/crates/cashu/src/nuts/nut11/serde_p2pk_witness.rs similarity index 100% rename from crates/cdk/src/nuts/nut11/serde_p2pk_witness.rs rename to crates/cashu/src/nuts/nut11/serde_p2pk_witness.rs diff --git a/crates/cdk/src/nuts/nut12.rs b/crates/cashu/src/nuts/nut12.rs similarity index 100% rename from crates/cdk/src/nuts/nut12.rs rename to crates/cashu/src/nuts/nut12.rs diff --git a/crates/cdk/src/nuts/nut13.rs b/crates/cashu/src/nuts/nut13.rs similarity index 100% rename from crates/cdk/src/nuts/nut13.rs rename to crates/cashu/src/nuts/nut13.rs diff --git a/crates/cdk/src/nuts/nut14/mod.rs b/crates/cashu/src/nuts/nut14/mod.rs similarity index 100% rename from crates/cdk/src/nuts/nut14/mod.rs rename to crates/cashu/src/nuts/nut14/mod.rs diff --git a/crates/cdk/src/nuts/nut14/serde_htlc_witness.rs b/crates/cashu/src/nuts/nut14/serde_htlc_witness.rs similarity index 100% rename from crates/cdk/src/nuts/nut14/serde_htlc_witness.rs rename to crates/cashu/src/nuts/nut14/serde_htlc_witness.rs diff --git a/crates/cdk/src/nuts/nut15.rs b/crates/cashu/src/nuts/nut15.rs similarity index 100% rename from crates/cdk/src/nuts/nut15.rs rename to crates/cashu/src/nuts/nut15.rs diff --git a/crates/cdk/src/nuts/nut17/mod.rs b/crates/cashu/src/nuts/nut17/mod.rs similarity index 65% rename from crates/cdk/src/nuts/nut17/mod.rs rename to crates/cashu/src/nuts/nut17/mod.rs index 296a30e5..11e68624 100644 --- a/crates/cdk/src/nuts/nut17/mod.rs +++ b/crates/cashu/src/nuts/nut17/mod.rs @@ -1,38 +1,28 @@ //! Specific Subscription for the cdk crate -use std::str::FromStr; - use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; +#[cfg(feature = "mint")] use uuid::Uuid; +#[cfg(feature = "mint")] use super::PublicKey; use crate::nuts::{ CurrencyUnit, MeltQuoteBolt11Response, MintQuoteBolt11Response, PaymentMethod, ProofState, }; -use crate::pub_sub::{Index, Indexable, SubscriptionGlobalId}; -#[cfg(feature = "mint")] -mod manager; -#[cfg(feature = "mint")] -mod on_subscription; -#[cfg(feature = "mint")] -pub use manager::PubSubManager; -#[cfg(feature = "mint")] -pub use on_subscription::OnSubscription; - -pub use crate::pub_sub::SubId; pub mod ws; /// Subscription Parameter according to the standard #[derive(Debug, Clone, Serialize, Eq, PartialEq, Hash, Deserialize)] -pub struct Params { +#[serde(bound = "I: DeserializeOwned + Serialize")] +pub struct Params { /// Kind pub kind: Kind, /// Filters pub filters: Vec, /// Subscription Id #[serde(rename = "subId")] - pub id: SubId, + pub id: I, } /// Check state Settings @@ -109,6 +99,7 @@ impl From> for NotificationPayload { } } +#[cfg(feature = "mint")] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] /// A parsed notification pub enum Notification { @@ -120,24 +111,6 @@ pub enum Notification { MintQuoteBolt11(Uuid), } -impl Indexable for NotificationPayload { - type Type = Notification; - - fn to_indexes(&self) -> Vec> { - match self { - NotificationPayload::ProofState(proof_state) => { - vec![Index::from(Notification::ProofState(proof_state.y))] - } - NotificationPayload::MeltQuoteBolt11Response(melt_quote) => { - vec![Index::from(Notification::MeltQuoteBolt11(melt_quote.quote))] - } - NotificationPayload::MintQuoteBolt11Response(mint_quote) => { - vec![Index::from(Notification::MintQuoteBolt11(mint_quote.quote))] - } - } - } -} - /// Kind #[derive(Debug, Clone, Copy, Eq, Ord, PartialOrd, PartialEq, Hash, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] @@ -150,8 +123,8 @@ pub enum Kind { ProofState, } -impl AsRef for Params { - fn as_ref(&self) -> &SubId { +impl AsRef for Params { + fn as_ref(&self) -> &I { &self.id } } @@ -159,6 +132,7 @@ impl AsRef for Params { /// Parsing error #[derive(thiserror::Error, Debug)] pub enum Error { + #[cfg(feature = "mint")] #[error("Uuid Error: {0}")] /// Uuid Error Uuid(#[from] uuid::Error), @@ -167,27 +141,3 @@ pub enum Error { /// PublicKey Error PublicKey(#[from] crate::nuts::nut01::Error), } - -impl TryFrom for Vec> { - type Error = Error; - - fn try_from(val: Params) -> Result { - let sub_id: SubscriptionGlobalId = Default::default(); - val.filters - .into_iter() - .map(|filter| { - let idx = match val.kind { - Kind::Bolt11MeltQuote => { - Notification::MeltQuoteBolt11(Uuid::from_str(&filter)?) - } - Kind::Bolt11MintQuote => { - Notification::MintQuoteBolt11(Uuid::from_str(&filter)?) - } - Kind::ProofState => Notification::ProofState(PublicKey::from_str(&filter)?), - }; - - Ok(Index::from((idx, val.id.clone(), sub_id))) - }) - .collect::>() - } -} diff --git a/crates/cdk/src/nuts/nut17/ws.rs b/crates/cashu/src/nuts/nut17/ws.rs similarity index 55% rename from crates/cdk/src/nuts/nut17/ws.rs rename to crates/cashu/src/nuts/nut17/ws.rs index e59d87b6..a15c52ff 100644 --- a/crates/cdk/src/nuts/nut17/ws.rs +++ b/crates/cashu/src/nuts/nut17/ws.rs @@ -2,31 +2,32 @@ use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; -use uuid::Uuid; -use super::{NotificationPayload, Params, SubId}; +use super::{NotificationPayload, Params}; /// JSON RPC version pub const JSON_RPC_VERSION: &str = "2.0"; /// The response to a subscription request #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct WsSubscribeResponse { +#[serde(bound = "I: Serialize + DeserializeOwned")] +pub struct WsSubscribeResponse { /// Status pub status: String, /// Subscription ID #[serde(rename = "subId")] - pub sub_id: SubId, + pub sub_id: I, } /// The response to an unsubscription request #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct WsUnsubscribeResponse { +#[serde(bound = "I: Serialize + DeserializeOwned")] +pub struct WsUnsubscribeResponse { /// Status pub status: String, /// Subscription ID #[serde(rename = "subId")] - pub sub_id: SubId, + pub sub_id: I, } /// The notification @@ -34,87 +35,74 @@ pub struct WsUnsubscribeResponse { /// This is the notification that is sent to the client when an event matches a /// subscription #[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(bound = "T: Serialize + DeserializeOwned")] -pub struct NotificationInner { +#[serde(bound = "T: Serialize + DeserializeOwned, I: Serialize + DeserializeOwned")] +pub struct NotificationInner { /// The subscription ID #[serde(rename = "subId")] - pub sub_id: SubId, + pub sub_id: I, /// The notification payload pub payload: NotificationPayload, } -impl From> for NotificationInner { - fn from(value: NotificationInner) -> Self { - NotificationInner { - sub_id: value.sub_id, - payload: match value.payload { - NotificationPayload::ProofState(pk) => NotificationPayload::ProofState(pk), - NotificationPayload::MeltQuoteBolt11Response(quote) => { - NotificationPayload::MeltQuoteBolt11Response(quote.to_string_id()) - } - NotificationPayload::MintQuoteBolt11Response(quote) => { - NotificationPayload::MintQuoteBolt11Response(quote.to_string_id()) - } - }, - } - } -} - /// Responses from the web socket server #[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(bound = "I: Serialize + DeserializeOwned")] #[serde(untagged)] -pub enum WsResponseResult { +pub enum WsResponseResult { /// A response to a subscription request - Subscribe(WsSubscribeResponse), + Subscribe(WsSubscribeResponse), /// Unsubscribe - Unsubscribe(WsUnsubscribeResponse), + Unsubscribe(WsUnsubscribeResponse), } -impl From for WsResponseResult { - fn from(response: WsSubscribeResponse) -> Self { +impl From> for WsResponseResult { + fn from(response: WsSubscribeResponse) -> Self { WsResponseResult::Subscribe(response) } } -impl From for WsResponseResult { - fn from(response: WsUnsubscribeResponse) -> Self { +impl From> for WsResponseResult { + fn from(response: WsUnsubscribeResponse) -> Self { WsResponseResult::Unsubscribe(response) } } /// The request to unsubscribe #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct WsUnsubscribeRequest { +#[serde(bound = "I: Serialize + DeserializeOwned")] +pub struct WsUnsubscribeRequest { /// Subscription ID #[serde(rename = "subId")] - pub sub_id: SubId, + pub sub_id: I, } /// The inner method of the websocket request #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "snake_case", tag = "method", content = "params")] -pub enum WsMethodRequest { +#[serde(bound = "I: Serialize + DeserializeOwned")] +pub enum WsMethodRequest { /// Subscribe method - Subscribe(Params), + Subscribe(Params), /// Unsubscribe method - Unsubscribe(WsUnsubscribeRequest), + Unsubscribe(WsUnsubscribeRequest), } /// Websocket request #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct WsRequest { +#[serde(bound = "I: Serialize + DeserializeOwned")] +pub struct WsRequest { /// JSON RPC version pub jsonrpc: String, /// The method body #[serde(flatten)] - pub method: WsMethodRequest, + pub method: WsMethodRequest, /// The request ID pub id: usize, } -impl From<(WsMethodRequest, usize)> for WsRequest { - fn from((method, id): (WsMethodRequest, usize)) -> Self { +impl From<(WsMethodRequest, usize)> for WsRequest { + fn from((method, id): (WsMethodRequest, usize)) -> Self { WsRequest { jsonrpc: JSON_RPC_VERSION.to_owned(), method, @@ -145,11 +133,12 @@ pub struct WsErrorBody { /// Websocket response #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct WsResponse { +#[serde(bound = "I: Serialize + DeserializeOwned")] +pub struct WsResponse { /// JSON RPC version pub jsonrpc: String, /// The result - pub result: WsResponseResult, + pub result: WsResponseResult, /// The request ID pub id: usize, } @@ -167,18 +156,19 @@ pub struct WsErrorResponse { /// Message from the server to the client #[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(bound = "I: Serialize + DeserializeOwned")] #[serde(untagged)] -pub enum WsMessageOrResponse { +pub enum WsMessageOrResponse { /// A response to a request - Response(WsResponse), + Response(WsResponse), /// An error response ErrorResponse(WsErrorResponse), /// A notification - Notification(WsNotification>), + Notification(WsNotification>), } -impl From<(usize, Result)> for WsMessageOrResponse { - fn from((id, result): (usize, Result)) -> Self { +impl From<(usize, Result, WsErrorBody>)> for WsMessageOrResponse { + fn from((id, result): (usize, Result, WsErrorBody>)) -> Self { match result { Ok(result) => WsMessageOrResponse::Response(WsResponse { jsonrpc: JSON_RPC_VERSION.to_owned(), @@ -193,23 +183,3 @@ impl From<(usize, Result)> for WsMessageOrRespons } } } - -impl From> for WsMessageOrResponse { - fn from(notification: NotificationInner) -> Self { - WsMessageOrResponse::Notification(WsNotification { - jsonrpc: JSON_RPC_VERSION.to_owned(), - method: "subscribe".to_string(), - params: notification.into(), - }) - } -} - -impl From> for WsMessageOrResponse { - fn from(notification: NotificationInner) -> Self { - WsMessageOrResponse::Notification(WsNotification { - jsonrpc: JSON_RPC_VERSION.to_owned(), - method: "subscribe".to_string(), - params: notification, - }) - } -} diff --git a/crates/cdk/src/nuts/nut18.rs b/crates/cashu/src/nuts/nut18.rs similarity index 93% rename from crates/cdk/src/nuts/nut18.rs rename to crates/cashu/src/nuts/nut18.rs index 170af17d..7bccb947 100644 --- a/crates/cdk/src/nuts/nut18.rs +++ b/crates/cashu/src/nuts/nut18.rs @@ -147,15 +147,15 @@ mod tests { const PAYMENT_REQUEST: &str = "creqApWF0gaNhdGVub3N0cmFheKlucHJvZmlsZTFxeTI4d3VtbjhnaGo3dW45ZDNzaGp0bnl2OWtoMnVld2Q5aHN6OW1od2RlbjV0ZTB3ZmprY2N0ZTljdXJ4dmVuOWVlaHFjdHJ2NWhzenJ0aHdkZW41dGUwZGVoaHh0bnZkYWtxcWd5ZGFxeTdjdXJrNDM5eWtwdGt5c3Y3dWRoZGh1NjhzdWNtMjk1YWtxZWZkZWhrZjBkNDk1Y3d1bmw1YWeBgmFuYjE3YWloYjdhOTAxNzZhYQphdWNzYXRhbYF4Imh0dHBzOi8vbm9mZWVzLnRlc3RudXQuY2FzaHUuc3BhY2U="; #[test] - fn test_decode_payment_req() -> anyhow::Result<()> { - let req = PaymentRequest::from_str(PAYMENT_REQUEST)?; + fn test_decode_payment_req() { + let req = PaymentRequest::from_str(PAYMENT_REQUEST).expect("valid payment request"); assert_eq!(&req.payment_id.unwrap(), "b7a90176"); assert_eq!(req.amount.unwrap(), 10.into()); assert_eq!(req.unit.clone().unwrap(), CurrencyUnit::Sat); assert_eq!( req.mints.unwrap(), - vec![MintUrl::from_str("https://nofees.testnut.cashu.space")?] + vec![MintUrl::from_str("https://nofees.testnut.cashu.space").expect("valid mint url")] ); assert_eq!(req.unit.unwrap(), CurrencyUnit::Sat); @@ -164,12 +164,10 @@ mod tests { let expected_transport = Transport {_type: TransportType::Nostr, target: "nprofile1qy28wumn8ghj7un9d3shjtnyv9kh2uewd9hsz9mhwden5te0wfjkccte9curxven9eehqctrv5hszrthwden5te0dehhxtnvdakqqgydaqy7curk439ykptkysv7udhdhu68sucm295akqefdehkf0d495cwunl5".to_string(), tags: Some(vec![vec!["n".to_string(), "17".to_string()]])}; assert_eq!(transport, &expected_transport); - - Ok(()) } #[test] - fn test_roundtrip_payment_req() -> anyhow::Result<()> { + fn test_roundtrip_payment_req() { let transport = Transport {_type: TransportType::Nostr, target: "nprofile1qy28wumn8ghj7un9d3shjtnyv9kh2uewd9hsz9mhwden5te0wfjkccte9curxven9eehqctrv5hszrthwden5te0dehhxtnvdakqqgydaqy7curk439ykptkysv7udhdhu68sucm295akqefdehkf0d495cwunl5".to_string(), tags: Some(vec![vec!["n".to_string(), "17".to_string()]])}; let request = PaymentRequest { @@ -177,27 +175,27 @@ mod tests { amount: Some(10.into()), unit: Some(CurrencyUnit::Sat), single_use: None, - mints: Some(vec!["https://nofees.testnut.cashu.space".parse()?]), + mints: Some(vec!["https://nofees.testnut.cashu.space" + .parse() + .expect("valid mint url")]), description: None, transports: vec![transport.clone()], }; let request_str = request.to_string(); - let req = PaymentRequest::from_str(&request_str)?; + let req = PaymentRequest::from_str(&request_str).expect("valid payment request"); assert_eq!(&req.payment_id.unwrap(), "b7a90176"); assert_eq!(req.amount.unwrap(), 10.into()); assert_eq!(req.unit.clone().unwrap(), CurrencyUnit::Sat); assert_eq!( req.mints.unwrap(), - vec![MintUrl::from_str("https://nofees.testnut.cashu.space")?] + vec![MintUrl::from_str("https://nofees.testnut.cashu.space").expect("valid mint url")] ); assert_eq!(req.unit.unwrap(), CurrencyUnit::Sat); let t = req.transports.first().unwrap(); assert_eq!(&transport, t); - - Ok(()) } } diff --git a/crates/cdk/src/nuts/nut19.rs b/crates/cashu/src/nuts/nut19.rs similarity index 100% rename from crates/cdk/src/nuts/nut19.rs rename to crates/cashu/src/nuts/nut19.rs diff --git a/crates/cdk/src/nuts/nut20.rs b/crates/cashu/src/nuts/nut20.rs similarity index 100% rename from crates/cdk/src/nuts/nut20.rs rename to crates/cashu/src/nuts/nut20.rs diff --git a/crates/cdk/src/secret.rs b/crates/cashu/src/secret.rs similarity index 100% rename from crates/cdk/src/secret.rs rename to crates/cashu/src/secret.rs diff --git a/crates/cdk/src/util/hex.rs b/crates/cashu/src/util/hex.rs similarity index 59% rename from crates/cdk/src/util/hex.rs rename to crates/cashu/src/util/hex.rs index ac1cf3ef..926911c2 100644 --- a/crates/cdk/src/util/hex.rs +++ b/crates/cashu/src/util/hex.rs @@ -122,26 +122,3 @@ mod tests { ); } } - -#[cfg(feature = "bench")] -mod benches { - use super::*; - use crate::test::{black_box, Bencher}; - - const EVENT_JSON: &str = r#"{"content":"uRuvYr585B80L6rSJiHocw==?iv=oh6LVqdsYYol3JfFnXTbPA==","created_at":1640839235,"id":"2be17aa3031bdcb006f0fce80c146dea9c1c0268b0af2398bb673365c6444d45","kind":4,"pubkey":"f86c44a2de95d9149b51c6a29afeabba264c18e2fa7c49de93424a0c56947785","sig":"a5d9290ef9659083c490b303eb7ee41356d8778ff19f2f91776c8dc4443388a64ffcf336e61af4c25c05ac3ae952d1ced889ed655b67790891222aaa15b99fdd","tags":[["p","13adc511de7e1cfcf1c6b7f6365fb5a03442d7bcacf565ea57fa7770912c023d"]]}"#; - - #[bench] - pub fn hex_encode(bh: &mut Bencher) { - bh.iter(|| { - black_box(encode(EVENT_JSON)); - }); - } - - #[bench] - pub fn hex_decode(bh: &mut Bencher) { - let h = "7b22636f6e74656e74223a227552757659723538354238304c3672534a69486f63773d3d3f69763d6f68364c5671647359596f6c334a66466e58546250413d3d222c22637265617465645f6174223a313634303833393233352c226964223a2232626531376161333033316264636230303666306663653830633134366465613963316330323638623061663233393862623637333336356336343434643435222c226b696e64223a342c227075626b6579223a2266383663343461326465393564393134396235316336613239616665616262613236346331386532666137633439646539333432346130633536393437373835222c22736967223a226135643932393065663936353930383363343930623330336562376565343133353664383737386666313966326639313737366338646334343433333838613634666663663333366536316166346332356330356163336165393532643163656438383965643635356236373739303839313232326161613135623939666464222c2274616773223a5b5b2270222c2231336164633531316465376531636663663163366237663633363566623561303334343264376263616366353635656135376661373737303931326330323364225d5d7d"; - bh.iter(|| { - black_box(decode(h)).unwrap(); - }); - } -} diff --git a/crates/cdk/src/util/mod.rs b/crates/cashu/src/util/mod.rs similarity index 73% rename from crates/cdk/src/util/mod.rs rename to crates/cashu/src/util/mod.rs index 485562ae..6a143ac9 100644 --- a/crates/cdk/src/util/mod.rs +++ b/crates/cashu/src/util/mod.rs @@ -1,16 +1,14 @@ -//! Util - #[cfg(not(target_arch = "wasm32"))] use std::time::{SystemTime, UNIX_EPOCH}; -use anyhow::Result; use bitcoin::secp256k1::{rand, All, Secp256k1}; -#[cfg(target_arch = "wasm32")] -use instant::SystemTime; use once_cell::sync::Lazy; pub mod hex; +#[cfg(target_arch = "wasm32")] +use instant::SystemTime; + #[cfg(target_arch = "wasm32")] const UNIX_EPOCH: SystemTime = SystemTime::UNIX_EPOCH; @@ -30,10 +28,22 @@ pub fn unix_time() -> u64 { .as_secs() } +#[derive(Debug, thiserror::Error)] +/// Error type for serialization +pub enum CborError { + /// CBOR serialization error + #[error("CBOR serialization error")] + Cbor(#[from] ciborium::ser::Error), + + /// CBOR diagnostic notation error + #[error("CBOR diagnostic notation error: {0}")] + CborDiag(#[from] cbor_diag::Error), +} + /// Serializes a struct to the CBOR diagnostic notation. /// /// See -pub fn serialize_to_cbor_diag(data: &T) -> Result { +pub fn serialize_to_cbor_diag(data: &T) -> Result { let mut cbor_buffer = Vec::new(); ciborium::ser::into_writer(data, &mut cbor_buffer)?; diff --git a/crates/cdk/src/wallet/types.rs b/crates/cashu/src/wallet.rs similarity index 75% rename from crates/cdk/src/wallet/types.rs rename to crates/cashu/src/wallet.rs index d1683db3..781bc9f7 100644 --- a/crates/cdk/src/wallet/types.rs +++ b/crates/cashu/src/wallet.rs @@ -1,11 +1,35 @@ //! Wallet Types +use std::fmt; + use serde::{Deserialize, Serialize}; use crate::mint_url::MintUrl; use crate::nuts::{CurrencyUnit, MeltQuoteState, MintQuoteState, SecretKey}; use crate::Amount; +/// Wallet Key +#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +pub struct WalletKey { + /// Mint Url + pub mint_url: MintUrl, + /// Currency Unit + pub unit: CurrencyUnit, +} + +impl fmt::Display for WalletKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "mint_url: {}, unit: {}", self.mint_url, self.unit,) + } +} + +impl WalletKey { + /// Create new [`WalletKey`] + pub fn new(mint_url: MintUrl, unit: CurrencyUnit) -> Self { + Self { mint_url, unit } + } +} + /// Mint Quote Info #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct MintQuote { diff --git a/crates/cdk-axum/src/ws/mod.rs b/crates/cdk-axum/src/ws/mod.rs index 4581e7f8..6357134f 100644 --- a/crates/cdk-axum/src/ws/mod.rs +++ b/crates/cdk-axum/src/ws/mod.rs @@ -1,10 +1,12 @@ use std::collections::HashMap; use axum::extract::ws::{Message, WebSocket}; -use cdk::nuts::nut17::ws::{ - NotificationInner, WsErrorBody, WsMessageOrResponse, WsMethodRequest, WsRequest, +use cdk::nuts::nut17::NotificationPayload; +use cdk::pub_sub::SubId; +use cdk::ws::{ + notification_to_ws_message, NotificationInner, WsErrorBody, WsMessageOrResponse, + WsMethodRequest, WsRequest, }; -use cdk::nuts::nut17::{NotificationPayload, SubId}; use futures::StreamExt; use tokio::sync::mpsc; use uuid::Uuid; @@ -61,10 +63,10 @@ pub async fn main_websocket(mut socket: WebSocket, state: MintState) { // unsubscribed from the subscription manager, just ignore it. continue; } - let notification: WsMessageOrResponse= NotificationInner { + let notification = notification_to_ws_message(NotificationInner { sub_id, payload, - }.into(); + }); let message = match serde_json::to_string(¬ification) { Ok(message) => message, Err(err) => { diff --git a/crates/cdk-axum/src/ws/subscribe.rs b/crates/cdk-axum/src/ws/subscribe.rs index 30f25e30..e675bf40 100644 --- a/crates/cdk-axum/src/ws/subscribe.rs +++ b/crates/cdk-axum/src/ws/subscribe.rs @@ -1,5 +1,5 @@ -use cdk::nuts::nut17::ws::{WsResponseResult, WsSubscribeResponse}; -use cdk::nuts::nut17::Params; +use cdk::subscription::{IndexableParams, Params}; +use cdk::ws::{WsResponseResult, WsSubscribeResponse}; use super::{WsContext, WsError}; @@ -15,6 +15,8 @@ pub(crate) async fn handle( return Err(WsError::InvalidParams); } + let params: IndexableParams = params.into(); + let mut subscription = context .state .mint diff --git a/crates/cdk-axum/src/ws/unsubscribe.rs b/crates/cdk-axum/src/ws/unsubscribe.rs index 0689e201..0442a109 100644 --- a/crates/cdk-axum/src/ws/unsubscribe.rs +++ b/crates/cdk-axum/src/ws/unsubscribe.rs @@ -1,4 +1,4 @@ -use cdk::nuts::nut17::ws::{WsResponseResult, WsUnsubscribeRequest, WsUnsubscribeResponse}; +use cdk::ws::{WsResponseResult, WsUnsubscribeRequest, WsUnsubscribeResponse}; use super::{WsContext, WsError}; diff --git a/crates/cdk-cli/src/sub_commands/burn.rs b/crates/cdk-cli/src/sub_commands/burn.rs index b687a620..4fbc7dcb 100644 --- a/crates/cdk-cli/src/sub_commands/burn.rs +++ b/crates/cdk-cli/src/sub_commands/burn.rs @@ -3,7 +3,7 @@ use std::str::FromStr; use anyhow::Result; use cdk::mint_url::MintUrl; use cdk::nuts::CurrencyUnit; -use cdk::wallet::multi_mint_wallet::WalletKey; +use cdk::wallet::types::WalletKey; use cdk::wallet::MultiMintWallet; use cdk::Amount; use clap::Args; diff --git a/crates/cdk-cli/src/sub_commands/melt.rs b/crates/cdk-cli/src/sub_commands/melt.rs index b152db20..2f56edbf 100644 --- a/crates/cdk-cli/src/sub_commands/melt.rs +++ b/crates/cdk-cli/src/sub_commands/melt.rs @@ -5,7 +5,8 @@ use std::str::FromStr; use anyhow::{bail, Result}; use cdk::amount::MSAT_IN_SAT; use cdk::nuts::{CurrencyUnit, MeltOptions}; -use cdk::wallet::multi_mint_wallet::{MultiMintWallet, WalletKey}; +use cdk::wallet::multi_mint_wallet::MultiMintWallet; +use cdk::wallet::types::WalletKey; use cdk::Bolt11Invoice; use clap::Args; diff --git a/crates/cdk-cli/src/sub_commands/mint.rs b/crates/cdk-cli/src/sub_commands/mint.rs index dfb18c8e..b438edf6 100644 --- a/crates/cdk-cli/src/sub_commands/mint.rs +++ b/crates/cdk-cli/src/sub_commands/mint.rs @@ -7,7 +7,7 @@ use cdk::cdk_database::{Error, WalletDatabase}; use cdk::mint_url::MintUrl; use cdk::nuts::nut00::ProofsMethods; use cdk::nuts::{CurrencyUnit, MintQuoteState, NotificationPayload}; -use cdk::wallet::multi_mint_wallet::WalletKey; +use cdk::wallet::types::WalletKey; use cdk::wallet::{MultiMintWallet, Wallet, WalletSubscription}; use cdk::Amount; use clap::Args; diff --git a/crates/cdk-cli/src/sub_commands/receive.rs b/crates/cdk-cli/src/sub_commands/receive.rs index f0d19738..990ca401 100644 --- a/crates/cdk-cli/src/sub_commands/receive.rs +++ b/crates/cdk-cli/src/sub_commands/receive.rs @@ -6,7 +6,8 @@ use anyhow::{anyhow, Result}; use cdk::cdk_database::{self, WalletDatabase}; use cdk::nuts::{SecretKey, Token}; use cdk::util::unix_time; -use cdk::wallet::multi_mint_wallet::{MultiMintWallet, WalletKey}; +use cdk::wallet::multi_mint_wallet::MultiMintWallet; +use cdk::wallet::types::WalletKey; use cdk::wallet::Wallet; use cdk::Amount; use clap::Args; diff --git a/crates/cdk-cli/src/sub_commands/restore.rs b/crates/cdk-cli/src/sub_commands/restore.rs index 3b990836..41d7839b 100644 --- a/crates/cdk-cli/src/sub_commands/restore.rs +++ b/crates/cdk-cli/src/sub_commands/restore.rs @@ -5,7 +5,7 @@ use anyhow::Result; use cdk::cdk_database::{Error, WalletDatabase}; use cdk::mint_url::MintUrl; use cdk::nuts::CurrencyUnit; -use cdk::wallet::multi_mint_wallet::WalletKey; +use cdk::wallet::types::WalletKey; use cdk::wallet::{MultiMintWallet, Wallet}; use clap::Args; diff --git a/crates/cdk-cli/src/sub_commands/send.rs b/crates/cdk-cli/src/sub_commands/send.rs index f5bcf3e5..0a642539 100644 --- a/crates/cdk-cli/src/sub_commands/send.rs +++ b/crates/cdk-cli/src/sub_commands/send.rs @@ -5,8 +5,7 @@ use std::str::FromStr; use anyhow::{bail, Result}; use cdk::amount::SplitTarget; use cdk::nuts::{Conditions, CurrencyUnit, PublicKey, SpendingConditions}; -use cdk::wallet::multi_mint_wallet::WalletKey; -use cdk::wallet::types::SendKind; +use cdk::wallet::types::{SendKind, WalletKey}; use cdk::wallet::MultiMintWallet; use cdk::Amount; use clap::Args; diff --git a/crates/cdk-cli/src/sub_commands/update_mint_url.rs b/crates/cdk-cli/src/sub_commands/update_mint_url.rs index 6b4336c7..66216203 100644 --- a/crates/cdk-cli/src/sub_commands/update_mint_url.rs +++ b/crates/cdk-cli/src/sub_commands/update_mint_url.rs @@ -1,7 +1,7 @@ use anyhow::{anyhow, Result}; use cdk::mint_url::MintUrl; use cdk::nuts::CurrencyUnit; -use cdk::wallet::multi_mint_wallet::WalletKey; +use cdk::wallet::types::WalletKey; use cdk::wallet::MultiMintWallet; use clap::Args; diff --git a/crates/cdk-common/Cargo.toml b/crates/cdk-common/Cargo.toml new file mode 100644 index 00000000..0a8e3591 --- /dev/null +++ b/crates/cdk-common/Cargo.toml @@ -0,0 +1,43 @@ +[package] +name = "cdk-common" +version = "0.6.0" +edition = "2021" +description = "CDK common types and traits" +rust-version = "1.63.0" # MSRV + +[features] +default = ["mint", "wallet"] +swagger = ["dep:utoipa", "cashu/swagger"] +bench = [] +wallet = ["cashu/wallet"] +mint = ["cashu/mint", "dep:uuid"] + +[dependencies] +async-trait = "0.1" +bitcoin = { version = "0.32.2", features = [ + "base64", + "serde", + "rand", + "rand-std", +] } +cashu = { path = "../cashu", default-features = false, version = "0.6.0" } +cbor-diag = "0.1.12" +ciborium = { version = "0.2.2", default-features = false, features = ["std"] } +serde = { version = "1", features = ["derive"] } +lightning-invoice = { version = "0.32.0", features = ["serde", "std"] } +thiserror = "2" +tracing = "0.1" +url = "2.3" +uuid = { version = "1", features = ["v4", "serde"], optional = true } +utoipa = { version = "4", optional = true } +futures = "0.3.31" +anyhow = "1.0" +serde_json = "1" +serde_with = "3" + +[target.'cfg(target_arch = "wasm32")'.dependencies] +instant = { version = "0.1", features = ["wasm-bindgen", "inaccurate"] } + +[dev-dependencies] +rand = "0.8.5" +bip39 = "2.0" diff --git a/crates/cdk/src/types.rs b/crates/cdk-common/src/common.rs similarity index 100% rename from crates/cdk/src/types.rs rename to crates/cdk-common/src/common.rs diff --git a/crates/cdk-common/src/database/mint.rs b/crates/cdk-common/src/database/mint.rs new file mode 100644 index 00000000..4755b626 --- /dev/null +++ b/crates/cdk-common/src/database/mint.rs @@ -0,0 +1,130 @@ +//! CDK Database + +use std::collections::HashMap; + +use async_trait::async_trait; +use uuid::Uuid; + +use super::Error; +use crate::common::LnKey; +use crate::mint::{self, MintKeySetInfo, MintQuote as MintMintQuote}; +use crate::nuts::{ + BlindSignature, CurrencyUnit, Id, MeltBolt11Request, MeltQuoteState, MintQuoteState, Proof, + Proofs, PublicKey, State, +}; + +/// Mint Database trait +#[async_trait] +pub trait Database { + /// Mint Database Error + type Err: Into + From; + + /// Add Active Keyset + async fn set_active_keyset(&self, unit: CurrencyUnit, id: Id) -> Result<(), Self::Err>; + /// Get Active Keyset + async fn get_active_keyset_id(&self, unit: &CurrencyUnit) -> Result, Self::Err>; + /// Get all Active Keyset + async fn get_active_keysets(&self) -> Result, Self::Err>; + + /// Add [`MintMintQuote`] + async fn add_mint_quote(&self, quote: MintMintQuote) -> Result<(), Self::Err>; + /// Get [`MintMintQuote`] + async fn get_mint_quote(&self, quote_id: &Uuid) -> Result, Self::Err>; + /// Update state of [`MintMintQuote`] + async fn update_mint_quote_state( + &self, + quote_id: &Uuid, + state: MintQuoteState, + ) -> Result; + /// Get all [`MintMintQuote`]s + async fn get_mint_quote_by_request( + &self, + request: &str, + ) -> Result, Self::Err>; + /// Get all [`MintMintQuote`]s + async fn get_mint_quote_by_request_lookup_id( + &self, + request_lookup_id: &str, + ) -> Result, Self::Err>; + /// Get Mint Quotes + async fn get_mint_quotes(&self) -> Result, Self::Err>; + /// Remove [`MintMintQuote`] + async fn remove_mint_quote(&self, quote_id: &Uuid) -> Result<(), Self::Err>; + + /// Add [`mint::MeltQuote`] + async fn add_melt_quote(&self, quote: mint::MeltQuote) -> Result<(), Self::Err>; + /// Get [`mint::MeltQuote`] + async fn get_melt_quote(&self, quote_id: &Uuid) -> Result, Self::Err>; + /// Update [`mint::MeltQuote`] state + async fn update_melt_quote_state( + &self, + quote_id: &Uuid, + state: MeltQuoteState, + ) -> Result; + /// Get all [`mint::MeltQuote`]s + async fn get_melt_quotes(&self) -> Result, Self::Err>; + /// Remove [`mint::MeltQuote`] + async fn remove_melt_quote(&self, quote_id: &Uuid) -> Result<(), Self::Err>; + + /// Add melt request + async fn add_melt_request( + &self, + melt_request: MeltBolt11Request, + ln_key: LnKey, + ) -> Result<(), Self::Err>; + /// Get melt request + async fn get_melt_request( + &self, + quote_id: &Uuid, + ) -> Result, LnKey)>, Self::Err>; + + /// Add [`MintKeySetInfo`] + async fn add_keyset_info(&self, keyset: MintKeySetInfo) -> Result<(), Self::Err>; + /// Get [`MintKeySetInfo`] + async fn get_keyset_info(&self, id: &Id) -> Result, Self::Err>; + /// Get [`MintKeySetInfo`]s + async fn get_keyset_infos(&self) -> Result, Self::Err>; + + /// Add spent [`Proofs`] + async fn add_proofs(&self, proof: Proofs, quote_id: Option) -> Result<(), Self::Err>; + /// Get [`Proofs`] by ys + async fn get_proofs_by_ys(&self, ys: &[PublicKey]) -> Result>, Self::Err>; + /// Get ys by quote id + async fn get_proof_ys_by_quote_id(&self, quote_id: &Uuid) -> Result, Self::Err>; + /// Get [`Proofs`] state + async fn get_proofs_states(&self, ys: &[PublicKey]) -> Result>, Self::Err>; + /// Get [`Proofs`] state + async fn update_proofs_states( + &self, + ys: &[PublicKey], + proofs_state: State, + ) -> Result>, Self::Err>; + /// Get [`Proofs`] by state + async fn get_proofs_by_keyset_id( + &self, + keyset_id: &Id, + ) -> Result<(Proofs, Vec>), Self::Err>; + + /// Add [`BlindSignature`] + async fn add_blind_signatures( + &self, + blinded_messages: &[PublicKey], + blind_signatures: &[BlindSignature], + quote_id: Option, + ) -> Result<(), Self::Err>; + /// Get [`BlindSignature`]s + async fn get_blind_signatures( + &self, + blinded_messages: &[PublicKey], + ) -> Result>, Self::Err>; + /// Get [`BlindSignature`]s for keyset_id + async fn get_blind_signatures_for_keyset( + &self, + keyset_id: &Id, + ) -> Result, Self::Err>; + /// Get [`BlindSignature`]s for quote + async fn get_blind_signatures_for_quote( + &self, + quote_id: &Uuid, + ) -> Result, Self::Err>; +} diff --git a/crates/cdk-common/src/database/mod.rs b/crates/cdk-common/src/database/mod.rs new file mode 100644 index 00000000..f9a0e5ca --- /dev/null +++ b/crates/cdk-common/src/database/mod.rs @@ -0,0 +1,34 @@ +//! CDK Database + +#[cfg(feature = "mint")] +mod mint; +#[cfg(feature = "wallet")] +mod wallet; + +#[cfg(feature = "mint")] +pub use mint::Database as MintDatabase; +#[cfg(feature = "wallet")] +pub use wallet::Database as WalletDatabase; + +/// CDK_database error +#[derive(Debug, thiserror::Error)] +pub enum Error { + /// Database Error + #[error(transparent)] + Database(Box), + /// DHKE error + #[error(transparent)] + DHKE(#[from] crate::dhke::Error), + /// NUT00 Error + #[error(transparent)] + NUT00(#[from] crate::nuts::nut00::Error), + /// NUT02 Error + #[error(transparent)] + NUT02(#[from] crate::nuts::nut02::Error), + /// Serde Error + #[error(transparent)] + Serde(#[from] serde_json::Error), + /// Unknown Quote + #[error("Unknown Quote")] + UnknownQuote, +} diff --git a/crates/cdk-common/src/database/wallet.rs b/crates/cdk-common/src/database/wallet.rs new file mode 100644 index 00000000..e9213a1e --- /dev/null +++ b/crates/cdk-common/src/database/wallet.rs @@ -0,0 +1,120 @@ +//! CDK Database + +use std::collections::HashMap; +use std::fmt::Debug; + +use async_trait::async_trait; + +use super::Error; +use crate::common::ProofInfo; +use crate::mint_url::MintUrl; +use crate::nuts::{ + CurrencyUnit, Id, KeySetInfo, Keys, MintInfo, PublicKey, SpendingConditions, State, +}; +use crate::wallet; +use crate::wallet::MintQuote as WalletMintQuote; + +/// Wallet Database trait +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait Database: Debug { + /// Wallet Database Error + type Err: Into + From; + + /// Add Mint to storage + async fn add_mint( + &self, + mint_url: MintUrl, + mint_info: Option, + ) -> Result<(), Self::Err>; + /// Remove Mint from storage + async fn remove_mint(&self, mint_url: MintUrl) -> Result<(), Self::Err>; + /// Get mint from storage + async fn get_mint(&self, mint_url: MintUrl) -> Result, Self::Err>; + /// Get all mints from storage + async fn get_mints(&self) -> Result>, Self::Err>; + /// Update mint url + async fn update_mint_url( + &self, + old_mint_url: MintUrl, + new_mint_url: MintUrl, + ) -> Result<(), Self::Err>; + + /// Add mint keyset to storage + async fn add_mint_keysets( + &self, + mint_url: MintUrl, + keysets: Vec, + ) -> Result<(), Self::Err>; + /// Get mint keysets for mint url + async fn get_mint_keysets( + &self, + mint_url: MintUrl, + ) -> Result>, Self::Err>; + /// Get mint keyset by id + async fn get_keyset_by_id(&self, keyset_id: &Id) -> Result, Self::Err>; + + /// Add mint quote to storage + async fn add_mint_quote(&self, quote: WalletMintQuote) -> Result<(), Self::Err>; + /// Get mint quote from storage + async fn get_mint_quote(&self, quote_id: &str) -> Result, Self::Err>; + /// Get mint quotes from storage + async fn get_mint_quotes(&self) -> Result, Self::Err>; + /// Remove mint quote from storage + async fn remove_mint_quote(&self, quote_id: &str) -> Result<(), Self::Err>; + + /// Add melt quote to storage + async fn add_melt_quote(&self, quote: wallet::MeltQuote) -> Result<(), Self::Err>; + /// Get melt quote from storage + async fn get_melt_quote(&self, quote_id: &str) -> Result, Self::Err>; + /// Remove melt quote from storage + async fn remove_melt_quote(&self, quote_id: &str) -> Result<(), Self::Err>; + + /// Add [`Keys`] to storage + async fn add_keys(&self, keys: Keys) -> Result<(), Self::Err>; + /// Get [`Keys`] from storage + async fn get_keys(&self, id: &Id) -> Result, Self::Err>; + /// Remove [`Keys`] from storage + async fn remove_keys(&self, id: &Id) -> Result<(), Self::Err>; + + /// Update the proofs in storage by adding new proofs or removing proofs by + /// their Y value. + async fn update_proofs( + &self, + added: Vec, + removed_ys: Vec, + ) -> Result<(), Self::Err>; + /// Set proofs as pending in storage. Proofs are identified by their Y + /// value. + async fn set_pending_proofs(&self, ys: Vec) -> Result<(), Self::Err>; + /// Reserve proofs in storage. Proofs are identified by their Y value. + async fn reserve_proofs(&self, ys: Vec) -> Result<(), Self::Err>; + /// Set proofs as unspent in storage. Proofs are identified by their Y + /// value. + async fn set_unspent_proofs(&self, ys: Vec) -> Result<(), Self::Err>; + /// Get proofs from storage + async fn get_proofs( + &self, + mint_url: Option, + unit: Option, + state: Option>, + spending_conditions: Option>, + ) -> Result, Self::Err>; + + /// Increment Keyset counter + async fn increment_keyset_counter(&self, keyset_id: &Id, count: u32) -> Result<(), Self::Err>; + /// Get current Keyset counter + async fn get_keyset_counter(&self, keyset_id: &Id) -> Result, Self::Err>; + + /// Get when nostr key was last checked + async fn get_nostr_last_checked( + &self, + verifying_key: &PublicKey, + ) -> Result, Self::Err>; + /// Update last checked time + async fn add_nostr_last_checked( + &self, + verifying_key: PublicKey, + last_checked: u32, + ) -> Result<(), Self::Err>; +} diff --git a/crates/cdk/src/error.rs b/crates/cdk-common/src/error.rs similarity index 98% rename from crates/cdk/src/error.rs rename to crates/cdk-common/src/error.rs index edeb7b03..6c2471c2 100644 --- a/crates/cdk/src/error.rs +++ b/crates/cdk-common/src/error.rs @@ -9,7 +9,7 @@ use thiserror::Error; use crate::nuts::Id; use crate::util::hex; #[cfg(feature = "wallet")] -use crate::wallet::multi_mint_wallet::WalletKey; +use crate::wallet::WalletKey; use crate::Amount; /// CDK Error @@ -128,8 +128,8 @@ pub enum Error { #[error("Incorrect wallet: `{0}`")] IncorrectWallet(String), /// Unknown Wallet - #[cfg(feature = "wallet")] #[error("Unknown wallet: `{0}`")] + #[cfg(feature = "wallet")] UnknownWallet(WalletKey), /// Max Fee Ecxeded #[error("Max fee exceeded")] @@ -197,10 +197,9 @@ pub enum Error { /// From hex error #[error(transparent)] HexError(#[from] hex::Error), - #[cfg(feature = "wallet")] - /// From hex error - #[error(transparent)] - ReqwestError(#[from] reqwest::Error), + /// Http transport error + #[error("Http transport error: {0}")] + HttpError(String), // Crate error conversions /// Cashu Url Error @@ -238,6 +237,7 @@ pub enum Error { NUT12(#[from] crate::nuts::nut12::Error), /// NUT13 Error #[error(transparent)] + #[cfg(feature = "wallet")] NUT13(#[from] crate::nuts::nut13::Error), /// NUT14 Error #[error(transparent)] @@ -249,13 +249,12 @@ pub enum Error { #[error(transparent)] NUT20(#[from] crate::nuts::nut20::Error), /// Database Error - #[cfg(any(feature = "wallet", feature = "mint"))] #[error(transparent)] - Database(#[from] crate::cdk_database::Error), + Database(#[from] crate::database::Error), /// Lightning Error - #[cfg(feature = "mint")] #[error(transparent)] - Lightning(#[from] crate::cdk_lightning::Error), + #[cfg(feature = "mint")] + Lightning(#[from] crate::lightning::Error), } /// CDK Error Response diff --git a/crates/cdk-common/src/lib.rs b/crates/cdk-common/src/lib.rs new file mode 100644 index 00000000..52e7068e --- /dev/null +++ b/crates/cdk-common/src/lib.rs @@ -0,0 +1,28 @@ +//! Cashu shared types and functions. +//! +//! This crate is the base foundation to build things that can interact with the CDK (Cashu +//! Development Kit) and their internal crates. +//! +//! This is meant to contain the shared types, traits and common functions that are used across the +//! internal crates. + +pub mod common; +pub mod database; +pub mod error; +#[cfg(feature = "mint")] +pub mod lightning; +pub mod pub_sub; +#[cfg(feature = "mint")] +pub mod subscription; +pub mod ws; + +// re-exporting external crates +pub use bitcoin; +pub use cashu::amount::{self, Amount}; +pub use cashu::lightning_invoice::{self, Bolt11Invoice}; +#[cfg(feature = "mint")] +pub use cashu::mint; +pub use cashu::nuts::{self, *}; +#[cfg(feature = "wallet")] +pub use cashu::wallet; +pub use cashu::{dhke, mint_url, secret, util, SECP256K1}; diff --git a/crates/cdk/src/cdk_lightning/mod.rs b/crates/cdk-common/src/lightning.rs similarity index 100% rename from crates/cdk/src/cdk_lightning/mod.rs rename to crates/cdk-common/src/lightning.rs diff --git a/crates/cdk/src/pub_sub/index.rs b/crates/cdk-common/src/pub_sub/index.rs similarity index 100% rename from crates/cdk/src/pub_sub/index.rs rename to crates/cdk-common/src/pub_sub/index.rs diff --git a/crates/cdk-common/src/pub_sub/mod.rs b/crates/cdk-common/src/pub_sub/mod.rs new file mode 100644 index 00000000..35c6bb15 --- /dev/null +++ b/crates/cdk-common/src/pub_sub/mod.rs @@ -0,0 +1,77 @@ +//! Publish–subscribe pattern. +//! +//! This is a generic implementation for +//! [NUT-17(https://github.com/cashubtc/nuts/blob/main/17.md) with a type +//! agnostic Publish-subscribe manager. +//! +//! The manager has a method for subscribers to subscribe to events with a +//! generic type that must be converted to a vector of indexes. +//! +//! Events are also generic that should implement the `Indexable` trait. +use std::fmt::Debug; +use std::ops::Deref; +use std::str::FromStr; + +use serde::{Deserialize, Serialize}; + +pub mod index; + +/// Default size of the remove channel +pub const DEFAULT_REMOVE_SIZE: usize = 10_000; + +/// Default channel size for subscription buffering +pub const DEFAULT_CHANNEL_SIZE: usize = 10; + +#[async_trait::async_trait] +/// On New Subscription trait +/// +/// This trait is optional and it is used to notify the application when a new +/// subscription is created. This is useful when the application needs to send +/// the initial state to the subscriber upon subscription +pub trait OnNewSubscription { + /// Index type + type Index; + /// Subscription event type + type Event; + + /// Called when a new subscription is created + async fn on_new_subscription( + &self, + request: &[&Self::Index], + ) -> Result, String>; +} + +/// Subscription Id wrapper +/// +/// This is the place to add some sane default (like a max length) to the +/// subscription ID +#[derive(Debug, Clone, Default, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] +pub struct SubId(String); + +impl From<&str> for SubId { + fn from(s: &str) -> Self { + Self(s.to_string()) + } +} + +impl From for SubId { + fn from(s: String) -> Self { + Self(s) + } +} + +impl FromStr for SubId { + type Err = (); + + fn from_str(s: &str) -> Result { + Ok(Self(s.to_string())) + } +} + +impl Deref for SubId { + type Target = String; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} diff --git a/crates/cdk-common/src/subscription.rs b/crates/cdk-common/src/subscription.rs new file mode 100644 index 00000000..3d62270c --- /dev/null +++ b/crates/cdk-common/src/subscription.rs @@ -0,0 +1,74 @@ +//! Subscription types and traits +use std::str::FromStr; + +use cashu::nut17::{self, Error, Kind, Notification}; +use cashu::{NotificationPayload, PublicKey}; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +use crate::pub_sub::index::{Index, Indexable, SubscriptionGlobalId}; +use crate::pub_sub::SubId; + +/// Subscription parameters. +/// +/// This is a concrete type alias for `nut17::Params`. +pub type Params = nut17::Params; + +/// Wrapper around `nut17::Params` to implement `Indexable` for `Notification`. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct IndexableParams(Params); + +impl From for IndexableParams { + fn from(params: Params) -> Self { + Self(params) + } +} + +impl TryFrom for Vec> { + type Error = Error; + fn try_from(params: IndexableParams) -> Result { + let sub_id: SubscriptionGlobalId = Default::default(); + let params = params.0; + params + .filters + .into_iter() + .map(|filter| { + let idx = match params.kind { + Kind::Bolt11MeltQuote => { + Notification::MeltQuoteBolt11(Uuid::from_str(&filter)?) + } + Kind::Bolt11MintQuote => { + Notification::MintQuoteBolt11(Uuid::from_str(&filter)?) + } + Kind::ProofState => Notification::ProofState(PublicKey::from_str(&filter)?), + }; + + Ok(Index::from((idx, params.id.clone(), sub_id))) + }) + .collect::>() + } +} + +impl AsRef for IndexableParams { + fn as_ref(&self) -> &SubId { + &self.0.id + } +} + +impl Indexable for NotificationPayload { + type Type = Notification; + + fn to_indexes(&self) -> Vec> { + match self { + NotificationPayload::ProofState(proof_state) => { + vec![Index::from(Notification::ProofState(proof_state.y))] + } + NotificationPayload::MeltQuoteBolt11Response(melt_quote) => { + vec![Index::from(Notification::MeltQuoteBolt11(melt_quote.quote))] + } + NotificationPayload::MintQuoteBolt11Response(mint_quote) => { + vec![Index::from(Notification::MintQuoteBolt11(mint_quote.quote))] + } + } + } +} diff --git a/crates/cdk-common/src/ws.rs b/crates/cdk-common/src/ws.rs new file mode 100644 index 00000000..da2ce573 --- /dev/null +++ b/crates/cdk-common/src/ws.rs @@ -0,0 +1,52 @@ +//! Websocket types and functions for the CDK. +//! +//! This module extends the `cashu` crate with types and functions for the CDK, using the correct +//! expected ID types. +#[cfg(feature = "mint")] +use cashu::nut17::ws::JSON_RPC_VERSION; +use cashu::nut17::{self}; +#[cfg(feature = "mint")] +use cashu::NotificationPayload; +#[cfg(feature = "mint")] +use uuid::Uuid; + +use crate::pub_sub::SubId; + +pub type WsUnsubscribeRequest = nut17::ws::WsUnsubscribeRequest; +pub type WsNotification = nut17::ws::WsNotification; +pub type WsSubscribeResponse = nut17::ws::WsSubscribeResponse; +pub type WsResponseResult = nut17::ws::WsResponseResult; +pub type WsUnsubscribeResponse = nut17::ws::WsUnsubscribeResponse; +pub type WsRequest = nut17::ws::WsRequest; +pub type WsResponse = nut17::ws::WsResponse; +pub type WsMethodRequest = nut17::ws::WsMethodRequest; +pub type WsErrorBody = nut17::ws::WsErrorBody; +pub type WsMessageOrResponse = nut17::ws::WsMessageOrResponse; +pub type NotificationInner = nut17::ws::NotificationInner; + +#[cfg(feature = "mint")] +pub fn notification_uuid_to_notification_string( + notification: NotificationInner, +) -> NotificationInner { + nut17::ws::NotificationInner { + sub_id: notification.sub_id, + payload: match notification.payload { + NotificationPayload::ProofState(pk) => NotificationPayload::ProofState(pk), + NotificationPayload::MeltQuoteBolt11Response(quote) => { + NotificationPayload::MeltQuoteBolt11Response(quote.to_string_id()) + } + NotificationPayload::MintQuoteBolt11Response(quote) => { + NotificationPayload::MintQuoteBolt11Response(quote.to_string_id()) + } + }, + } +} + +#[cfg(feature = "mint")] +pub fn notification_to_ws_message(notification: NotificationInner) -> WsMessageOrResponse { + nut17::ws::WsMessageOrResponse::Notification(nut17::ws::WsNotification { + jsonrpc: JSON_RPC_VERSION.to_owned(), + method: "subscribe".to_string(), + params: notification_uuid_to_notification_string(notification), + }) +} diff --git a/crates/cdk-integration-tests/tests/mint.rs b/crates/cdk-integration-tests/tests/mint.rs index 96892cc4..10cd5a16 100644 --- a/crates/cdk-integration-tests/tests/mint.rs +++ b/crates/cdk-integration-tests/tests/mint.rs @@ -11,11 +11,11 @@ use cdk::cdk_database::mint_memory::MintMemoryDatabase; use cdk::dhke::construct_proofs; use cdk::mint::MintQuote; use cdk::nuts::nut00::ProofsMethods; -use cdk::nuts::nut17::Params; use cdk::nuts::{ CurrencyUnit, Id, MintBolt11Request, MintInfo, NotificationPayload, Nuts, PreMintSecrets, ProofState, Proofs, SecretKey, SpendingConditions, State, SwapRequest, }; +use cdk::subscription::{IndexableParams, Params}; use cdk::types::QuoteTTL; use cdk::util::unix_time; use cdk::Mint; @@ -232,11 +232,14 @@ pub async fn test_p2pk_swap() -> Result<()> { let mut listener = mint .pubsub_manager - .try_subscribe(Params { - kind: cdk::nuts::nut17::Kind::ProofState, - filters: public_keys_to_listen.clone(), - id: "test".into(), - }) + .try_subscribe::( + Params { + kind: cdk::nuts::nut17::Kind::ProofState, + filters: public_keys_to_listen.clone(), + id: "test".into(), + } + .into(), + ) .await .expect("valid subscription"); diff --git a/crates/cdk-redb/Cargo.toml b/crates/cdk-redb/Cargo.toml index c6382468..06ed9a82 100644 --- a/crates/cdk-redb/Cargo.toml +++ b/crates/cdk-redb/Cargo.toml @@ -7,20 +7,23 @@ description = "Redb storage backend for CDK" license = "MIT" homepage = "https://github.com/cashubtc/cdk" repository = "https://github.com/cashubtc/cdk.git" -rust-version = "1.66.0" # MSRV +rust-version = "1.66.0" # MSRV # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] default = ["mint", "wallet"] -mint = ["cdk/mint"] -wallet = ["cdk/wallet"] +mint = [] +wallet = [] [dependencies] async-trait = "0.1" -cdk = { path = "../cdk", version = "0.6.0", default-features = false } +cdk-common = { path = "../cdk-common", version = "0.6.0" } redb = "2.1.0" thiserror = "1" -tracing = { version = "0.1", default-features = false, features = ["attributes", "log"] } +tracing = { version = "0.1", default-features = false, features = [ + "attributes", + "log", +] } serde = { version = "1", default-features = false, features = ["derive"] } serde_json = "1" lightning-invoice = { version = "0.32.0", features = ["serde", "std"] } diff --git a/crates/cdk-redb/src/error.rs b/crates/cdk-redb/src/error.rs index 556adb78..a4a51a00 100644 --- a/crates/cdk-redb/src/error.rs +++ b/crates/cdk-redb/src/error.rs @@ -33,22 +33,22 @@ pub enum Error { ParseInt(#[from] ParseIntError), /// CDK Database Error #[error(transparent)] - CDKDatabase(#[from] cdk::cdk_database::Error), + CDKDatabase(#[from] cdk_common::database::Error), /// CDK Mint Url Error #[error(transparent)] - CDKMintUrl(#[from] cdk::mint_url::Error), + CDKMintUrl(#[from] cdk_common::mint_url::Error), /// CDK Error #[error(transparent)] - CDK(#[from] cdk::error::Error), + CDK(#[from] cdk_common::error::Error), /// NUT00 Error #[error(transparent)] - CDKNUT00(#[from] cdk::nuts::nut00::Error), + CDKNUT00(#[from] cdk_common::nuts::nut00::Error), /// NUT02 Error #[error(transparent)] - CDKNUT02(#[from] cdk::nuts::nut02::Error), + CDKNUT02(#[from] cdk_common::nuts::nut02::Error), /// DHKE Error #[error(transparent)] - DHKE(#[from] cdk::dhke::Error), + DHKE(#[from] cdk_common::dhke::Error), /// Unknown Mint Info #[error("Unknown mint info")] UnknownMintInfo, @@ -60,7 +60,7 @@ pub enum Error { UnknownDatabaseVersion, } -impl From for cdk::cdk_database::Error { +impl From for cdk_common::database::Error { fn from(e: Error) -> Self { Self::Database(Box::new(e)) } diff --git a/crates/cdk-redb/src/migrations.rs b/crates/cdk-redb/src/migrations.rs index 6a8fd8cf..0a40068d 100644 --- a/crates/cdk-redb/src/migrations.rs +++ b/crates/cdk-redb/src/migrations.rs @@ -1,9 +1,8 @@ use std::collections::HashMap; use std::sync::Arc; -use cdk::mint_url::MintUrl; -use cdk::nuts::{CurrencyUnit, MeltQuoteState, MintQuoteState}; -use cdk::Amount; +use cdk_common::mint_url::MintUrl; +use cdk_common::{Amount, CurrencyUnit, MeltQuoteState, MintQuoteState}; use redb::{Database, ReadableTable, TableDefinition}; use serde::{Deserialize, Serialize}; diff --git a/crates/cdk-redb/src/mint/migrations.rs b/crates/cdk-redb/src/mint/migrations.rs index 48b2ae68..3dafb99a 100644 --- a/crates/cdk-redb/src/mint/migrations.rs +++ b/crates/cdk-redb/src/mint/migrations.rs @@ -3,10 +3,9 @@ use std::collections::HashMap; use std::str::FromStr; use std::sync::Arc; -use cdk::mint::MintQuote; -use cdk::mint_url::MintUrl; -use cdk::nuts::{CurrencyUnit, MintQuoteState, Proof, State}; -use cdk::Amount; +use cdk_common::mint::MintQuote; +use cdk_common::mint_url::MintUrl; +use cdk_common::{Amount, CurrencyUnit, MintQuoteState, Proof, State}; use lightning_invoice::Bolt11Invoice; use redb::{ Database, MultimapTableDefinition, ReadableMultimapTable, ReadableTable, TableDefinition, diff --git a/crates/cdk-redb/src/mint/mod.rs b/crates/cdk-redb/src/mint/mod.rs index c5517c86..89385f45 100644 --- a/crates/cdk-redb/src/mint/mod.rs +++ b/crates/cdk-redb/src/mint/mod.rs @@ -7,16 +7,15 @@ use std::str::FromStr; use std::sync::Arc; use async_trait::async_trait; -use cdk::cdk_database::MintDatabase; -use cdk::dhke::hash_to_curve; -use cdk::mint::{MintKeySetInfo, MintQuote}; -use cdk::nuts::nut00::ProofsMethods; -use cdk::nuts::{ +use cdk_common::common::LnKey; +use cdk_common::database::{self, MintDatabase}; +use cdk_common::dhke::hash_to_curve; +use cdk_common::mint::{self, MintKeySetInfo, MintQuote}; +use cdk_common::nut00::ProofsMethods; +use cdk_common::{ BlindSignature, CurrencyUnit, Id, MeltBolt11Request, MeltQuoteState, MintQuoteState, Proof, Proofs, PublicKey, State, }; -use cdk::types::LnKey; -use cdk::{cdk_database, mint}; use migrations::{migrate_01_to_02, migrate_04_to_05}; use redb::{Database, MultimapTableDefinition, ReadableTable, TableDefinition}; use uuid::Uuid; @@ -162,7 +161,7 @@ impl MintRedbDatabase { #[async_trait] impl MintDatabase for MintRedbDatabase { - type Err = cdk_database::Error; + type Err = database::Error; async fn set_active_keyset(&self, unit: CurrencyUnit, id: Id) -> Result<(), Self::Err> { let write_txn = self.db.begin_write().map_err(Error::from)?; diff --git a/crates/cdk-redb/src/wallet/migrations.rs b/crates/cdk-redb/src/wallet/migrations.rs index bbfbc24d..27f11332 100644 --- a/crates/cdk-redb/src/wallet/migrations.rs +++ b/crates/cdk-redb/src/wallet/migrations.rs @@ -3,7 +3,7 @@ use std::ops::Deref; use std::str::FromStr; use std::sync::Arc; -use cdk::mint_url::MintUrl; +use cdk_common::mint_url::MintUrl; use redb::{ Database, MultimapTableDefinition, ReadableMultimapTable, ReadableTable, TableDefinition, }; diff --git a/crates/cdk-redb/src/wallet/mod.rs b/crates/cdk-redb/src/wallet/mod.rs index 29e1ff50..ff7a8478 100644 --- a/crates/cdk-redb/src/wallet/mod.rs +++ b/crates/cdk-redb/src/wallet/mod.rs @@ -7,15 +7,14 @@ use std::str::FromStr; use std::sync::Arc; use async_trait::async_trait; -use cdk::cdk_database::WalletDatabase; -use cdk::mint_url::MintUrl; -use cdk::nuts::{ - CurrencyUnit, Id, KeySetInfo, Keys, MintInfo, PublicKey, SpendingConditions, State, +use cdk_common::common::ProofInfo; +use cdk_common::database::WalletDatabase; +use cdk_common::mint_url::MintUrl; +use cdk_common::util::unix_time; +use cdk_common::wallet::{self, MintQuote}; +use cdk_common::{ + database, CurrencyUnit, Id, KeySetInfo, Keys, MintInfo, PublicKey, SpendingConditions, State, }; -use cdk::types::ProofInfo; -use cdk::util::unix_time; -use cdk::wallet::MintQuote; -use cdk::{cdk_database, wallet}; use redb::{Database, MultimapTableDefinition, ReadableTable, TableDefinition}; use tracing::instrument; @@ -153,7 +152,7 @@ impl WalletRedbDatabase { &self, ys: Vec, state: State, - ) -> Result<(), cdk_database::Error> { + ) -> Result<(), database::Error> { let read_txn = self.db.begin_read().map_err(Error::from)?; let table = read_txn.open_table(PROOFS_TABLE).map_err(Error::from)?; @@ -192,7 +191,7 @@ impl WalletRedbDatabase { #[async_trait] impl WalletDatabase for WalletRedbDatabase { - type Err = cdk_database::Error; + type Err = database::Error; #[instrument(skip(self))] async fn add_mint( diff --git a/crates/cdk-sqlite/Cargo.toml b/crates/cdk-sqlite/Cargo.toml index 4f6c0e22..df0523d3 100644 --- a/crates/cdk-sqlite/Cargo.toml +++ b/crates/cdk-sqlite/Cargo.toml @@ -7,26 +7,31 @@ description = "SQLite storage backend for CDK" license = "MIT" homepage = "https://github.com/cashubtc/cdk" repository = "https://github.com/cashubtc/cdk.git" -rust-version = "1.66.0" # MSRV +rust-version = "1.66.0" # MSRV # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] default = ["mint", "wallet"] -mint = ["cdk/mint"] -wallet = ["cdk/wallet"] +mint = [] +wallet = [] [dependencies] async-trait = "0.1" -cdk = { path = "../cdk", version = "0.6.0", default-features = false } +cdk-common = { path = "../cdk-common", version = "0.6.0" } bitcoin = { version = "0.32.2", default-features = false } -sqlx = { version = "0.6.3", default-features = false, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate", "uuid"] } -thiserror = "1" -tokio = { version = "1", features = [ - "time", +sqlx = { version = "0.6.3", default-features = false, features = [ + "runtime-tokio-rustls", + "sqlite", "macros", - "sync", + "migrate", + "uuid", +] } +thiserror = "1" +tokio = { version = "1", features = ["time", "macros", "sync"] } +tracing = { version = "0.1", default-features = false, features = [ + "attributes", + "log", ] } -tracing = { version = "0.1", default-features = false, features = ["attributes", "log"] } serde_json = "1" lightning-invoice = { version = "0.32.0", features = ["serde", "std"] } uuid = { version = "1", features = ["v4", "serde"] } diff --git a/crates/cdk-sqlite/src/mint/error.rs b/crates/cdk-sqlite/src/mint/error.rs index 5f2be090..e4c185fd 100644 --- a/crates/cdk-sqlite/src/mint/error.rs +++ b/crates/cdk-sqlite/src/mint/error.rs @@ -10,31 +10,31 @@ pub enum Error { SQLX(#[from] sqlx::Error), /// NUT00 Error #[error(transparent)] - CDKNUT00(#[from] cdk::nuts::nut00::Error), + CDKNUT00(#[from] cdk_common::nuts::nut00::Error), /// NUT01 Error #[error(transparent)] - CDKNUT01(#[from] cdk::nuts::nut01::Error), + CDKNUT01(#[from] cdk_common::nuts::nut01::Error), /// NUT02 Error #[error(transparent)] - CDKNUT02(#[from] cdk::nuts::nut02::Error), + CDKNUT02(#[from] cdk_common::nuts::nut02::Error), /// NUT04 Error #[error(transparent)] - CDKNUT04(#[from] cdk::nuts::nut04::Error), + CDKNUT04(#[from] cdk_common::nuts::nut04::Error), /// NUT05 Error #[error(transparent)] - CDKNUT05(#[from] cdk::nuts::nut05::Error), + CDKNUT05(#[from] cdk_common::nuts::nut05::Error), /// NUT07 Error #[error(transparent)] - CDKNUT07(#[from] cdk::nuts::nut07::Error), + CDKNUT07(#[from] cdk_common::nuts::nut07::Error), /// Secret Error #[error(transparent)] - CDKSECRET(#[from] cdk::secret::Error), + CDKSECRET(#[from] cdk_common::secret::Error), /// BIP32 Error #[error(transparent)] BIP32(#[from] bitcoin::bip32::Error), /// Mint Url Error #[error(transparent)] - MintUrl(#[from] cdk::mint_url::Error), + MintUrl(#[from] cdk_common::mint_url::Error), /// Could Not Initialize Database #[error("Could not initialize database")] CouldNotInitialize, @@ -46,7 +46,7 @@ pub enum Error { Serde(#[from] serde_json::Error), } -impl From for cdk::cdk_database::Error { +impl From for cdk_common::database::Error { fn from(e: Error) -> Self { Self::Database(Box::new(e)) } diff --git a/crates/cdk-sqlite/src/mint/mod.rs b/crates/cdk-sqlite/src/mint/mod.rs index 2d8c5623..d0c6dbe0 100644 --- a/crates/cdk-sqlite/src/mint/mod.rs +++ b/crates/cdk-sqlite/src/mint/mod.rs @@ -7,18 +7,17 @@ use std::time::Duration; use async_trait::async_trait; use bitcoin::bip32::DerivationPath; -use cdk::cdk_database::{self, MintDatabase}; -use cdk::mint::{MintKeySetInfo, MintQuote}; -use cdk::mint_url::MintUrl; -use cdk::nuts::nut00::ProofsMethods; -use cdk::nuts::nut05::QuoteState; -use cdk::nuts::{ - BlindSignature, BlindSignatureDleq, CurrencyUnit, Id, MeltBolt11Request, MeltQuoteState, - MintQuoteState, PaymentMethod, Proof, Proofs, PublicKey, SecretKey, State, +use cdk_common::common::LnKey; +use cdk_common::database::{self, MintDatabase}; +use cdk_common::mint::{self, MintKeySetInfo, MintQuote}; +use cdk_common::mint_url::MintUrl; +use cdk_common::nut00::ProofsMethods; +use cdk_common::nut05::QuoteState; +use cdk_common::secret::Secret; +use cdk_common::{ + Amount, BlindSignature, BlindSignatureDleq, CurrencyUnit, Id, MeltBolt11Request, + MeltQuoteState, MintQuoteState, PaymentMethod, Proof, Proofs, PublicKey, SecretKey, State, }; -use cdk::secret::Secret; -use cdk::types::LnKey; -use cdk::{mint, Amount}; use error::Error; use lightning_invoice::Bolt11Invoice; use sqlx::sqlite::{SqliteConnectOptions, SqlitePool, SqlitePoolOptions, SqliteRow}; @@ -63,7 +62,7 @@ impl MintSqliteDatabase { #[async_trait] impl MintDatabase for MintSqliteDatabase { - type Err = cdk_database::Error; + type Err = database::Error; async fn set_active_keyset(&self, unit: CurrencyUnit, id: Id) -> Result<(), Self::Err> { let mut transaction = self.pool.begin().await.map_err(Error::from)?; diff --git a/crates/cdk-sqlite/src/wallet/error.rs b/crates/cdk-sqlite/src/wallet/error.rs index bfebdf26..66514cfe 100644 --- a/crates/cdk-sqlite/src/wallet/error.rs +++ b/crates/cdk-sqlite/src/wallet/error.rs @@ -13,28 +13,28 @@ pub enum Error { Serde(#[from] serde_json::Error), /// NUT00 Error #[error(transparent)] - CDKNUT00(#[from] cdk::nuts::nut00::Error), + CDKNUT00(#[from] cdk_common::nuts::nut00::Error), /// NUT01 Error #[error(transparent)] - CDKNUT01(#[from] cdk::nuts::nut01::Error), + CDKNUT01(#[from] cdk_common::nuts::nut01::Error), /// NUT02 Error #[error(transparent)] - CDKNUT02(#[from] cdk::nuts::nut02::Error), + CDKNUT02(#[from] cdk_common::nuts::nut02::Error), /// NUT04 Error #[error(transparent)] - CDKNUT04(#[from] cdk::nuts::nut04::Error), + CDKNUT04(#[from] cdk_common::nuts::nut04::Error), /// NUT05 Error #[error(transparent)] - CDKNUT05(#[from] cdk::nuts::nut05::Error), + CDKNUT05(#[from] cdk_common::nuts::nut05::Error), /// NUT07 Error #[error(transparent)] - CDKNUT07(#[from] cdk::nuts::nut07::Error), + CDKNUT07(#[from] cdk_common::nuts::nut07::Error), /// Secret Error #[error(transparent)] - CDKSECRET(#[from] cdk::secret::Error), + CDKSECRET(#[from] cdk_common::secret::Error), /// Mint Url #[error(transparent)] - MintUrl(#[from] cdk::mint_url::Error), + MintUrl(#[from] cdk_common::mint_url::Error), /// BIP32 Error #[error(transparent)] BIP32(#[from] bitcoin::bip32::Error), @@ -46,7 +46,7 @@ pub enum Error { InvalidDbPath, } -impl From for cdk::cdk_database::Error { +impl From for cdk_common::database::Error { fn from(e: Error) -> Self { Self::Database(Box::new(e)) } diff --git a/crates/cdk-sqlite/src/wallet/mod.rs b/crates/cdk-sqlite/src/wallet/mod.rs index e85d506f..fa2bb489 100644 --- a/crates/cdk-sqlite/src/wallet/mod.rs +++ b/crates/cdk-sqlite/src/wallet/mod.rs @@ -5,17 +5,16 @@ use std::path::Path; use std::str::FromStr; use async_trait::async_trait; -use cdk::amount::Amount; -use cdk::cdk_database::{self, WalletDatabase}; -use cdk::mint_url::MintUrl; -use cdk::nuts::{ - CurrencyUnit, Id, KeySetInfo, Keys, MeltQuoteState, MintInfo, MintQuoteState, Proof, PublicKey, - SecretKey, SpendingConditions, State, +use cdk_common::common::ProofInfo; +use cdk_common::database::WalletDatabase; +use cdk_common::mint_url::MintUrl; +use cdk_common::nuts::{MeltQuoteState, MintQuoteState}; +use cdk_common::secret::Secret; +use cdk_common::wallet::{self, MintQuote}; +use cdk_common::{ + database, Amount, CurrencyUnit, Id, KeySetInfo, Keys, MintInfo, Proof, PublicKey, SecretKey, + SpendingConditions, State, }; -use cdk::secret::Secret; -use cdk::types::ProofInfo; -use cdk::wallet; -use cdk::wallet::MintQuote; use error::Error; use sqlx::sqlite::{SqliteConnectOptions, SqlitePool, SqliteRow}; use sqlx::{ConnectOptions, Row}; @@ -54,7 +53,7 @@ impl WalletSqliteDatabase { .expect("Could not run migrations"); } - async fn set_proof_state(&self, y: PublicKey, state: State) -> Result<(), cdk_database::Error> { + async fn set_proof_state(&self, y: PublicKey, state: State) -> Result<(), database::Error> { sqlx::query( r#" UPDATE proof @@ -74,7 +73,7 @@ impl WalletSqliteDatabase { #[async_trait] impl WalletDatabase for WalletSqliteDatabase { - type Err = cdk_database::Error; + type Err = database::Error; #[instrument(skip(self, mint_info))] async fn add_mint( diff --git a/crates/cdk/Cargo.toml b/crates/cdk/Cargo.toml index 9b88b8f8..35eff847 100644 --- a/crates/cdk/Cargo.toml +++ b/crates/cdk/Cargo.toml @@ -12,15 +12,17 @@ license = "MIT" [features] default = ["mint", "wallet"] -mint = ["dep:futures"] +mint = ["dep:futures", "cdk-common/mint"] # We do not commit to a MSRV with swagger enabled -swagger = ["mint", "dep:utoipa"] -wallet = ["dep:reqwest"] +swagger = ["mint", "dep:utoipa", "cdk-common/swagger"] +wallet = ["dep:reqwest", "cdk-common/wallet"] bench = [] http_subscription = [] [dependencies] +cdk-common = { path = "../cdk-common", version = "0.6.0" } +cbor-diag = "0.1.12" arc-swap = "1.7.1" async-trait = "0.1" anyhow = { version = "1.0.43", features = ["backtrace"] } @@ -31,16 +33,17 @@ bitcoin = { version = "0.32.2", features = [ "rand-std", ] } ciborium = { version = "0.2.2", default-features = false, features = ["std"] } -cbor-diag = "0.1.12" lightning-invoice = { version = "0.32.0", features = ["serde", "std"] } -once_cell = "1.19" regex = "1" reqwest = { version = "0.12", default-features = false, features = [ "json", "rustls-tls", "rustls-tls-native-roots", "socks", - "zstd", "brotli", "gzip", "deflate" + "zstd", + "brotli", + "gzip", + "deflate", ], optional = true } serde = { version = "1", default-features = false, features = ["derive"] } serde_json = "1" @@ -77,7 +80,6 @@ tokio-tungstenite = { version = "0.19.0", features = [ [target.'cfg(target_arch = "wasm32")'.dependencies] tokio = { version = "1.21", features = ["rt", "macros", "sync", "time"] } getrandom = { version = "0.2", features = ["js"] } -instant = { version = "0.1", features = ["wasm-bindgen", "inaccurate"] } [[example]] name = "mint-token" diff --git a/crates/cdk/src/cdk_database/mint_memory.rs b/crates/cdk/src/cdk_database/mint_memory.rs index 41bbd0bf..86ec3577 100644 --- a/crates/cdk/src/cdk_database/mint_memory.rs +++ b/crates/cdk/src/cdk_database/mint_memory.rs @@ -4,13 +4,14 @@ use std::collections::HashMap; use std::sync::Arc; use async_trait::async_trait; +use cdk_common::database::{Error, MintDatabase}; +use cdk_common::mint::MintKeySetInfo; +use cdk_common::nut00::ProofsMethods; use tokio::sync::{Mutex, RwLock}; use uuid::Uuid; -use super::{Error, MintDatabase}; use crate::dhke::hash_to_curve; -use crate::mint::{self, MintKeySetInfo, MintQuote}; -use crate::nuts::nut00::ProofsMethods; +use crate::mint::{self, MintQuote}; use crate::nuts::nut07::State; use crate::nuts::{ nut07, BlindSignature, CurrencyUnit, Id, MeltBolt11Request, MeltQuoteState, MintQuoteState, diff --git a/crates/cdk/src/cdk_database/mod.rs b/crates/cdk/src/cdk_database/mod.rs index a590a67c..e2ec6458 100644 --- a/crates/cdk/src/cdk_database/mod.rs +++ b/crates/cdk/src/cdk_database/mod.rs @@ -1,290 +1,11 @@ //! CDK Database -#[cfg(any(feature = "wallet", feature = "mint"))] -use std::collections::HashMap; -use std::fmt::Debug; - -#[cfg(any(feature = "wallet", feature = "mint"))] -use async_trait::async_trait; -use thiserror::Error; -#[cfg(feature = "mint")] -use uuid::Uuid; - -#[cfg(feature = "mint")] -use crate::mint; -#[cfg(feature = "mint")] -use crate::mint::MintKeySetInfo; -#[cfg(feature = "mint")] -use crate::mint::MintQuote as MintMintQuote; -#[cfg(feature = "wallet")] -use crate::mint_url::MintUrl; -#[cfg(feature = "mint")] -use crate::nuts::MeltBolt11Request; -#[cfg(feature = "mint")] -use crate::nuts::{BlindSignature, MeltQuoteState, MintQuoteState, Proof, Proofs}; -#[cfg(any(feature = "wallet", feature = "mint"))] -use crate::nuts::{CurrencyUnit, Id, PublicKey, State}; -#[cfg(feature = "wallet")] -use crate::nuts::{KeySetInfo, Keys, MintInfo, SpendingConditions}; -#[cfg(feature = "mint")] -use crate::types::LnKey; -#[cfg(feature = "wallet")] -use crate::types::ProofInfo; -#[cfg(feature = "wallet")] -use crate::wallet; -#[cfg(feature = "wallet")] -use crate::wallet::MintQuote as WalletMintQuote; - #[cfg(feature = "mint")] pub mod mint_memory; #[cfg(feature = "wallet")] pub mod wallet_memory; +/// re-export types +pub use cdk_common::database::{Error, MintDatabase, WalletDatabase}; #[cfg(feature = "wallet")] pub use wallet_memory::WalletMemoryDatabase; - -/// CDK_database error -#[derive(Debug, Error)] -pub enum Error { - /// Database Error - #[error(transparent)] - Database(Box), - /// DHKE error - #[error(transparent)] - DHKE(#[from] crate::dhke::Error), - /// NUT00 Error - #[error(transparent)] - NUT00(#[from] crate::nuts::nut00::Error), - /// NUT02 Error - #[error(transparent)] - NUT02(#[from] crate::nuts::nut02::Error), - /// Serde Error - #[error(transparent)] - Serde(#[from] serde_json::Error), - /// Unknown Quote - #[error("Unknown Quote")] - UnknownQuote, -} - -/// Wallet Database trait -#[cfg(feature = "wallet")] -#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] -#[cfg_attr(not(target_arch = "wasm32"), async_trait)] -pub trait WalletDatabase: Debug { - /// Wallet Database Error - type Err: Into + From; - - /// Add Mint to storage - async fn add_mint( - &self, - mint_url: MintUrl, - mint_info: Option, - ) -> Result<(), Self::Err>; - /// Remove Mint from storage - async fn remove_mint(&self, mint_url: MintUrl) -> Result<(), Self::Err>; - /// Get mint from storage - async fn get_mint(&self, mint_url: MintUrl) -> Result, Self::Err>; - /// Get all mints from storage - async fn get_mints(&self) -> Result>, Self::Err>; - /// Update mint url - async fn update_mint_url( - &self, - old_mint_url: MintUrl, - new_mint_url: MintUrl, - ) -> Result<(), Self::Err>; - - /// Add mint keyset to storage - async fn add_mint_keysets( - &self, - mint_url: MintUrl, - keysets: Vec, - ) -> Result<(), Self::Err>; - /// Get mint keysets for mint url - async fn get_mint_keysets( - &self, - mint_url: MintUrl, - ) -> Result>, Self::Err>; - /// Get mint keyset by id - async fn get_keyset_by_id(&self, keyset_id: &Id) -> Result, Self::Err>; - - /// Add mint quote to storage - async fn add_mint_quote(&self, quote: WalletMintQuote) -> Result<(), Self::Err>; - /// Get mint quote from storage - async fn get_mint_quote(&self, quote_id: &str) -> Result, Self::Err>; - /// Get mint quotes from storage - async fn get_mint_quotes(&self) -> Result, Self::Err>; - /// Remove mint quote from storage - async fn remove_mint_quote(&self, quote_id: &str) -> Result<(), Self::Err>; - - /// Add melt quote to storage - async fn add_melt_quote(&self, quote: wallet::MeltQuote) -> Result<(), Self::Err>; - /// Get melt quote from storage - async fn get_melt_quote(&self, quote_id: &str) -> Result, Self::Err>; - /// Remove melt quote from storage - async fn remove_melt_quote(&self, quote_id: &str) -> Result<(), Self::Err>; - - /// Add [`Keys`] to storage - async fn add_keys(&self, keys: Keys) -> Result<(), Self::Err>; - /// Get [`Keys`] from storage - async fn get_keys(&self, id: &Id) -> Result, Self::Err>; - /// Remove [`Keys`] from storage - async fn remove_keys(&self, id: &Id) -> Result<(), Self::Err>; - - /// Update the proofs in storage by adding new proofs or removing proofs by - /// their Y value. - async fn update_proofs( - &self, - added: Vec, - removed_ys: Vec, - ) -> Result<(), Self::Err>; - /// Set proofs as pending in storage. Proofs are identified by their Y - /// value. - async fn set_pending_proofs(&self, ys: Vec) -> Result<(), Self::Err>; - /// Reserve proofs in storage. Proofs are identified by their Y value. - async fn reserve_proofs(&self, ys: Vec) -> Result<(), Self::Err>; - /// Set proofs as unspent in storage. Proofs are identified by their Y - /// value. - async fn set_unspent_proofs(&self, ys: Vec) -> Result<(), Self::Err>; - /// Get proofs from storage - async fn get_proofs( - &self, - mint_url: Option, - unit: Option, - state: Option>, - spending_conditions: Option>, - ) -> Result, Self::Err>; - - /// Increment Keyset counter - async fn increment_keyset_counter(&self, keyset_id: &Id, count: u32) -> Result<(), Self::Err>; - /// Get current Keyset counter - async fn get_keyset_counter(&self, keyset_id: &Id) -> Result, Self::Err>; - - /// Get when nostr key was last checked - async fn get_nostr_last_checked( - &self, - verifying_key: &PublicKey, - ) -> Result, Self::Err>; - /// Update last checked time - async fn add_nostr_last_checked( - &self, - verifying_key: PublicKey, - last_checked: u32, - ) -> Result<(), Self::Err>; -} - -/// Mint Database trait -#[cfg(feature = "mint")] -#[async_trait] -pub trait MintDatabase { - /// Mint Database Error - type Err: Into + From; - - /// Add Active Keyset - async fn set_active_keyset(&self, unit: CurrencyUnit, id: Id) -> Result<(), Self::Err>; - /// Get Active Keyset - async fn get_active_keyset_id(&self, unit: &CurrencyUnit) -> Result, Self::Err>; - /// Get all Active Keyset - async fn get_active_keysets(&self) -> Result, Self::Err>; - - /// Add [`MintMintQuote`] - async fn add_mint_quote(&self, quote: MintMintQuote) -> Result<(), Self::Err>; - /// Get [`MintMintQuote`] - async fn get_mint_quote(&self, quote_id: &Uuid) -> Result, Self::Err>; - /// Update state of [`MintMintQuote`] - async fn update_mint_quote_state( - &self, - quote_id: &Uuid, - state: MintQuoteState, - ) -> Result; - /// Get all [`MintMintQuote`]s - async fn get_mint_quote_by_request( - &self, - request: &str, - ) -> Result, Self::Err>; - /// Get all [`MintMintQuote`]s - async fn get_mint_quote_by_request_lookup_id( - &self, - request_lookup_id: &str, - ) -> Result, Self::Err>; - /// Get Mint Quotes - async fn get_mint_quotes(&self) -> Result, Self::Err>; - /// Remove [`MintMintQuote`] - async fn remove_mint_quote(&self, quote_id: &Uuid) -> Result<(), Self::Err>; - - /// Add [`mint::MeltQuote`] - async fn add_melt_quote(&self, quote: mint::MeltQuote) -> Result<(), Self::Err>; - /// Get [`mint::MeltQuote`] - async fn get_melt_quote(&self, quote_id: &Uuid) -> Result, Self::Err>; - /// Update [`mint::MeltQuote`] state - async fn update_melt_quote_state( - &self, - quote_id: &Uuid, - state: MeltQuoteState, - ) -> Result; - /// Get all [`mint::MeltQuote`]s - async fn get_melt_quotes(&self) -> Result, Self::Err>; - /// Remove [`mint::MeltQuote`] - async fn remove_melt_quote(&self, quote_id: &Uuid) -> Result<(), Self::Err>; - - /// Add melt request - async fn add_melt_request( - &self, - melt_request: MeltBolt11Request, - ln_key: LnKey, - ) -> Result<(), Self::Err>; - /// Get melt request - async fn get_melt_request( - &self, - quote_id: &Uuid, - ) -> Result, LnKey)>, Self::Err>; - - /// Add [`MintKeySetInfo`] - async fn add_keyset_info(&self, keyset: MintKeySetInfo) -> Result<(), Self::Err>; - /// Get [`MintKeySetInfo`] - async fn get_keyset_info(&self, id: &Id) -> Result, Self::Err>; - /// Get [`MintKeySetInfo`]s - async fn get_keyset_infos(&self) -> Result, Self::Err>; - - /// Add spent [`Proofs`] - async fn add_proofs(&self, proof: Proofs, quote_id: Option) -> Result<(), Self::Err>; - /// Get [`Proofs`] by ys - async fn get_proofs_by_ys(&self, ys: &[PublicKey]) -> Result>, Self::Err>; - /// Get ys by quote id - async fn get_proof_ys_by_quote_id(&self, quote_id: &Uuid) -> Result, Self::Err>; - /// Get [`Proofs`] state - async fn get_proofs_states(&self, ys: &[PublicKey]) -> Result>, Self::Err>; - /// Get [`Proofs`] state - async fn update_proofs_states( - &self, - ys: &[PublicKey], - proofs_state: State, - ) -> Result>, Self::Err>; - /// Get [`Proofs`] by state - async fn get_proofs_by_keyset_id( - &self, - keyset_id: &Id, - ) -> Result<(Proofs, Vec>), Self::Err>; - - /// Add [`BlindSignature`] - async fn add_blind_signatures( - &self, - blinded_messages: &[PublicKey], - blind_signatures: &[BlindSignature], - quote_id: Option, - ) -> Result<(), Self::Err>; - /// Get [`BlindSignature`]s - async fn get_blind_signatures( - &self, - blinded_messages: &[PublicKey], - ) -> Result>, Self::Err>; - /// Get [`BlindSignature`]s for keyset_id - async fn get_blind_signatures_for_keyset( - &self, - keyset_id: &Id, - ) -> Result, Self::Err>; - /// Get [`BlindSignature`]s for quote - async fn get_blind_signatures_for_quote( - &self, - quote_id: &Uuid, - ) -> Result, Self::Err>; -} diff --git a/crates/cdk/src/cdk_database/wallet_memory.rs b/crates/cdk/src/cdk_database/wallet_memory.rs index f517d4a0..5c2785db 100644 --- a/crates/cdk/src/cdk_database/wallet_memory.rs +++ b/crates/cdk/src/cdk_database/wallet_memory.rs @@ -4,10 +4,9 @@ use std::collections::{HashMap, HashSet}; use std::sync::Arc; use async_trait::async_trait; +use cdk_common::database::{Error, WalletDatabase}; use tokio::sync::RwLock; -use super::WalletDatabase; -use crate::cdk_database::Error; use crate::mint_url::MintUrl; use crate::nuts::{ CurrencyUnit, Id, KeySetInfo, Keys, MintInfo, PublicKey, SpendingConditions, State, diff --git a/crates/cdk/src/fees.rs b/crates/cdk/src/fees.rs index 238ba51b..42abb8a3 100644 --- a/crates/cdk/src/fees.rs +++ b/crates/cdk/src/fees.rs @@ -6,9 +6,8 @@ use std::collections::HashMap; use tracing::instrument; -use crate::error::Error; use crate::nuts::Id; -use crate::Amount; +use crate::{Amount, Error}; /// Fee required for proof set #[instrument(skip_all)] diff --git a/crates/cdk/src/lib.rs b/crates/cdk/src/lib.rs index 80be76bd..9fffadb3 100644 --- a/crates/cdk/src/lib.rs +++ b/crates/cdk/src/lib.rs @@ -3,33 +3,29 @@ #![warn(missing_docs)] #![warn(rustdoc::bare_urls)] -pub mod amount; #[cfg(any(feature = "wallet", feature = "mint"))] pub mod cdk_database; -#[cfg(feature = "mint")] -pub mod cdk_lightning; -pub mod dhke; -pub mod error; + #[cfg(feature = "mint")] pub mod mint; -pub mod mint_url; -pub mod nuts; -pub mod secret; -pub mod types; -pub mod util; #[cfg(feature = "wallet")] pub mod wallet; pub mod pub_sub; +/// Re-export amount type +#[doc(hidden)] +pub use cdk_common::{ + amount, common as types, dhke, + error::{self, Error}, + lightning as cdk_lightning, lightning_invoice, mint_url, nuts, secret, subscription, util, ws, + Amount, Bolt11Invoice, +}; + pub mod fees; #[doc(hidden)] pub use bitcoin::secp256k1; -#[doc(hidden)] -pub use error::Error; -#[doc(hidden)] -pub use lightning_invoice::{self, Bolt11Invoice}; #[cfg(feature = "mint")] #[doc(hidden)] pub use mint::Mint; @@ -37,8 +33,6 @@ pub use mint::Mint; #[doc(hidden)] pub use wallet::{Wallet, WalletSubscription}; -#[doc(hidden)] -pub use self::amount::Amount; #[doc(hidden)] pub use self::util::SECP256K1; #[cfg(feature = "wallet")] diff --git a/crates/cdk/src/mint/builder.rs b/crates/cdk/src/mint/builder.rs index b61ae8e0..d40e2b12 100644 --- a/crates/cdk/src/mint/builder.rs +++ b/crates/cdk/src/mint/builder.rs @@ -4,12 +4,12 @@ use std::collections::HashMap; use std::sync::Arc; use anyhow::anyhow; +use cdk_common::database::{self, MintDatabase}; use super::nut17::SupportedMethods; use super::nut19::{self, CachedEndpoint}; use super::Nuts; use crate::amount::Amount; -use crate::cdk_database::{self, MintDatabase}; use crate::cdk_lightning::{self, MintLightning}; use crate::mint::Mint; use crate::nuts::{ @@ -26,7 +26,7 @@ pub struct MintBuilder { /// Mint Info mint_info: MintInfo, /// Mint Storage backend - localstore: Option + Send + Sync>>, + localstore: Option + Send + Sync>>, /// Ln backends for mint ln: Option + Send + Sync>>>, seed: Option>, @@ -57,7 +57,7 @@ impl MintBuilder { /// Set localstore pub fn with_localstore( mut self, - localstore: Arc + Send + Sync>, + localstore: Arc + Send + Sync>, ) -> MintBuilder { self.localstore = Some(localstore); self @@ -149,28 +149,26 @@ impl MintBuilder { self.mint_info.nuts.nut15 = mpp; } - match method { - PaymentMethod::Bolt11 => { - let mint_method_settings = MintMethodSettings { - method, - unit: unit.clone(), - min_amount: Some(limits.mint_min), - max_amount: Some(limits.mint_max), - description: settings.invoice_description, - }; + if method == PaymentMethod::Bolt11 { + let mint_method_settings = MintMethodSettings { + method, + unit: unit.clone(), + min_amount: Some(limits.mint_min), + max_amount: Some(limits.mint_max), + description: settings.invoice_description, + }; - self.mint_info.nuts.nut04.methods.push(mint_method_settings); - self.mint_info.nuts.nut04.disabled = false; + self.mint_info.nuts.nut04.methods.push(mint_method_settings); + self.mint_info.nuts.nut04.disabled = false; - let melt_method_settings = MeltMethodSettings { - method, - unit, - min_amount: Some(limits.melt_min), - max_amount: Some(limits.melt_max), - }; - self.mint_info.nuts.nut05.methods.push(melt_method_settings); - self.mint_info.nuts.nut05.disabled = false; - } + let melt_method_settings = MeltMethodSettings { + method, + unit, + min_amount: Some(limits.melt_min), + max_amount: Some(limits.melt_max), + }; + self.mint_info.nuts.nut05.methods.push(melt_method_settings); + self.mint_info.nuts.nut05.disabled = false; } ln.insert(ln_key.clone(), ln_backend); diff --git a/crates/cdk/src/mint/melt.rs b/crates/cdk/src/mint/melt.rs index cacda785..f2679b52 100644 --- a/crates/cdk/src/mint/melt.rs +++ b/crates/cdk/src/mint/melt.rs @@ -2,6 +2,7 @@ use std::collections::HashSet; use std::str::FromStr; use anyhow::bail; +use cdk_common::nut00::ProofsMethods; use lightning_invoice::Bolt11Invoice; use tracing::instrument; use uuid::Uuid; @@ -13,7 +14,6 @@ use super::{ use crate::amount::to_unit; use crate::cdk_lightning::{MintLightning, PayInvoiceResponse}; use crate::mint::SigFlag; -use crate::nuts::nut00::ProofsMethods; use crate::nuts::nut11::{enforce_sig_flag, EnforceSigFlag}; use crate::nuts::{Id, MeltQuoteState}; use crate::types::LnKey; diff --git a/crates/cdk/src/mint/mod.rs b/crates/cdk/src/mint/mod.rs index 102eec00..0d7a1647 100644 --- a/crates/cdk/src/mint/mod.rs +++ b/crates/cdk/src/mint/mod.rs @@ -6,22 +6,24 @@ use std::sync::Arc; use bitcoin::bip32::{ChildNumber, DerivationPath, Xpriv}; use bitcoin::secp256k1::{self, Secp256k1}; +use cdk_common::common::{LnKey, QuoteTTL}; +use cdk_common::database::{self, MintDatabase}; +use cdk_common::mint::MintKeySetInfo; use config::SwappableConfig; use futures::StreamExt; use serde::{Deserialize, Serialize}; +use subscription::PubSubManager; use tokio::sync::Notify; use tokio::task::JoinSet; use tracing::instrument; use uuid::Uuid; -use crate::cdk_database::{self, MintDatabase}; use crate::cdk_lightning::{self, MintLightning}; use crate::dhke::{sign_message, verify_message}; use crate::error::Error; use crate::fees::calculate_fee; use crate::mint_url::MintUrl; use crate::nuts::*; -use crate::types::{LnKey, QuoteTTL}; use crate::util::unix_time; use crate::Amount; @@ -33,11 +35,11 @@ mod keysets; mod melt; mod mint_nut04; mod start_up_check; +pub mod subscription; mod swap; -pub mod types; pub use builder::{MintBuilder, MintMeltLimits}; -pub use types::{MeltQuote, MintQuote}; +pub use cdk_common::mint::{MeltQuote, MintQuote}; /// Cashu Mint #[derive(Clone)] @@ -45,7 +47,7 @@ pub struct Mint { /// Mint Config pub config: SwappableConfig, /// Mint Storage backend - pub localstore: Arc + Send + Sync>, + pub localstore: Arc + Send + Sync>, /// Ln backends for mint pub ln: HashMap + Send + Sync>>, /// Subscription manager @@ -62,7 +64,7 @@ impl Mint { seed: &[u8], mint_info: MintInfo, quote_ttl: QuoteTTL, - localstore: Arc + Send + Sync>, + localstore: Arc + Send + Sync>, ln: HashMap + Send + Sync>>, // Hashmap where the key is the unit and value is (input fee ppk, max_order) supported_units: HashMap, @@ -516,47 +518,6 @@ pub struct FeeReserve { pub percent_fee_reserve: f32, } -/// Mint Keyset Info -#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)] -pub struct MintKeySetInfo { - /// Keyset [`Id`] - pub id: Id, - /// Keyset [`CurrencyUnit`] - pub unit: CurrencyUnit, - /// Keyset active or inactive - /// Mint will only issue new [`BlindSignature`] on active keysets - pub active: bool, - /// Starting unix time Keyset is valid from - pub valid_from: u64, - /// When the Keyset is valid to - /// This is not shown to the wallet and can only be used internally - pub valid_to: Option, - /// [`DerivationPath`] keyset - pub derivation_path: DerivationPath, - /// DerivationPath index of Keyset - pub derivation_path_index: Option, - /// Max order of keyset - pub max_order: u8, - /// Input Fee ppk - #[serde(default = "default_fee")] - pub input_fee_ppk: u64, -} - -fn default_fee() -> u64 { - 0 -} - -impl From for KeySetInfo { - fn from(keyset_info: MintKeySetInfo) -> Self { - Self { - id: keyset_info.id, - unit: keyset_info.unit, - active: keyset_info.active, - input_fee_ppk: keyset_info.input_fee_ppk, - } - } -} - /// Generate new [`MintKeySetInfo`] from path #[instrument(skip_all)] fn create_new_keyset( @@ -605,11 +566,11 @@ mod tests { use std::collections::HashSet; use bitcoin::Network; + use cdk_common::common::{LnKey, QuoteTTL}; use secp256k1::Secp256k1; use uuid::Uuid; use super::*; - use crate::types::LnKey; #[test] fn mint_mod_generate_keyset_from_seed() { @@ -697,7 +658,7 @@ mod tests { assert_eq!(amounts_and_pubkeys, expected_amounts_and_pubkeys); } - use cdk_database::mint_memory::MintMemoryDatabase; + use crate::cdk_database::mint_memory::MintMemoryDatabase; #[derive(Default)] struct MintConfig<'a> { diff --git a/crates/cdk/src/nuts/nut17/manager.rs b/crates/cdk/src/mint/subscription/manager.rs similarity index 81% rename from crates/cdk/src/nuts/nut17/manager.rs rename to crates/cdk/src/mint/subscription/manager.rs index 21103d6e..4df11330 100644 --- a/crates/cdk/src/nuts/nut17/manager.rs +++ b/crates/cdk/src/mint/subscription/manager.rs @@ -2,10 +2,12 @@ use std::ops::Deref; use std::sync::Arc; +use cdk_common::database::{self, MintDatabase}; +use cdk_common::nut17::Notification; +use cdk_common::NotificationPayload; use uuid::Uuid; -use super::{Notification, NotificationPayload, OnSubscription}; -use crate::cdk_database::{self, MintDatabase}; +use super::OnSubscription; use crate::nuts::{ BlindSignature, MeltQuoteBolt11Response, MeltQuoteState, MintQuoteBolt11Response, MintQuoteState, ProofState, @@ -26,8 +28,8 @@ impl Default for PubSubManager { } } -impl From + Send + Sync>> for PubSubManager { - fn from(val: Arc + Send + Sync>) -> Self { +impl From + Send + Sync>> for PubSubManager { + fn from(val: Arc + Send + Sync>) -> Self { PubSubManager(OnSubscription(Some(val)).into()) } } @@ -82,19 +84,21 @@ mod test { use tokio::time::sleep; use super::*; - use crate::nuts::nut17::{Kind, Params}; + use crate::nuts::nut17::Kind; use crate::nuts::{PublicKey, State}; + use crate::subscription::{IndexableParams, Params}; #[tokio::test] async fn active_and_drop() { let manager = PubSubManager::default(); - let params = Params { + let params: IndexableParams = Params { kind: Kind::ProofState, filters: vec![ "02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2".to_owned(), ], id: "uno".into(), - }; + } + .into(); // Although the same param is used, two subscriptions are created, that // is because each index is unique, thanks to `Unique`, it is the @@ -123,25 +127,31 @@ mod test { let manager = PubSubManager::default(); let mut subscriptions = [ manager - .try_subscribe(Params { - kind: Kind::ProofState, - filters: vec![ - "02194603ffa36356f4a56b7df9371fc3192472351453ec7398b8da8117e7c3e104" - .to_string(), - ], - id: "uno".into(), - }) + .try_subscribe::( + Params { + kind: Kind::ProofState, + filters: vec![ + "02194603ffa36356f4a56b7df9371fc3192472351453ec7398b8da8117e7c3e104" + .to_string(), + ], + id: "uno".into(), + } + .into(), + ) .await .expect("valid subscription"), manager - .try_subscribe(Params { - kind: Kind::ProofState, - filters: vec![ - "02194603ffa36356f4a56b7df9371fc3192472351453ec7398b8da8117e7c3e104" - .to_string(), - ], - id: "dos".into(), - }) + .try_subscribe::( + Params { + kind: Kind::ProofState, + filters: vec![ + "02194603ffa36356f4a56b7df9371fc3192472351453ec7398b8da8117e7c3e104" + .to_string(), + ], + id: "dos".into(), + } + .into(), + ) .await .expect("valid subscription"), ]; @@ -182,7 +192,7 @@ mod test { async fn json_test() { let manager = PubSubManager::default(); let mut subscription = manager - .try_subscribe::( + .try_subscribe::( serde_json::from_str(r#"{"kind":"proof_state","filters":["02194603ffa36356f4a56b7df9371fc3192472351453ec7398b8da8117e7c3e104"],"subId":"uno"}"#) .expect("valid json"), ) diff --git a/crates/cdk/src/mint/subscription/mod.rs b/crates/cdk/src/mint/subscription/mod.rs new file mode 100644 index 00000000..20216fab --- /dev/null +++ b/crates/cdk/src/mint/subscription/mod.rs @@ -0,0 +1,12 @@ +//! Specific Subscription for the cdk crate + +#[cfg(feature = "mint")] +mod manager; +#[cfg(feature = "mint")] +mod on_subscription; +#[cfg(feature = "mint")] +pub use manager::PubSubManager; +#[cfg(feature = "mint")] +pub use on_subscription::OnSubscription; + +pub use crate::pub_sub::SubId; diff --git a/crates/cdk/src/nuts/nut17/on_subscription.rs b/crates/cdk/src/mint/subscription/on_subscription.rs similarity index 92% rename from crates/cdk/src/nuts/nut17/on_subscription.rs rename to crates/cdk/src/mint/subscription/on_subscription.rs index 37d74587..08d1f169 100644 --- a/crates/cdk/src/nuts/nut17/on_subscription.rs +++ b/crates/cdk/src/mint/subscription/on_subscription.rs @@ -3,12 +3,13 @@ //! This module contains the code that is triggered when a new subscription is created. use std::sync::Arc; +use cdk_common::database::{self, MintDatabase}; +use cdk_common::nut17::Notification; +use cdk_common::pub_sub::OnNewSubscription; +use cdk_common::NotificationPayload; use uuid::Uuid; -use super::{Notification, NotificationPayload}; -use crate::cdk_database::{self, MintDatabase}; use crate::nuts::{MeltQuoteBolt11Response, MintQuoteBolt11Response, ProofState, PublicKey}; -use crate::pub_sub::OnNewSubscription; #[derive(Default)] /// Subscription Init @@ -17,7 +18,7 @@ use crate::pub_sub::OnNewSubscription; /// /// It is used to send the initial state of the subscription to the client. pub struct OnSubscription( - pub(crate) Option + Send + Sync>>, + pub(crate) Option + Send + Sync>>, ); #[async_trait::async_trait] diff --git a/crates/cdk/src/pub_sub/mod.rs b/crates/cdk/src/pub_sub.rs similarity index 88% rename from crates/cdk/src/pub_sub/mod.rs rename to crates/cdk/src/pub_sub.rs index cff76970..532a14cc 100644 --- a/crates/cdk/src/pub_sub/mod.rs +++ b/crates/cdk/src/pub_sub.rs @@ -12,18 +12,15 @@ use std::cmp::Ordering; use std::collections::{BTreeMap, HashSet}; use std::fmt::Debug; use std::ops::{Deref, DerefMut}; -use std::str::FromStr; use std::sync::atomic::{self, AtomicUsize}; use std::sync::Arc; -use serde::{Deserialize, Serialize}; +pub use cdk_common::pub_sub::index::{Index, Indexable, SubscriptionGlobalId}; +use cdk_common::pub_sub::OnNewSubscription; +pub use cdk_common::pub_sub::SubId; use tokio::sync::{mpsc, RwLock}; use tokio::task::JoinHandle; -mod index; - -pub use index::{Index, Indexable, SubscriptionGlobalId}; - type IndexTree = Arc, mpsc::Sender<(SubId, T)>>>>; /// Default size of the remove channel @@ -32,25 +29,6 @@ pub const DEFAULT_REMOVE_SIZE: usize = 10_000; /// Default channel size for subscription buffering pub const DEFAULT_CHANNEL_SIZE: usize = 10; -#[async_trait::async_trait] -/// On New Subscription trait -/// -/// This trait is optional and it is used to notify the application when a new -/// subscription is created. This is useful when the application needs to send -/// the initial state to the subscriber upon subscription -pub trait OnNewSubscription { - /// Index type - type Index; - /// Subscription event type - type Event; - - /// Called when a new subscription is created - async fn on_new_subscription( - &self, - request: &[&Self::Index], - ) -> Result, String>; -} - /// Subscription manager /// /// This object keep track of all subscription listener and it is also @@ -320,41 +298,6 @@ where } } -/// Subscription Id wrapper -/// -/// This is the place to add some sane default (like a max length) to the -/// subscription ID -#[derive(Debug, Clone, Default, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] -pub struct SubId(String); - -impl From<&str> for SubId { - fn from(s: &str) -> Self { - Self(s.to_string()) - } -} - -impl From for SubId { - fn from(s: String) -> Self { - Self(s) - } -} - -impl FromStr for SubId { - type Err = (); - - fn from_str(s: &str) -> Result { - Ok(Self(s.to_string())) - } -} - -impl Deref for SubId { - type Target = String; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - #[cfg(test)] mod test { use tokio::sync::mpsc; diff --git a/crates/cdk/src/wallet/client.rs b/crates/cdk/src/wallet/client.rs index d92eec0b..6059a45e 100644 --- a/crates/cdk/src/wallet/client.rs +++ b/crates/cdk/src/wallet/client.rs @@ -3,7 +3,9 @@ use std::fmt::Debug; use async_trait::async_trait; -use reqwest::Client; +use reqwest::{Client, IntoUrl}; +use serde::de::DeserializeOwned; +use serde::Serialize; use tracing::instrument; #[cfg(not(target_arch = "wasm32"))] use url::Url; @@ -18,18 +20,6 @@ use crate::nuts::{ RestoreResponse, SwapRequest, SwapResponse, }; -macro_rules! convert_http_response { - ($type:ty, $data:ident) => { - serde_json::from_str::<$type>(&$data).map_err(|err| { - tracing::warn!("Http Response error: {}", err); - match ErrorResponse::from_json(&$data) { - Ok(ok) => >::into(ok), - Err(err) => err.into(), - } - }) - }; -} - /// Http Client #[derive(Debug, Clone)] pub struct HttpClient { @@ -46,6 +36,53 @@ impl HttpClient { } } + #[inline] + async fn http_get(&self, url: U) -> Result { + let response = self + .inner + .get(url) + .send() + .await + .map_err(|e| Error::HttpError(e.to_string()))? + .text() + .await + .map_err(|e| Error::HttpError(e.to_string()))?; + + serde_json::from_str::(&response).map_err(|err| { + tracing::warn!("Http Response error: {}", err); + match ErrorResponse::from_json(&response) { + Ok(ok) => >::into(ok), + Err(err) => err.into(), + } + }) + } + + #[inline] + async fn http_post( + &self, + url: U, + payload: &P, + ) -> Result { + let response = self + .inner + .post(url) + .json(&payload) + .send() + .await + .map_err(|e| Error::HttpError(e.to_string()))? + .text() + .await + .map_err(|e| Error::HttpError(e.to_string()))?; + + serde_json::from_str::(&response).map_err(|err| { + tracing::warn!("Http Response error: {}", err); + match ErrorResponse::from_json(&response) { + Ok(ok) => >::into(ok), + Err(err) => err.into(), + } + }) + } + #[cfg(not(target_arch = "wasm32"))] /// Create new [`HttpClient`] with a proxy for specific TLDs. /// Specifying `None` for `host_matcher` will use the proxy for all @@ -72,7 +109,8 @@ impl HttpClient { None })) .danger_accept_invalid_certs(accept_invalid_certs) // Allow self-signed certs - .build()?; + .build() + .map_err(|e| Error::HttpError(e.to_string()))?; Ok(Self { inner: client, @@ -88,9 +126,7 @@ impl MintConnector for HttpClient { #[instrument(skip(self), fields(mint_url = %self.mint_url))] async fn get_mint_keys(&self) -> Result, Error> { let url = self.mint_url.join_paths(&["v1", "keys"])?; - let keys = self.inner.get(url).send().await?.text().await?; - - Ok(convert_http_response!(KeysResponse, keys)?.keysets) + Ok(self.http_get::<_, KeysResponse>(url).await?.keysets) } /// Get Keyset Keys [NUT-01] @@ -99,9 +135,8 @@ impl MintConnector for HttpClient { let url = self .mint_url .join_paths(&["v1", "keys", &keyset_id.to_string()])?; - let keys = self.inner.get(url).send().await?.text().await?; - - convert_http_response!(KeysResponse, keys)? + self.http_get::<_, KeysResponse>(url) + .await? .keysets .drain(0..1) .next() @@ -112,9 +147,7 @@ impl MintConnector for HttpClient { #[instrument(skip(self), fields(mint_url = %self.mint_url))] async fn get_mint_keysets(&self) -> Result { let url = self.mint_url.join_paths(&["v1", "keysets"])?; - let res = self.inner.get(url).send().await?.text().await?; - - convert_http_response!(KeysetResponse, res) + self.http_get(url).await } /// Mint Quote [NUT-04] @@ -126,17 +159,7 @@ impl MintConnector for HttpClient { let url = self .mint_url .join_paths(&["v1", "mint", "quote", "bolt11"])?; - - let res = self - .inner - .post(url) - .json(&request) - .send() - .await? - .text() - .await?; - - convert_http_response!(MintQuoteBolt11Response, res) + self.http_post(url, &request).await } /// Mint Quote status @@ -149,9 +172,7 @@ impl MintConnector for HttpClient { .mint_url .join_paths(&["v1", "mint", "quote", "bolt11", quote_id])?; - let res = self.inner.get(url).send().await?.text().await?; - - convert_http_response!(MintQuoteBolt11Response, res) + self.http_get(url).await } /// Mint Tokens [NUT-04] @@ -161,17 +182,7 @@ impl MintConnector for HttpClient { request: MintBolt11Request, ) -> Result { let url = self.mint_url.join_paths(&["v1", "mint", "bolt11"])?; - - let res = self - .inner - .post(url) - .json(&request) - .send() - .await? - .text() - .await?; - - convert_http_response!(MintBolt11Response, res) + self.http_post(url, &request).await } /// Melt Quote [NUT-05] @@ -183,17 +194,7 @@ impl MintConnector for HttpClient { let url = self .mint_url .join_paths(&["v1", "melt", "quote", "bolt11"])?; - - let res = self - .inner - .post(url) - .json(&request) - .send() - .await? - .text() - .await?; - - convert_http_response!(MeltQuoteBolt11Response, res) + self.http_post(url, &request).await } /// Melt Quote Status @@ -206,9 +207,7 @@ impl MintConnector for HttpClient { .mint_url .join_paths(&["v1", "melt", "quote", "bolt11", quote_id])?; - let res = self.inner.get(url).send().await?.text().await?; - - convert_http_response!(MeltQuoteBolt11Response, res) + self.http_get(url).await } /// Melt [NUT-05] @@ -219,44 +218,21 @@ impl MintConnector for HttpClient { request: MeltBolt11Request, ) -> Result, Error> { let url = self.mint_url.join_paths(&["v1", "melt", "bolt11"])?; - - let res = self - .inner - .post(url) - .json(&request) - .send() - .await? - .text() - .await?; - - convert_http_response!(MeltQuoteBolt11Response, res) + self.http_post(url, &request).await } /// Swap Token [NUT-03] #[instrument(skip(self, swap_request), fields(mint_url = %self.mint_url))] async fn post_swap(&self, swap_request: SwapRequest) -> Result { let url = self.mint_url.join_paths(&["v1", "swap"])?; - - let res = self - .inner - .post(url) - .json(&swap_request) - .send() - .await? - .text() - .await?; - - convert_http_response!(SwapResponse, res) + self.http_post(url, &swap_request).await } /// Get Mint Info [NUT-06] #[instrument(skip(self), fields(mint_url = %self.mint_url))] async fn get_mint_info(&self) -> Result { let url = self.mint_url.join_paths(&["v1", "info"])?; - - let res = self.inner.get(url).send().await?.text().await?; - - convert_http_response!(MintInfo, res) + self.http_get(url).await } /// Spendable check [NUT-07] @@ -266,34 +242,14 @@ impl MintConnector for HttpClient { request: CheckStateRequest, ) -> Result { let url = self.mint_url.join_paths(&["v1", "checkstate"])?; - - let res = self - .inner - .post(url) - .json(&request) - .send() - .await? - .text() - .await?; - - convert_http_response!(CheckStateResponse, res) + self.http_post(url, &request).await } /// Restore request [NUT-13] #[instrument(skip(self, request), fields(mint_url = %self.mint_url))] async fn post_restore(&self, request: RestoreRequest) -> Result { let url = self.mint_url.join_paths(&["v1", "restore"])?; - - let res = self - .inner - .post(url) - .json(&request) - .send() - .await? - .text() - .await?; - - convert_http_response!(RestoreResponse, res) + self.http_post(url, &request).await } } diff --git a/crates/cdk/src/wallet/mod.rs b/crates/cdk/src/wallet/mod.rs index 10609481..68dd2599 100644 --- a/crates/cdk/src/wallet/mod.rs +++ b/crates/cdk/src/wallet/mod.rs @@ -6,6 +6,8 @@ use std::sync::Arc; use bitcoin::bip32::Xpriv; use bitcoin::Network; +use cdk_common::database::{self, WalletDatabase}; +use cdk_common::subscription::Params; use client::MintConnector; use getrandom::getrandom; pub use multi_mint_wallet::MultiMintWallet; @@ -14,13 +16,12 @@ use tracing::instrument; pub use types::{MeltQuote, MintQuote, SendKind}; use crate::amount::SplitTarget; -use crate::cdk_database::{self, WalletDatabase}; use crate::dhke::construct_proofs; use crate::error::Error; use crate::fees::calculate_fee; use crate::mint_url::MintUrl; use crate::nuts::nut00::token::Token; -use crate::nuts::nut17::{Kind, Params}; +use crate::nuts::nut17::Kind; use crate::nuts::{ nut10, CurrencyUnit, Id, Keys, MintInfo, MintQuoteState, PreMintSecrets, Proof, Proofs, RestoreRequest, SpendingConditions, State, @@ -39,9 +40,10 @@ mod receive; mod send; pub mod subscription; mod swap; -pub mod types; pub mod util; +pub use cdk_common::wallet as types; + use crate::nuts::nut00::ProofsMethods; /// CDK Wallet @@ -56,7 +58,7 @@ pub struct Wallet { /// Unit pub unit: CurrencyUnit, /// Storage backend - pub localstore: Arc + Send + Sync>, + pub localstore: Arc + Send + Sync>, /// The targeted amount of proofs to have at each size pub target_proof_count: usize, xpriv: Xpriv, @@ -132,7 +134,7 @@ impl Wallet { pub fn new( mint_url: &str, unit: CurrencyUnit, - localstore: Arc + Send + Sync>, + localstore: Arc + Send + Sync>, seed: &[u8], target_proof_count: Option, ) -> Result { diff --git a/crates/cdk/src/wallet/multi_mint_wallet.rs b/crates/cdk/src/wallet/multi_mint_wallet.rs index 25f4dbd0..59c3f395 100644 --- a/crates/cdk/src/wallet/multi_mint_wallet.rs +++ b/crates/cdk/src/wallet/multi_mint_wallet.rs @@ -4,11 +4,10 @@ //! pairs use std::collections::{BTreeMap, HashMap}; -use std::fmt; use std::str::FromStr; use std::sync::Arc; -use serde::{Deserialize, Serialize}; +use cdk_common::wallet::WalletKey; use tokio::sync::Mutex; use tracing::instrument; @@ -28,26 +27,6 @@ pub struct MultiMintWallet { pub wallets: Arc>>, } -/// Wallet Key -#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] -pub struct WalletKey { - mint_url: MintUrl, - unit: CurrencyUnit, -} - -impl fmt::Display for WalletKey { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "mint_url: {}, unit: {}", self.mint_url, self.unit,) - } -} - -impl WalletKey { - /// Create new [`WalletKey`] - pub fn new(mint_url: MintUrl, unit: CurrencyUnit) -> Self { - Self { mint_url, unit } - } -} - impl MultiMintWallet { /// New Multimint wallet pub fn new(wallets: Vec) -> Self { diff --git a/crates/cdk/src/wallet/subscription/mod.rs b/crates/cdk/src/wallet/subscription/mod.rs index 79ee1b7b..31544dd1 100644 --- a/crates/cdk/src/wallet/subscription/mod.rs +++ b/crates/cdk/src/wallet/subscription/mod.rs @@ -9,12 +9,12 @@ use std::collections::HashMap; use std::fmt::Debug; use std::sync::Arc; +use cdk_common::subscription::Params; use tokio::sync::{mpsc, RwLock}; use tokio::task::JoinHandle; use tracing::error; use crate::mint_url::MintUrl; -use crate::nuts::nut17::Params; use crate::pub_sub::SubId; use crate::wallet::client::MintConnector; diff --git a/crates/cdk/src/wallet/subscription/ws.rs b/crates/cdk/src/wallet/subscription/ws.rs index 22c3a70e..f90cbe06 100644 --- a/crates/cdk/src/wallet/subscription/ws.rs +++ b/crates/cdk/src/wallet/subscription/ws.rs @@ -2,6 +2,8 @@ use std::collections::{HashMap, HashSet}; use std::sync::atomic::AtomicUsize; use std::sync::Arc; +use cdk_common::subscription::Params; +use cdk_common::ws::{WsMessageOrResponse, WsMethodRequest, WsRequest, WsUnsubscribeRequest}; use futures::{SinkExt, StreamExt}; use tokio::sync::{mpsc, RwLock}; use tokio_tungstenite::connect_async; @@ -10,10 +12,6 @@ use tokio_tungstenite::tungstenite::Message; use super::http::http_main; use super::WsSubscriptionBody; use crate::mint_url::MintUrl; -use crate::nuts::nut17::ws::{ - WsMessageOrResponse, WsMethodRequest, WsRequest, WsUnsubscribeRequest, -}; -use crate::nuts::nut17::Params; use crate::pub_sub::SubId; use crate::wallet::client::MintConnector;