Introduce cashu to host the shared types, traits and other common code (#519)

---------

Co-authored-by: thesimplekid <tsk@thesimplekid.com>
This commit is contained in:
C
2025-01-12 09:50:05 -03:00
committed by GitHub
parent 8ac766dd62
commit 8fe0982c6d
93 changed files with 1341 additions and 963 deletions

View File

@@ -49,6 +49,16 @@ jobs:
matrix: matrix:
build-args: 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,
-p cdk --no-default-features, -p cdk --no-default-features,
-p cdk --no-default-features --features wallet, -p cdk --no-default-features --features wallet,
@@ -154,10 +164,9 @@ jobs:
matrix: matrix:
build-args: build-args:
[ [
-p cdk, -p cashu --no-default-features --features "wallet mint",
-p cdk --no-default-features, -p cdk-common --no-default-features --features "wallet mint",
-p cdk --no-default-features --features wallet, -p cdk --no-default-features --features "mint mint",
-p cdk --no-default-features --features mint,
-p cdk-axum, -p cdk-axum,
-p cdk-axum --no-default-features --features redis, -p cdk-axum --no-default-features --features redis,
-p cdk-strike, -p cdk-strike,

1
.gitignore vendored
View File

@@ -1,4 +1,5 @@
**/target **/target
.DS_Store
.direnv .direnv
.vscode/ .vscode/
.idea/ .idea/

224
Cargo.lock generated
View File

@@ -682,6 +682,28 @@ dependencies = [
"serde_json", "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]] [[package]]
name = "cast" name = "cast"
version = "0.3.0" version = "0.3.0"
@@ -718,9 +740,9 @@ dependencies = [
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.2.7" version = "1.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7" checksum = "ad0cf6e91fde44c773c6ee7ec6bba798504641a8bc2eb7e37a04ffbf4dfaa55a"
dependencies = [ dependencies = [
"jobserver", "jobserver",
"libc", "libc",
@@ -738,13 +760,12 @@ dependencies = [
"bip39", "bip39",
"bitcoin 0.32.5", "bitcoin 0.32.5",
"cbor-diag", "cbor-diag",
"cdk-common",
"ciborium", "ciborium",
"criterion", "criterion",
"futures", "futures",
"getrandom", "getrandom",
"instant",
"lightning-invoice", "lightning-invoice",
"once_cell",
"rand", "rand",
"regex", "regex",
"reqwest", "reqwest",
@@ -820,6 +841,31 @@ dependencies = [
"uuid", "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]] [[package]]
name = "cdk-fake-wallet" name = "cdk-fake-wallet"
version = "0.6.0" version = "0.6.0"
@@ -959,7 +1005,7 @@ name = "cdk-redb"
version = "0.6.0" version = "0.6.0"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"cdk", "cdk-common",
"lightning-invoice", "lightning-invoice",
"redb", "redb",
"serde", "serde",
@@ -990,7 +1036,7 @@ version = "0.6.0"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"bitcoin 0.32.5", "bitcoin 0.32.5",
"cdk", "cdk-common",
"lightning-invoice", "lightning-invoice",
"serde_json", "serde_json",
"sqlx", "sqlx",
@@ -1502,6 +1548,16 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 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]] [[package]]
name = "errno" name = "errno"
version = "0.3.10" version = "0.3.10"
@@ -2547,9 +2603,12 @@ dependencies = [
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.22" version = "0.4.24"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" checksum = "3d6ea2a48c204030ee31a7d7fc72c93294c92fe87ecb1789881c9543516e1a0d"
dependencies = [
"value-bag",
]
[[package]] [[package]]
name = "lru" name = "lru"
@@ -2994,7 +3053,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc"
dependencies = [ dependencies = [
"memchr", "memchr",
"thiserror 2.0.10", "thiserror 2.0.11",
"ucd-trie", "ucd-trie",
] ]
@@ -3203,9 +3262,9 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.92" version = "1.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@@ -3303,7 +3362,7 @@ dependencies = [
"rustc-hash", "rustc-hash",
"rustls 0.23.21", "rustls 0.23.21",
"socket2 0.5.8", "socket2 0.5.8",
"thiserror 2.0.10", "thiserror 2.0.11",
"tokio", "tokio",
"tracing", "tracing",
] ]
@@ -3322,7 +3381,7 @@ dependencies = [
"rustls 0.23.21", "rustls 0.23.21",
"rustls-pki-types", "rustls-pki-types",
"slab", "slab",
"thiserror 2.0.10", "thiserror 2.0.11",
"tinyvec", "tinyvec",
"tracing", "tracing",
"web-time", "web-time",
@@ -4017,6 +4076,15 @@ dependencies = [
"syn 2.0.96", "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]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.135" version = "1.0.135"
@@ -4357,6 +4425,84 @@ version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 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]] [[package]]
name = "syn" name = "syn"
version = "1.0.109" version = "1.0.109"
@@ -4436,11 +4582,11 @@ dependencies = [
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "2.0.10" version = "2.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3ac7f54ca534db81081ef1c1e7f6ea8a3ef428d2fc069097c079443d24124d3" checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc"
dependencies = [ dependencies = [
"thiserror-impl 2.0.10", "thiserror-impl 2.0.11",
] ]
[[package]] [[package]]
@@ -4456,9 +4602,9 @@ dependencies = [
[[package]] [[package]]
name = "thiserror-impl" name = "thiserror-impl"
version = "2.0.10" version = "2.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e9465d30713b56a37ede7185763c3492a91be2f5fa68d958c44e41ab9248beb" checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -4945,6 +5091,12 @@ dependencies = [
"utf-8", "utf-8",
] ]
[[package]]
name = "typeid"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e13db2e0ccd5e14a544e8a246ba2312cd25223f616442d7f2cb0e3db614236e"
[[package]] [[package]]
name = "typenum" name = "typenum"
version = "1.17.0" version = "1.17.0"
@@ -5116,6 +5268,42 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" 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]] [[package]]
name = "vcpkg" name = "vcpkg"
version = "0.2.15" version = "0.2.15"

41
crates/cashu/Cargo.toml Normal file
View File

@@ -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"] }

18
crates/cashu/src/lib.rs Normal file
View File

@@ -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;

View File

@@ -1,12 +1,12 @@
//! Mint Types //! Mint types
use bitcoin::bip32::DerivationPath;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use uuid::Uuid; use uuid::Uuid;
use super::{CurrencyUnit, PublicKey};
use crate::mint_url::MintUrl; use crate::mint_url::MintUrl;
use crate::nuts::{MeltQuoteState, MintQuoteState}; use crate::nuts::{MeltQuoteState, MintQuoteState};
use crate::Amount; use crate::{Amount, CurrencyUnit, Id, KeySetInfo, PublicKey};
/// Mint Quote Info /// Mint Quote Info
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)] #[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)] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub struct MeltQuote { pub struct MeltQuote {
/// Quote id /// 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<u64>,
/// [`DerivationPath`] keyset
pub derivation_path: DerivationPath,
/// DerivationPath index of Keyset
pub derivation_path_index: Option<u32>,
/// 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<MintKeySetInfo> 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,
}
}
}

View File

@@ -15,6 +15,7 @@ pub mod nut09;
pub mod nut10; pub mod nut10;
pub mod nut11; pub mod nut11;
pub mod nut12; pub mod nut12;
#[cfg(feature = "wallet")]
pub mod nut13; pub mod nut13;
pub mod nut14; pub mod nut14;
pub mod nut15; pub mod nut15;
@@ -24,9 +25,11 @@ pub mod nut19;
pub mod nut20; pub mod nut20;
pub use nut00::{ pub use nut00::{
BlindSignature, BlindedMessage, CurrencyUnit, PaymentMethod, PreMint, PreMintSecrets, Proof, BlindSignature, BlindedMessage, CurrencyUnit, PaymentMethod, Proof, Proofs, ProofsMethods,
Proofs, ProofsMethods, Token, TokenV3, TokenV4, Witness, Token, TokenV3, TokenV4, Witness,
}; };
#[cfg(feature = "wallet")]
pub use nut00::{PreMint, PreMintSecrets};
pub use nut01::{Keys, KeysResponse, PublicKey, SecretKey}; pub use nut01::{Keys, KeysResponse, PublicKey, SecretKey};
#[cfg(feature = "mint")] #[cfg(feature = "mint")]
pub use nut02::MintKeySet; pub use nut02::MintKeySet;
@@ -50,7 +53,5 @@ pub use nut11::{Conditions, P2PKWitness, SigFlag, SpendingConditions};
pub use nut12::{BlindSignatureDleq, ProofDleq}; pub use nut12::{BlindSignatureDleq, ProofDleq};
pub use nut14::HTLCWitness; pub use nut14::HTLCWitness;
pub use nut15::{Mpp, MppMethodSettings, Settings as NUT15Settings}; pub use nut15::{Mpp, MppMethodSettings, Settings as NUT15Settings};
#[cfg(feature = "mint")] pub use nut17::NotificationPayload;
pub use nut17::PubSubManager;
pub use nut17::{NotificationPayload, SupportedSettings as Nut17SupportedSettings};
pub use nut18::{PaymentRequest, PaymentRequestPayload, Transport}; pub use nut18::{PaymentRequest, PaymentRequestPayload, Transport};

View File

@@ -11,11 +11,18 @@ use std::string::FromUtf8Error;
use serde::{de, Deserialize, Deserializer, Serialize}; use serde::{de, Deserialize, Deserializer, Serialize};
use thiserror::Error; use thiserror::Error;
#[cfg(feature = "wallet")]
use super::nut10; use super::nut10;
#[cfg(feature = "wallet")]
use super::nut11::SpendingConditions; use super::nut11::SpendingConditions;
#[cfg(feature = "wallet")]
use crate::amount::SplitTarget; use crate::amount::SplitTarget;
use crate::dhke::{blind_message, hash_to_curve}; #[cfg(feature = "wallet")]
use crate::nuts::nut01::{PublicKey, SecretKey}; 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::nut11::{serde_p2pk_witness, P2PKWitness};
use crate::nuts::nut12::BlindSignatureDleq; use crate::nuts::nut12::BlindSignatureDleq;
use crate::nuts::nut14::{serde_htlc_witness, HTLCWitness}; use crate::nuts::nut14::{serde_htlc_witness, HTLCWitness};
@@ -491,6 +498,7 @@ impl<'de> Deserialize<'de> for PaymentMethod {
} }
/// PreMint /// PreMint
#[cfg(feature = "wallet")]
#[derive(Debug, Clone, PartialEq, Eq, Serialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct PreMint { pub struct PreMint {
/// Blinded message /// Blinded message
@@ -503,12 +511,14 @@ pub struct PreMint {
pub amount: Amount, pub amount: Amount,
} }
#[cfg(feature = "wallet")]
impl Ord for PreMint { impl Ord for PreMint {
fn cmp(&self, other: &Self) -> std::cmp::Ordering { fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.amount.cmp(&other.amount) self.amount.cmp(&other.amount)
} }
} }
#[cfg(feature = "wallet")]
impl PartialOrd for PreMint { impl PartialOrd for PreMint {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other)) Some(self.cmp(other))
@@ -516,6 +526,7 @@ impl PartialOrd for PreMint {
} }
/// Premint Secrets /// Premint Secrets
#[cfg(feature = "wallet")]
#[derive(Debug, Clone, PartialEq, Eq, Serialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct PreMintSecrets { pub struct PreMintSecrets {
/// Secrets /// Secrets
@@ -524,6 +535,7 @@ pub struct PreMintSecrets {
pub keyset_id: Id, pub keyset_id: Id,
} }
#[cfg(feature = "wallet")]
impl PreMintSecrets { impl PreMintSecrets {
/// Create new [`PreMintSecrets`] /// Create new [`PreMintSecrets`]
pub fn new(keyset_id: Id) -> Self { pub fn new(keyset_id: Id) -> Self {
@@ -712,6 +724,7 @@ impl PreMintSecrets {
} }
// Implement Iterator for PreMintSecrets // Implement Iterator for PreMintSecrets
#[cfg(feature = "wallet")]
impl Iterator for PreMintSecrets { impl Iterator for PreMintSecrets {
type Item = PreMint; type Item = PreMint;
@@ -721,12 +734,14 @@ impl Iterator for PreMintSecrets {
} }
} }
#[cfg(feature = "wallet")]
impl Ord for PreMintSecrets { impl Ord for PreMintSecrets {
fn cmp(&self, other: &Self) -> Ordering { fn cmp(&self, other: &Self) -> Ordering {
self.secrets.cmp(&other.secrets) self.secrets.cmp(&other.secrets)
} }
} }
#[cfg(feature = "wallet")]
impl PartialOrd for PreMintSecrets { impl PartialOrd for PreMintSecrets {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other)) Some(self.cmp(other))
@@ -753,6 +768,7 @@ mod tests {
} }
#[test] #[test]
#[cfg(feature = "wallet")]
fn test_blank_blinded_messages() { fn test_blank_blinded_messages() {
let b = PreMintSecrets::blank( let b = PreMintSecrets::blank(
Id::from_str("009a1f293253e41e").unwrap(), Id::from_str("009a1f293253e41e").unwrap(),

View File

@@ -527,11 +527,11 @@ mod tests {
} }
#[test] #[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_str_multi_keysets = "cashuBo2F0gqJhaUgA_9SLj17PgGFwgaNhYQFhc3hAYWNjMTI0MzVlN2I4NDg0YzNjZjE4NTAxNDkyMThhZjkwZjcxNmE1MmJmNGE1ZWQzNDdlNDhlY2MxM2Y3NzM4OGFjWCECRFODGd5IXVW-07KaZCvuWHk3WrnnpiDhHki6SCQh88-iYWlIAK0mjE0fWCZhcIKjYWECYXN4QDEzMjNkM2Q0NzA3YTU4YWQyZTIzYWRhNGU5ZjFmNDlmNWE1YjRhYzdiNzA4ZWIwZDYxZjczOGY0ODMwN2U4ZWVhY1ghAjRWqhENhLSsdHrr2Cw7AFrKUL9Ffr1XN6RBT6w659lNo2FhAWFzeEA1NmJjYmNiYjdjYzY0MDZiM2ZhNWQ1N2QyMTc0ZjRlZmY4YjQ0MDJiMTc2OTI2ZDNhNTdkM2MzZGNiYjU5ZDU3YWNYIQJzEpxXGeWZN5qXSmJjY8MzxWyvwObQGr5G1YCCgHicY2FtdWh0dHA6Ly9sb2NhbGhvc3Q6MzMzOGF1Y3NhdA==";
let token = Token::from_str(token_str_multi_keysets).unwrap(); 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)); assert_eq!(amount, Amount::from(4));
@@ -552,21 +552,19 @@ mod tests {
assert_eq!("http://localhost:3338", &mint_url.to_string()); 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] #[test]
fn test_tokenv4_from_tokenv3() -> anyhow::Result<()> { fn test_tokenv4_from_tokenv3() {
let token_v3_str = "cashuAeyJ0b2tlbiI6W3sibWludCI6Imh0dHBzOi8vODMzMy5zcGFjZTozMzM4IiwicHJvb2ZzIjpbeyJhbW91bnQiOjIsImlkIjoiMDA5YTFmMjkzMjUzZTQxZSIsInNlY3JldCI6IjQwNzkxNWJjMjEyYmU2MWE3N2UzZTZkMmFlYjRjNzI3OTgwYmRhNTFjZDA2YTZhZmMyOWUyODYxNzY4YTc4MzciLCJDIjoiMDJiYzkwOTc5OTdkODFhZmIyY2M3MzQ2YjVlNDM0NWE5MzQ2YmQyYTUwNmViNzk1ODU5OGE3MmYwY2Y4NTE2M2VhIn0seyJhbW91bnQiOjgsImlkIjoiMDA5YTFmMjkzMjUzZTQxZSIsInNlY3JldCI6ImZlMTUxMDkzMTRlNjFkNzc1NmIwZjhlZTBmMjNhNjI0YWNhYTNmNGUwNDJmNjE0MzNjNzI4YzcwNTdiOTMxYmUiLCJDIjoiMDI5ZThlNTA1MGI4OTBhN2Q2YzA5NjhkYjE2YmMxZDVkNWZhMDQwZWExZGUyODRmNmVjNjlkNjEyOTlmNjcxMDU5In1dfV0sInVuaXQiOiJzYXQiLCJtZW1vIjoiVGhhbmsgeW91LiJ9"; 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 = TokenV4::try_from(token_v3).expect("TokenV3 should be converted to TokenV4");
let token_v4_expected = "cashuBpGFtd2h0dHBzOi8vODMzMy5zcGFjZTozMzM4YXVjc2F0YWRqVGhhbmsgeW91LmF0gaJhaUgAmh8pMlPkHmFwgqRhYQJhc3hANDA3OTE1YmMyMTJiZTYxYTc3ZTNlNmQyYWViNGM3Mjc5ODBiZGE1MWNkMDZhNmFmYzI5ZTI4NjE3NjhhNzgzN2FjWCECvJCXmX2Br7LMc0a15DRak0a9KlBut5WFmKcvDPhRY-phZPakYWEIYXN4QGZlMTUxMDkzMTRlNjFkNzc1NmIwZjhlZTBmMjNhNjI0YWNhYTNmNGUwNDJmNjE0MzNjNzI4YzcwNTdiOTMxYmVhY1ghAp6OUFC4kKfWwJaNsWvB1dX6BA6h3ihPbsadYSmfZxBZYWT2"; let token_v4_expected = "cashuBpGFtd2h0dHBzOi8vODMzMy5zcGFjZTozMzM4YXVjc2F0YWRqVGhhbmsgeW91LmF0gaJhaUgAmh8pMlPkHmFwgqRhYQJhc3hANDA3OTE1YmMyMTJiZTYxYTc3ZTNlNmQyYWViNGM3Mjc5ODBiZGE1MWNkMDZhNmFmYzI5ZTI4NjE3NjhhNzgzN2FjWCECvJCXmX2Br7LMc0a15DRak0a9KlBut5WFmKcvDPhRY-phZPakYWEIYXN4QGZlMTUxMDkzMTRlNjFkNzc1NmIwZjhlZTBmMjNhNjI0YWNhYTNmNGUwNDJmNjE0MzNjNzI4YzcwNTdiOTMxYmVhY1ghAp6OUFC4kKfWwJaNsWvB1dX6BA6h3ihPbsadYSmfZxBZYWT2";
assert_eq!(token_v4.to_string(), token_v4_expected); assert_eq!(token_v4.to_string(), token_v4_expected);
Ok(())
} }
#[test] #[test]

View File

@@ -9,9 +9,7 @@ use std::array::TryFromSliceError;
use std::collections::BTreeMap; use std::collections::BTreeMap;
#[cfg(feature = "mint")] #[cfg(feature = "mint")]
use bitcoin::bip32::DerivationPath; use bitcoin::bip32::{ChildNumber, DerivationPath, Xpriv};
#[cfg(feature = "mint")]
use bitcoin::bip32::{ChildNumber, Xpriv};
use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::hashes::Hash; use bitcoin::hashes::Hash;
#[cfg(feature = "mint")] #[cfg(feature = "mint")]
@@ -268,8 +266,8 @@ fn default_input_fee_ppk() -> u64 {
0 0
} }
/// MintKeyset
#[cfg(feature = "mint")] #[cfg(feature = "mint")]
/// MintKeyset
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct MintKeySet { pub struct MintKeySet {
/// Keyset [`Id`] /// Keyset [`Id`]

View File

@@ -5,7 +5,9 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use thiserror::Error; 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; use crate::Amount;
/// NUT03 Error /// NUT03 Error
@@ -20,6 +22,7 @@ pub enum Error {
} }
/// Preswap information /// Preswap information
#[cfg(feature = "wallet")]
#[derive(Debug, Clone, PartialEq, Eq, Serialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct PreSwap { pub struct PreSwap {
/// Preswap mint secrets /// Preswap mint secrets

View File

@@ -5,7 +5,6 @@
use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde::{Deserialize, Deserializer, Serialize, Serializer};
use super::nut01::PublicKey; use super::nut01::PublicKey;
#[cfg(feature = "mint")]
use super::nut17::SupportedMethods; use super::nut17::SupportedMethods;
use super::nut19::CachedEndpoint; use super::nut19::CachedEndpoint;
use super::{nut04, nut05, nut15, nut19, MppMethodSettings}; use super::{nut04, nut05, nut15, nut19, MppMethodSettings};
@@ -241,7 +240,6 @@ pub struct Nuts {
/// NUT17 Settings /// NUT17 Settings
#[serde(default)] #[serde(default)]
#[serde(rename = "17")] #[serde(rename = "17")]
#[cfg(feature = "mint")]
pub nut17: super::nut17::SupportedSettings, pub nut17: super::nut17::SupportedSettings,
/// NUT19 Settings /// NUT19 Settings
#[serde(default)] #[serde(default)]
@@ -342,7 +340,6 @@ impl Nuts {
} }
/// Nut17 settings /// Nut17 settings
#[cfg(feature = "mint")]
pub fn nut17(self, supported: Vec<SupportedMethods>) -> Self { pub fn nut17(self, supported: Vec<SupportedMethods>) -> Self {
Self { Self {
nut17: super::nut17::SupportedSettings { supported }, nut17: super::nut17::SupportedSettings { supported },

View File

@@ -2,9 +2,7 @@
//! //!
//! <https://github.com/cashubtc/nuts/blob/main/11.md> //! <https://github.com/cashubtc/nuts/blob/main/11.md>
use std::collections::HashMap; use std::collections::{HashMap, HashSet};
#[cfg(feature = "mint")]
use std::collections::HashSet;
use std::str::FromStr; use std::str::FromStr;
use std::{fmt, vec}; use std::{fmt, vec};
@@ -18,9 +16,7 @@ use thiserror::Error;
use super::nut00::Witness; use super::nut00::Witness;
use super::nut01::PublicKey; use super::nut01::PublicKey;
#[cfg(feature = "mint")] use super::{Kind, Nut10Secret, Proof, Proofs, SecretKey};
use super::Proofs;
use super::{Kind, Nut10Secret, Proof, SecretKey};
use crate::nuts::nut00::BlindedMessage; use crate::nuts::nut00::BlindedMessage;
use crate::secret::Secret; use crate::secret::Secret;
use crate::util::{hex, unix_time}; 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 /// Get the signature flag that should be enforced for a set of proofs and the
/// public keys that signatures are valid for /// 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 sig_flag = SigFlag::SigInputs;
let mut pubkeys = HashSet::new(); let mut pubkeys = HashSet::new();
let mut sigs_required = 1; let mut sigs_required = 1;
@@ -658,10 +653,9 @@ pub(crate) fn enforce_sig_flag(proofs: Proofs) -> EnforceSigFlag {
} }
} }
#[cfg(feature = "mint")]
/// Enforce Sigflag info /// Enforce Sigflag info
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct EnforceSigFlag { pub struct EnforceSigFlag {
/// Sigflag required for proofs /// Sigflag required for proofs
pub sig_flag: SigFlag, pub sig_flag: SigFlag,
/// Pubkeys that can sign for proofs /// Pubkeys that can sign for proofs

View File

@@ -1,38 +1,28 @@
//! Specific Subscription for the cdk crate //! Specific Subscription for the cdk crate
use std::str::FromStr;
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[cfg(feature = "mint")]
use uuid::Uuid; use uuid::Uuid;
#[cfg(feature = "mint")]
use super::PublicKey; use super::PublicKey;
use crate::nuts::{ use crate::nuts::{
CurrencyUnit, MeltQuoteBolt11Response, MintQuoteBolt11Response, PaymentMethod, ProofState, 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; pub mod ws;
/// Subscription Parameter according to the standard /// Subscription Parameter according to the standard
#[derive(Debug, Clone, Serialize, Eq, PartialEq, Hash, Deserialize)] #[derive(Debug, Clone, Serialize, Eq, PartialEq, Hash, Deserialize)]
pub struct Params { #[serde(bound = "I: DeserializeOwned + Serialize")]
pub struct Params<I> {
/// Kind /// Kind
pub kind: Kind, pub kind: Kind,
/// Filters /// Filters
pub filters: Vec<String>, pub filters: Vec<String>,
/// Subscription Id /// Subscription Id
#[serde(rename = "subId")] #[serde(rename = "subId")]
pub id: SubId, pub id: I,
} }
/// Check state Settings /// Check state Settings
@@ -109,6 +99,7 @@ impl<T> From<MintQuoteBolt11Response<T>> for NotificationPayload<T> {
} }
} }
#[cfg(feature = "mint")]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
/// A parsed notification /// A parsed notification
pub enum Notification { pub enum Notification {
@@ -120,24 +111,6 @@ pub enum Notification {
MintQuoteBolt11(Uuid), MintQuoteBolt11(Uuid),
} }
impl Indexable for NotificationPayload<Uuid> {
type Type = Notification;
fn to_indexes(&self) -> Vec<Index<Self::Type>> {
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 /// Kind
#[derive(Debug, Clone, Copy, Eq, Ord, PartialOrd, PartialEq, Hash, Serialize, Deserialize)] #[derive(Debug, Clone, Copy, Eq, Ord, PartialOrd, PartialEq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
@@ -150,8 +123,8 @@ pub enum Kind {
ProofState, ProofState,
} }
impl AsRef<SubId> for Params { impl<I> AsRef<I> for Params<I> {
fn as_ref(&self) -> &SubId { fn as_ref(&self) -> &I {
&self.id &self.id
} }
} }
@@ -159,6 +132,7 @@ impl AsRef<SubId> for Params {
/// Parsing error /// Parsing error
#[derive(thiserror::Error, Debug)] #[derive(thiserror::Error, Debug)]
pub enum Error { pub enum Error {
#[cfg(feature = "mint")]
#[error("Uuid Error: {0}")] #[error("Uuid Error: {0}")]
/// Uuid Error /// Uuid Error
Uuid(#[from] uuid::Error), Uuid(#[from] uuid::Error),
@@ -167,27 +141,3 @@ pub enum Error {
/// PublicKey Error /// PublicKey Error
PublicKey(#[from] crate::nuts::nut01::Error), PublicKey(#[from] crate::nuts::nut01::Error),
} }
impl TryFrom<Params> for Vec<Index<Notification>> {
type Error = Error;
fn try_from(val: Params) -> Result<Self, Self::Error> {
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::<Result<_, _>>()
}
}

View File

@@ -2,31 +2,32 @@
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use uuid::Uuid;
use super::{NotificationPayload, Params, SubId}; use super::{NotificationPayload, Params};
/// JSON RPC version /// JSON RPC version
pub const JSON_RPC_VERSION: &str = "2.0"; pub const JSON_RPC_VERSION: &str = "2.0";
/// The response to a subscription request /// The response to a subscription request
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WsSubscribeResponse { #[serde(bound = "I: Serialize + DeserializeOwned")]
pub struct WsSubscribeResponse<I> {
/// Status /// Status
pub status: String, pub status: String,
/// Subscription ID /// Subscription ID
#[serde(rename = "subId")] #[serde(rename = "subId")]
pub sub_id: SubId, pub sub_id: I,
} }
/// The response to an unsubscription request /// The response to an unsubscription request
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WsUnsubscribeResponse { #[serde(bound = "I: Serialize + DeserializeOwned")]
pub struct WsUnsubscribeResponse<I> {
/// Status /// Status
pub status: String, pub status: String,
/// Subscription ID /// Subscription ID
#[serde(rename = "subId")] #[serde(rename = "subId")]
pub sub_id: SubId, pub sub_id: I,
} }
/// The notification /// The notification
@@ -34,87 +35,74 @@ pub struct WsUnsubscribeResponse {
/// This is the notification that is sent to the client when an event matches a /// This is the notification that is sent to the client when an event matches a
/// subscription /// subscription
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(bound = "T: Serialize + DeserializeOwned")] #[serde(bound = "T: Serialize + DeserializeOwned, I: Serialize + DeserializeOwned")]
pub struct NotificationInner<T> { pub struct NotificationInner<T, I> {
/// The subscription ID /// The subscription ID
#[serde(rename = "subId")] #[serde(rename = "subId")]
pub sub_id: SubId, pub sub_id: I,
/// The notification payload /// The notification payload
pub payload: NotificationPayload<T>, pub payload: NotificationPayload<T>,
} }
impl From<NotificationInner<Uuid>> for NotificationInner<String> {
fn from(value: NotificationInner<Uuid>) -> 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 /// Responses from the web socket server
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(bound = "I: Serialize + DeserializeOwned")]
#[serde(untagged)] #[serde(untagged)]
pub enum WsResponseResult { pub enum WsResponseResult<I> {
/// A response to a subscription request /// A response to a subscription request
Subscribe(WsSubscribeResponse), Subscribe(WsSubscribeResponse<I>),
/// Unsubscribe /// Unsubscribe
Unsubscribe(WsUnsubscribeResponse), Unsubscribe(WsUnsubscribeResponse<I>),
} }
impl From<WsSubscribeResponse> for WsResponseResult { impl<I> From<WsSubscribeResponse<I>> for WsResponseResult<I> {
fn from(response: WsSubscribeResponse) -> Self { fn from(response: WsSubscribeResponse<I>) -> Self {
WsResponseResult::Subscribe(response) WsResponseResult::Subscribe(response)
} }
} }
impl From<WsUnsubscribeResponse> for WsResponseResult { impl<I> From<WsUnsubscribeResponse<I>> for WsResponseResult<I> {
fn from(response: WsUnsubscribeResponse) -> Self { fn from(response: WsUnsubscribeResponse<I>) -> Self {
WsResponseResult::Unsubscribe(response) WsResponseResult::Unsubscribe(response)
} }
} }
/// The request to unsubscribe /// The request to unsubscribe
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WsUnsubscribeRequest { #[serde(bound = "I: Serialize + DeserializeOwned")]
pub struct WsUnsubscribeRequest<I> {
/// Subscription ID /// Subscription ID
#[serde(rename = "subId")] #[serde(rename = "subId")]
pub sub_id: SubId, pub sub_id: I,
} }
/// The inner method of the websocket request /// The inner method of the websocket request
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case", tag = "method", content = "params")] #[serde(rename_all = "snake_case", tag = "method", content = "params")]
pub enum WsMethodRequest { #[serde(bound = "I: Serialize + DeserializeOwned")]
pub enum WsMethodRequest<I> {
/// Subscribe method /// Subscribe method
Subscribe(Params), Subscribe(Params<I>),
/// Unsubscribe method /// Unsubscribe method
Unsubscribe(WsUnsubscribeRequest), Unsubscribe(WsUnsubscribeRequest<I>),
} }
/// Websocket request /// Websocket request
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WsRequest { #[serde(bound = "I: Serialize + DeserializeOwned")]
pub struct WsRequest<I> {
/// JSON RPC version /// JSON RPC version
pub jsonrpc: String, pub jsonrpc: String,
/// The method body /// The method body
#[serde(flatten)] #[serde(flatten)]
pub method: WsMethodRequest, pub method: WsMethodRequest<I>,
/// The request ID /// The request ID
pub id: usize, pub id: usize,
} }
impl From<(WsMethodRequest, usize)> for WsRequest { impl<I> From<(WsMethodRequest<I>, usize)> for WsRequest<I> {
fn from((method, id): (WsMethodRequest, usize)) -> Self { fn from((method, id): (WsMethodRequest<I>, usize)) -> Self {
WsRequest { WsRequest {
jsonrpc: JSON_RPC_VERSION.to_owned(), jsonrpc: JSON_RPC_VERSION.to_owned(),
method, method,
@@ -145,11 +133,12 @@ pub struct WsErrorBody {
/// Websocket response /// Websocket response
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WsResponse { #[serde(bound = "I: Serialize + DeserializeOwned")]
pub struct WsResponse<I> {
/// JSON RPC version /// JSON RPC version
pub jsonrpc: String, pub jsonrpc: String,
/// The result /// The result
pub result: WsResponseResult, pub result: WsResponseResult<I>,
/// The request ID /// The request ID
pub id: usize, pub id: usize,
} }
@@ -167,18 +156,19 @@ pub struct WsErrorResponse {
/// Message from the server to the client /// Message from the server to the client
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(bound = "I: Serialize + DeserializeOwned")]
#[serde(untagged)] #[serde(untagged)]
pub enum WsMessageOrResponse { pub enum WsMessageOrResponse<I> {
/// A response to a request /// A response to a request
Response(WsResponse), Response(WsResponse<I>),
/// An error response /// An error response
ErrorResponse(WsErrorResponse), ErrorResponse(WsErrorResponse),
/// A notification /// A notification
Notification(WsNotification<NotificationInner<String>>), Notification(WsNotification<NotificationInner<String, I>>),
} }
impl From<(usize, Result<WsResponseResult, WsErrorBody>)> for WsMessageOrResponse { impl<I> From<(usize, Result<WsResponseResult<I>, WsErrorBody>)> for WsMessageOrResponse<I> {
fn from((id, result): (usize, Result<WsResponseResult, WsErrorBody>)) -> Self { fn from((id, result): (usize, Result<WsResponseResult<I>, WsErrorBody>)) -> Self {
match result { match result {
Ok(result) => WsMessageOrResponse::Response(WsResponse { Ok(result) => WsMessageOrResponse::Response(WsResponse {
jsonrpc: JSON_RPC_VERSION.to_owned(), jsonrpc: JSON_RPC_VERSION.to_owned(),
@@ -193,23 +183,3 @@ impl From<(usize, Result<WsResponseResult, WsErrorBody>)> for WsMessageOrRespons
} }
} }
} }
impl From<NotificationInner<Uuid>> for WsMessageOrResponse {
fn from(notification: NotificationInner<Uuid>) -> Self {
WsMessageOrResponse::Notification(WsNotification {
jsonrpc: JSON_RPC_VERSION.to_owned(),
method: "subscribe".to_string(),
params: notification.into(),
})
}
}
impl From<NotificationInner<String>> for WsMessageOrResponse {
fn from(notification: NotificationInner<String>) -> Self {
WsMessageOrResponse::Notification(WsNotification {
jsonrpc: JSON_RPC_VERSION.to_owned(),
method: "subscribe".to_string(),
params: notification,
})
}
}

View File

@@ -147,15 +147,15 @@ mod tests {
const PAYMENT_REQUEST: &str = "creqApWF0gaNhdGVub3N0cmFheKlucHJvZmlsZTFxeTI4d3VtbjhnaGo3dW45ZDNzaGp0bnl2OWtoMnVld2Q5aHN6OW1od2RlbjV0ZTB3ZmprY2N0ZTljdXJ4dmVuOWVlaHFjdHJ2NWhzenJ0aHdkZW41dGUwZGVoaHh0bnZkYWtxcWd5ZGFxeTdjdXJrNDM5eWtwdGt5c3Y3dWRoZGh1NjhzdWNtMjk1YWtxZWZkZWhrZjBkNDk1Y3d1bmw1YWeBgmFuYjE3YWloYjdhOTAxNzZhYQphdWNzYXRhbYF4Imh0dHBzOi8vbm9mZWVzLnRlc3RudXQuY2FzaHUuc3BhY2U="; const PAYMENT_REQUEST: &str = "creqApWF0gaNhdGVub3N0cmFheKlucHJvZmlsZTFxeTI4d3VtbjhnaGo3dW45ZDNzaGp0bnl2OWtoMnVld2Q5aHN6OW1od2RlbjV0ZTB3ZmprY2N0ZTljdXJ4dmVuOWVlaHFjdHJ2NWhzenJ0aHdkZW41dGUwZGVoaHh0bnZkYWtxcWd5ZGFxeTdjdXJrNDM5eWtwdGt5c3Y3dWRoZGh1NjhzdWNtMjk1YWtxZWZkZWhrZjBkNDk1Y3d1bmw1YWeBgmFuYjE3YWloYjdhOTAxNzZhYQphdWNzYXRhbYF4Imh0dHBzOi8vbm9mZWVzLnRlc3RudXQuY2FzaHUuc3BhY2U=";
#[test] #[test]
fn test_decode_payment_req() -> anyhow::Result<()> { fn test_decode_payment_req() {
let req = PaymentRequest::from_str(PAYMENT_REQUEST)?; let req = PaymentRequest::from_str(PAYMENT_REQUEST).expect("valid payment request");
assert_eq!(&req.payment_id.unwrap(), "b7a90176"); assert_eq!(&req.payment_id.unwrap(), "b7a90176");
assert_eq!(req.amount.unwrap(), 10.into()); assert_eq!(req.amount.unwrap(), 10.into());
assert_eq!(req.unit.clone().unwrap(), CurrencyUnit::Sat); assert_eq!(req.unit.clone().unwrap(), CurrencyUnit::Sat);
assert_eq!( assert_eq!(
req.mints.unwrap(), 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); 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()]])}; 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); assert_eq!(transport, &expected_transport);
Ok(())
} }
#[test] #[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 transport = Transport {_type: TransportType::Nostr, target: "nprofile1qy28wumn8ghj7un9d3shjtnyv9kh2uewd9hsz9mhwden5te0wfjkccte9curxven9eehqctrv5hszrthwden5te0dehhxtnvdakqqgydaqy7curk439ykptkysv7udhdhu68sucm295akqefdehkf0d495cwunl5".to_string(), tags: Some(vec![vec!["n".to_string(), "17".to_string()]])};
let request = PaymentRequest { let request = PaymentRequest {
@@ -177,27 +175,27 @@ mod tests {
amount: Some(10.into()), amount: Some(10.into()),
unit: Some(CurrencyUnit::Sat), unit: Some(CurrencyUnit::Sat),
single_use: None, 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, description: None,
transports: vec![transport.clone()], transports: vec![transport.clone()],
}; };
let request_str = request.to_string(); 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.payment_id.unwrap(), "b7a90176");
assert_eq!(req.amount.unwrap(), 10.into()); assert_eq!(req.amount.unwrap(), 10.into());
assert_eq!(req.unit.clone().unwrap(), CurrencyUnit::Sat); assert_eq!(req.unit.clone().unwrap(), CurrencyUnit::Sat);
assert_eq!( assert_eq!(
req.mints.unwrap(), 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); assert_eq!(req.unit.unwrap(), CurrencyUnit::Sat);
let t = req.transports.first().unwrap(); let t = req.transports.first().unwrap();
assert_eq!(&transport, t); assert_eq!(&transport, t);
Ok(())
} }
} }

View File

@@ -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();
});
}
}

View File

@@ -1,16 +1,14 @@
//! Util
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
use anyhow::Result;
use bitcoin::secp256k1::{rand, All, Secp256k1}; use bitcoin::secp256k1::{rand, All, Secp256k1};
#[cfg(target_arch = "wasm32")]
use instant::SystemTime;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
pub mod hex; pub mod hex;
#[cfg(target_arch = "wasm32")]
use instant::SystemTime;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
const UNIX_EPOCH: SystemTime = SystemTime::UNIX_EPOCH; const UNIX_EPOCH: SystemTime = SystemTime::UNIX_EPOCH;
@@ -30,10 +28,22 @@ pub fn unix_time() -> u64 {
.as_secs() .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<std::io::Error>),
/// CBOR diagnostic notation error
#[error("CBOR diagnostic notation error: {0}")]
CborDiag(#[from] cbor_diag::Error),
}
/// Serializes a struct to the CBOR diagnostic notation. /// Serializes a struct to the CBOR diagnostic notation.
/// ///
/// See <https://www.rfc-editor.org/rfc/rfc8949.html#name-diagnostic-notation> /// See <https://www.rfc-editor.org/rfc/rfc8949.html#name-diagnostic-notation>
pub fn serialize_to_cbor_diag<T: serde::Serialize>(data: &T) -> Result<String> { pub fn serialize_to_cbor_diag<T: serde::Serialize>(data: &T) -> Result<String, CborError> {
let mut cbor_buffer = Vec::new(); let mut cbor_buffer = Vec::new();
ciborium::ser::into_writer(data, &mut cbor_buffer)?; ciborium::ser::into_writer(data, &mut cbor_buffer)?;

View File

@@ -1,11 +1,35 @@
//! Wallet Types //! Wallet Types
use std::fmt;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::mint_url::MintUrl; use crate::mint_url::MintUrl;
use crate::nuts::{CurrencyUnit, MeltQuoteState, MintQuoteState, SecretKey}; use crate::nuts::{CurrencyUnit, MeltQuoteState, MintQuoteState, SecretKey};
use crate::Amount; 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 /// Mint Quote Info
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct MintQuote { pub struct MintQuote {

View File

@@ -1,10 +1,12 @@
use std::collections::HashMap; use std::collections::HashMap;
use axum::extract::ws::{Message, WebSocket}; use axum::extract::ws::{Message, WebSocket};
use cdk::nuts::nut17::ws::{ use cdk::nuts::nut17::NotificationPayload;
NotificationInner, WsErrorBody, WsMessageOrResponse, WsMethodRequest, WsRequest, 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 futures::StreamExt;
use tokio::sync::mpsc; use tokio::sync::mpsc;
use uuid::Uuid; 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. // unsubscribed from the subscription manager, just ignore it.
continue; continue;
} }
let notification: WsMessageOrResponse= NotificationInner { let notification = notification_to_ws_message(NotificationInner {
sub_id, sub_id,
payload, payload,
}.into(); });
let message = match serde_json::to_string(&notification) { let message = match serde_json::to_string(&notification) {
Ok(message) => message, Ok(message) => message,
Err(err) => { Err(err) => {

View File

@@ -1,5 +1,5 @@
use cdk::nuts::nut17::ws::{WsResponseResult, WsSubscribeResponse}; use cdk::subscription::{IndexableParams, Params};
use cdk::nuts::nut17::Params; use cdk::ws::{WsResponseResult, WsSubscribeResponse};
use super::{WsContext, WsError}; use super::{WsContext, WsError};
@@ -15,6 +15,8 @@ pub(crate) async fn handle(
return Err(WsError::InvalidParams); return Err(WsError::InvalidParams);
} }
let params: IndexableParams = params.into();
let mut subscription = context let mut subscription = context
.state .state
.mint .mint

View File

@@ -1,4 +1,4 @@
use cdk::nuts::nut17::ws::{WsResponseResult, WsUnsubscribeRequest, WsUnsubscribeResponse}; use cdk::ws::{WsResponseResult, WsUnsubscribeRequest, WsUnsubscribeResponse};
use super::{WsContext, WsError}; use super::{WsContext, WsError};

View File

@@ -3,7 +3,7 @@ use std::str::FromStr;
use anyhow::Result; use anyhow::Result;
use cdk::mint_url::MintUrl; use cdk::mint_url::MintUrl;
use cdk::nuts::CurrencyUnit; use cdk::nuts::CurrencyUnit;
use cdk::wallet::multi_mint_wallet::WalletKey; use cdk::wallet::types::WalletKey;
use cdk::wallet::MultiMintWallet; use cdk::wallet::MultiMintWallet;
use cdk::Amount; use cdk::Amount;
use clap::Args; use clap::Args;

View File

@@ -5,7 +5,8 @@ use std::str::FromStr;
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use cdk::amount::MSAT_IN_SAT; use cdk::amount::MSAT_IN_SAT;
use cdk::nuts::{CurrencyUnit, MeltOptions}; 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 cdk::Bolt11Invoice;
use clap::Args; use clap::Args;

View File

@@ -7,7 +7,7 @@ use cdk::cdk_database::{Error, WalletDatabase};
use cdk::mint_url::MintUrl; use cdk::mint_url::MintUrl;
use cdk::nuts::nut00::ProofsMethods; use cdk::nuts::nut00::ProofsMethods;
use cdk::nuts::{CurrencyUnit, MintQuoteState, NotificationPayload}; 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::wallet::{MultiMintWallet, Wallet, WalletSubscription};
use cdk::Amount; use cdk::Amount;
use clap::Args; use clap::Args;

View File

@@ -6,7 +6,8 @@ use anyhow::{anyhow, Result};
use cdk::cdk_database::{self, WalletDatabase}; use cdk::cdk_database::{self, WalletDatabase};
use cdk::nuts::{SecretKey, Token}; use cdk::nuts::{SecretKey, Token};
use cdk::util::unix_time; 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::wallet::Wallet;
use cdk::Amount; use cdk::Amount;
use clap::Args; use clap::Args;

View File

@@ -5,7 +5,7 @@ use anyhow::Result;
use cdk::cdk_database::{Error, WalletDatabase}; use cdk::cdk_database::{Error, WalletDatabase};
use cdk::mint_url::MintUrl; use cdk::mint_url::MintUrl;
use cdk::nuts::CurrencyUnit; use cdk::nuts::CurrencyUnit;
use cdk::wallet::multi_mint_wallet::WalletKey; use cdk::wallet::types::WalletKey;
use cdk::wallet::{MultiMintWallet, Wallet}; use cdk::wallet::{MultiMintWallet, Wallet};
use clap::Args; use clap::Args;

View File

@@ -5,8 +5,7 @@ use std::str::FromStr;
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use cdk::amount::SplitTarget; use cdk::amount::SplitTarget;
use cdk::nuts::{Conditions, CurrencyUnit, PublicKey, SpendingConditions}; use cdk::nuts::{Conditions, CurrencyUnit, PublicKey, SpendingConditions};
use cdk::wallet::multi_mint_wallet::WalletKey; use cdk::wallet::types::{SendKind, WalletKey};
use cdk::wallet::types::SendKind;
use cdk::wallet::MultiMintWallet; use cdk::wallet::MultiMintWallet;
use cdk::Amount; use cdk::Amount;
use clap::Args; use clap::Args;

View File

@@ -1,7 +1,7 @@
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use cdk::mint_url::MintUrl; use cdk::mint_url::MintUrl;
use cdk::nuts::CurrencyUnit; use cdk::nuts::CurrencyUnit;
use cdk::wallet::multi_mint_wallet::WalletKey; use cdk::wallet::types::WalletKey;
use cdk::wallet::MultiMintWallet; use cdk::wallet::MultiMintWallet;
use clap::Args; use clap::Args;

View File

@@ -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"

View File

@@ -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<Error> + From<Error>;
/// 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<Option<Id>, Self::Err>;
/// Get all Active Keyset
async fn get_active_keysets(&self) -> Result<HashMap<CurrencyUnit, Id>, 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<Option<MintMintQuote>, Self::Err>;
/// Update state of [`MintMintQuote`]
async fn update_mint_quote_state(
&self,
quote_id: &Uuid,
state: MintQuoteState,
) -> Result<MintQuoteState, Self::Err>;
/// Get all [`MintMintQuote`]s
async fn get_mint_quote_by_request(
&self,
request: &str,
) -> Result<Option<MintMintQuote>, Self::Err>;
/// Get all [`MintMintQuote`]s
async fn get_mint_quote_by_request_lookup_id(
&self,
request_lookup_id: &str,
) -> Result<Option<MintMintQuote>, Self::Err>;
/// Get Mint Quotes
async fn get_mint_quotes(&self) -> Result<Vec<MintMintQuote>, 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<Option<mint::MeltQuote>, Self::Err>;
/// Update [`mint::MeltQuote`] state
async fn update_melt_quote_state(
&self,
quote_id: &Uuid,
state: MeltQuoteState,
) -> Result<MeltQuoteState, Self::Err>;
/// Get all [`mint::MeltQuote`]s
async fn get_melt_quotes(&self) -> Result<Vec<mint::MeltQuote>, 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<Uuid>,
ln_key: LnKey,
) -> Result<(), Self::Err>;
/// Get melt request
async fn get_melt_request(
&self,
quote_id: &Uuid,
) -> Result<Option<(MeltBolt11Request<Uuid>, 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<Option<MintKeySetInfo>, Self::Err>;
/// Get [`MintKeySetInfo`]s
async fn get_keyset_infos(&self) -> Result<Vec<MintKeySetInfo>, Self::Err>;
/// Add spent [`Proofs`]
async fn add_proofs(&self, proof: Proofs, quote_id: Option<Uuid>) -> Result<(), Self::Err>;
/// Get [`Proofs`] by ys
async fn get_proofs_by_ys(&self, ys: &[PublicKey]) -> Result<Vec<Option<Proof>>, Self::Err>;
/// Get ys by quote id
async fn get_proof_ys_by_quote_id(&self, quote_id: &Uuid) -> Result<Vec<PublicKey>, Self::Err>;
/// Get [`Proofs`] state
async fn get_proofs_states(&self, ys: &[PublicKey]) -> Result<Vec<Option<State>>, Self::Err>;
/// Get [`Proofs`] state
async fn update_proofs_states(
&self,
ys: &[PublicKey],
proofs_state: State,
) -> Result<Vec<Option<State>>, Self::Err>;
/// Get [`Proofs`] by state
async fn get_proofs_by_keyset_id(
&self,
keyset_id: &Id,
) -> Result<(Proofs, Vec<Option<State>>), Self::Err>;
/// Add [`BlindSignature`]
async fn add_blind_signatures(
&self,
blinded_messages: &[PublicKey],
blind_signatures: &[BlindSignature],
quote_id: Option<Uuid>,
) -> Result<(), Self::Err>;
/// Get [`BlindSignature`]s
async fn get_blind_signatures(
&self,
blinded_messages: &[PublicKey],
) -> Result<Vec<Option<BlindSignature>>, Self::Err>;
/// Get [`BlindSignature`]s for keyset_id
async fn get_blind_signatures_for_keyset(
&self,
keyset_id: &Id,
) -> Result<Vec<BlindSignature>, Self::Err>;
/// Get [`BlindSignature`]s for quote
async fn get_blind_signatures_for_quote(
&self,
quote_id: &Uuid,
) -> Result<Vec<BlindSignature>, Self::Err>;
}

View File

@@ -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<dyn std::error::Error + Send + Sync>),
/// 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,
}

View File

@@ -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<Error> + From<Error>;
/// Add Mint to storage
async fn add_mint(
&self,
mint_url: MintUrl,
mint_info: Option<MintInfo>,
) -> 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<Option<MintInfo>, Self::Err>;
/// Get all mints from storage
async fn get_mints(&self) -> Result<HashMap<MintUrl, Option<MintInfo>>, 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<KeySetInfo>,
) -> Result<(), Self::Err>;
/// Get mint keysets for mint url
async fn get_mint_keysets(
&self,
mint_url: MintUrl,
) -> Result<Option<Vec<KeySetInfo>>, Self::Err>;
/// Get mint keyset by id
async fn get_keyset_by_id(&self, keyset_id: &Id) -> Result<Option<KeySetInfo>, 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<Option<WalletMintQuote>, Self::Err>;
/// Get mint quotes from storage
async fn get_mint_quotes(&self) -> Result<Vec<WalletMintQuote>, 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<Option<wallet::MeltQuote>, 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<Option<Keys>, 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<ProofInfo>,
removed_ys: Vec<PublicKey>,
) -> Result<(), Self::Err>;
/// Set proofs as pending in storage. Proofs are identified by their Y
/// value.
async fn set_pending_proofs(&self, ys: Vec<PublicKey>) -> Result<(), Self::Err>;
/// Reserve proofs in storage. Proofs are identified by their Y value.
async fn reserve_proofs(&self, ys: Vec<PublicKey>) -> Result<(), Self::Err>;
/// Set proofs as unspent in storage. Proofs are identified by their Y
/// value.
async fn set_unspent_proofs(&self, ys: Vec<PublicKey>) -> Result<(), Self::Err>;
/// Get proofs from storage
async fn get_proofs(
&self,
mint_url: Option<MintUrl>,
unit: Option<CurrencyUnit>,
state: Option<Vec<State>>,
spending_conditions: Option<Vec<SpendingConditions>>,
) -> Result<Vec<ProofInfo>, 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<Option<u32>, Self::Err>;
/// Get when nostr key was last checked
async fn get_nostr_last_checked(
&self,
verifying_key: &PublicKey,
) -> Result<Option<u32>, Self::Err>;
/// Update last checked time
async fn add_nostr_last_checked(
&self,
verifying_key: PublicKey,
last_checked: u32,
) -> Result<(), Self::Err>;
}

View File

@@ -9,7 +9,7 @@ use thiserror::Error;
use crate::nuts::Id; use crate::nuts::Id;
use crate::util::hex; use crate::util::hex;
#[cfg(feature = "wallet")] #[cfg(feature = "wallet")]
use crate::wallet::multi_mint_wallet::WalletKey; use crate::wallet::WalletKey;
use crate::Amount; use crate::Amount;
/// CDK Error /// CDK Error
@@ -128,8 +128,8 @@ pub enum Error {
#[error("Incorrect wallet: `{0}`")] #[error("Incorrect wallet: `{0}`")]
IncorrectWallet(String), IncorrectWallet(String),
/// Unknown Wallet /// Unknown Wallet
#[cfg(feature = "wallet")]
#[error("Unknown wallet: `{0}`")] #[error("Unknown wallet: `{0}`")]
#[cfg(feature = "wallet")]
UnknownWallet(WalletKey), UnknownWallet(WalletKey),
/// Max Fee Ecxeded /// Max Fee Ecxeded
#[error("Max fee exceeded")] #[error("Max fee exceeded")]
@@ -197,10 +197,9 @@ pub enum Error {
/// From hex error /// From hex error
#[error(transparent)] #[error(transparent)]
HexError(#[from] hex::Error), HexError(#[from] hex::Error),
#[cfg(feature = "wallet")] /// Http transport error
/// From hex error #[error("Http transport error: {0}")]
#[error(transparent)] HttpError(String),
ReqwestError(#[from] reqwest::Error),
// Crate error conversions // Crate error conversions
/// Cashu Url Error /// Cashu Url Error
@@ -238,6 +237,7 @@ pub enum Error {
NUT12(#[from] crate::nuts::nut12::Error), NUT12(#[from] crate::nuts::nut12::Error),
/// NUT13 Error /// NUT13 Error
#[error(transparent)] #[error(transparent)]
#[cfg(feature = "wallet")]
NUT13(#[from] crate::nuts::nut13::Error), NUT13(#[from] crate::nuts::nut13::Error),
/// NUT14 Error /// NUT14 Error
#[error(transparent)] #[error(transparent)]
@@ -249,13 +249,12 @@ pub enum Error {
#[error(transparent)] #[error(transparent)]
NUT20(#[from] crate::nuts::nut20::Error), NUT20(#[from] crate::nuts::nut20::Error),
/// Database Error /// Database Error
#[cfg(any(feature = "wallet", feature = "mint"))]
#[error(transparent)] #[error(transparent)]
Database(#[from] crate::cdk_database::Error), Database(#[from] crate::database::Error),
/// Lightning Error /// Lightning Error
#[cfg(feature = "mint")]
#[error(transparent)] #[error(transparent)]
Lightning(#[from] crate::cdk_lightning::Error), #[cfg(feature = "mint")]
Lightning(#[from] crate::lightning::Error),
} }
/// CDK Error Response /// CDK Error Response

View File

@@ -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};

View File

@@ -0,0 +1,77 @@
//! Publishsubscribe 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<Vec<Self::Event>, 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<String> for SubId {
fn from(s: String) -> Self {
Self(s)
}
}
impl FromStr for SubId {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self(s.to_string()))
}
}
impl Deref for SubId {
type Target = String;
fn deref(&self) -> &Self::Target {
&self.0
}
}

View File

@@ -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<SubId>`.
pub type Params = nut17::Params<SubId>;
/// Wrapper around `nut17::Params` to implement `Indexable` for `Notification`.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IndexableParams(Params);
impl From<Params> for IndexableParams {
fn from(params: Params) -> Self {
Self(params)
}
}
impl TryFrom<IndexableParams> for Vec<Index<Notification>> {
type Error = Error;
fn try_from(params: IndexableParams) -> Result<Self, Self::Error> {
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::<Result<_, _>>()
}
}
impl AsRef<SubId> for IndexableParams {
fn as_ref(&self) -> &SubId {
&self.0.id
}
}
impl Indexable for NotificationPayload<Uuid> {
type Type = Notification;
fn to_indexes(&self) -> Vec<Index<Self::Type>> {
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))]
}
}
}
}

View File

@@ -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<SubId>;
pub type WsNotification = nut17::ws::WsNotification<SubId>;
pub type WsSubscribeResponse = nut17::ws::WsSubscribeResponse<SubId>;
pub type WsResponseResult = nut17::ws::WsResponseResult<SubId>;
pub type WsUnsubscribeResponse = nut17::ws::WsUnsubscribeResponse<SubId>;
pub type WsRequest = nut17::ws::WsRequest<SubId>;
pub type WsResponse = nut17::ws::WsResponse<SubId>;
pub type WsMethodRequest = nut17::ws::WsMethodRequest<SubId>;
pub type WsErrorBody = nut17::ws::WsErrorBody;
pub type WsMessageOrResponse = nut17::ws::WsMessageOrResponse<SubId>;
pub type NotificationInner<T> = nut17::ws::NotificationInner<T, SubId>;
#[cfg(feature = "mint")]
pub fn notification_uuid_to_notification_string(
notification: NotificationInner<Uuid>,
) -> NotificationInner<String> {
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<Uuid>) -> 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),
})
}

View File

@@ -11,11 +11,11 @@ use cdk::cdk_database::mint_memory::MintMemoryDatabase;
use cdk::dhke::construct_proofs; use cdk::dhke::construct_proofs;
use cdk::mint::MintQuote; use cdk::mint::MintQuote;
use cdk::nuts::nut00::ProofsMethods; use cdk::nuts::nut00::ProofsMethods;
use cdk::nuts::nut17::Params;
use cdk::nuts::{ use cdk::nuts::{
CurrencyUnit, Id, MintBolt11Request, MintInfo, NotificationPayload, Nuts, PreMintSecrets, CurrencyUnit, Id, MintBolt11Request, MintInfo, NotificationPayload, Nuts, PreMintSecrets,
ProofState, Proofs, SecretKey, SpendingConditions, State, SwapRequest, ProofState, Proofs, SecretKey, SpendingConditions, State, SwapRequest,
}; };
use cdk::subscription::{IndexableParams, Params};
use cdk::types::QuoteTTL; use cdk::types::QuoteTTL;
use cdk::util::unix_time; use cdk::util::unix_time;
use cdk::Mint; use cdk::Mint;
@@ -232,11 +232,14 @@ pub async fn test_p2pk_swap() -> Result<()> {
let mut listener = mint let mut listener = mint
.pubsub_manager .pubsub_manager
.try_subscribe(Params { .try_subscribe::<IndexableParams>(
Params {
kind: cdk::nuts::nut17::Kind::ProofState, kind: cdk::nuts::nut17::Kind::ProofState,
filters: public_keys_to_listen.clone(), filters: public_keys_to_listen.clone(),
id: "test".into(), id: "test".into(),
}) }
.into(),
)
.await .await
.expect("valid subscription"); .expect("valid subscription");

View File

@@ -12,15 +12,18 @@ rust-version = "1.66.0" # MSRV
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features] [features]
default = ["mint", "wallet"] default = ["mint", "wallet"]
mint = ["cdk/mint"] mint = []
wallet = ["cdk/wallet"] wallet = []
[dependencies] [dependencies]
async-trait = "0.1" 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" redb = "2.1.0"
thiserror = "1" 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 = { version = "1", default-features = false, features = ["derive"] }
serde_json = "1" serde_json = "1"
lightning-invoice = { version = "0.32.0", features = ["serde", "std"] } lightning-invoice = { version = "0.32.0", features = ["serde", "std"] }

View File

@@ -33,22 +33,22 @@ pub enum Error {
ParseInt(#[from] ParseIntError), ParseInt(#[from] ParseIntError),
/// CDK Database Error /// CDK Database Error
#[error(transparent)] #[error(transparent)]
CDKDatabase(#[from] cdk::cdk_database::Error), CDKDatabase(#[from] cdk_common::database::Error),
/// CDK Mint Url Error /// CDK Mint Url Error
#[error(transparent)] #[error(transparent)]
CDKMintUrl(#[from] cdk::mint_url::Error), CDKMintUrl(#[from] cdk_common::mint_url::Error),
/// CDK Error /// CDK Error
#[error(transparent)] #[error(transparent)]
CDK(#[from] cdk::error::Error), CDK(#[from] cdk_common::error::Error),
/// NUT00 Error /// NUT00 Error
#[error(transparent)] #[error(transparent)]
CDKNUT00(#[from] cdk::nuts::nut00::Error), CDKNUT00(#[from] cdk_common::nuts::nut00::Error),
/// NUT02 Error /// NUT02 Error
#[error(transparent)] #[error(transparent)]
CDKNUT02(#[from] cdk::nuts::nut02::Error), CDKNUT02(#[from] cdk_common::nuts::nut02::Error),
/// DHKE Error /// DHKE Error
#[error(transparent)] #[error(transparent)]
DHKE(#[from] cdk::dhke::Error), DHKE(#[from] cdk_common::dhke::Error),
/// Unknown Mint Info /// Unknown Mint Info
#[error("Unknown mint info")] #[error("Unknown mint info")]
UnknownMintInfo, UnknownMintInfo,
@@ -60,7 +60,7 @@ pub enum Error {
UnknownDatabaseVersion, UnknownDatabaseVersion,
} }
impl From<Error> for cdk::cdk_database::Error { impl From<Error> for cdk_common::database::Error {
fn from(e: Error) -> Self { fn from(e: Error) -> Self {
Self::Database(Box::new(e)) Self::Database(Box::new(e))
} }

View File

@@ -1,9 +1,8 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use cdk::mint_url::MintUrl; use cdk_common::mint_url::MintUrl;
use cdk::nuts::{CurrencyUnit, MeltQuoteState, MintQuoteState}; use cdk_common::{Amount, CurrencyUnit, MeltQuoteState, MintQuoteState};
use cdk::Amount;
use redb::{Database, ReadableTable, TableDefinition}; use redb::{Database, ReadableTable, TableDefinition};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};

View File

@@ -3,10 +3,9 @@ use std::collections::HashMap;
use std::str::FromStr; use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use cdk::mint::MintQuote; use cdk_common::mint::MintQuote;
use cdk::mint_url::MintUrl; use cdk_common::mint_url::MintUrl;
use cdk::nuts::{CurrencyUnit, MintQuoteState, Proof, State}; use cdk_common::{Amount, CurrencyUnit, MintQuoteState, Proof, State};
use cdk::Amount;
use lightning_invoice::Bolt11Invoice; use lightning_invoice::Bolt11Invoice;
use redb::{ use redb::{
Database, MultimapTableDefinition, ReadableMultimapTable, ReadableTable, TableDefinition, Database, MultimapTableDefinition, ReadableMultimapTable, ReadableTable, TableDefinition,

View File

@@ -7,16 +7,15 @@ use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use async_trait::async_trait; use async_trait::async_trait;
use cdk::cdk_database::MintDatabase; use cdk_common::common::LnKey;
use cdk::dhke::hash_to_curve; use cdk_common::database::{self, MintDatabase};
use cdk::mint::{MintKeySetInfo, MintQuote}; use cdk_common::dhke::hash_to_curve;
use cdk::nuts::nut00::ProofsMethods; use cdk_common::mint::{self, MintKeySetInfo, MintQuote};
use cdk::nuts::{ use cdk_common::nut00::ProofsMethods;
use cdk_common::{
BlindSignature, CurrencyUnit, Id, MeltBolt11Request, MeltQuoteState, MintQuoteState, Proof, BlindSignature, CurrencyUnit, Id, MeltBolt11Request, MeltQuoteState, MintQuoteState, Proof,
Proofs, PublicKey, State, Proofs, PublicKey, State,
}; };
use cdk::types::LnKey;
use cdk::{cdk_database, mint};
use migrations::{migrate_01_to_02, migrate_04_to_05}; use migrations::{migrate_01_to_02, migrate_04_to_05};
use redb::{Database, MultimapTableDefinition, ReadableTable, TableDefinition}; use redb::{Database, MultimapTableDefinition, ReadableTable, TableDefinition};
use uuid::Uuid; use uuid::Uuid;
@@ -162,7 +161,7 @@ impl MintRedbDatabase {
#[async_trait] #[async_trait]
impl MintDatabase for MintRedbDatabase { 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> { async fn set_active_keyset(&self, unit: CurrencyUnit, id: Id) -> Result<(), Self::Err> {
let write_txn = self.db.begin_write().map_err(Error::from)?; let write_txn = self.db.begin_write().map_err(Error::from)?;

View File

@@ -3,7 +3,7 @@ use std::ops::Deref;
use std::str::FromStr; use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use cdk::mint_url::MintUrl; use cdk_common::mint_url::MintUrl;
use redb::{ use redb::{
Database, MultimapTableDefinition, ReadableMultimapTable, ReadableTable, TableDefinition, Database, MultimapTableDefinition, ReadableMultimapTable, ReadableTable, TableDefinition,
}; };

View File

@@ -7,15 +7,14 @@ use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use async_trait::async_trait; use async_trait::async_trait;
use cdk::cdk_database::WalletDatabase; use cdk_common::common::ProofInfo;
use cdk::mint_url::MintUrl; use cdk_common::database::WalletDatabase;
use cdk::nuts::{ use cdk_common::mint_url::MintUrl;
CurrencyUnit, Id, KeySetInfo, Keys, MintInfo, PublicKey, SpendingConditions, State, 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 redb::{Database, MultimapTableDefinition, ReadableTable, TableDefinition};
use tracing::instrument; use tracing::instrument;
@@ -153,7 +152,7 @@ impl WalletRedbDatabase {
&self, &self,
ys: Vec<PublicKey>, ys: Vec<PublicKey>,
state: State, state: State,
) -> Result<(), cdk_database::Error> { ) -> Result<(), database::Error> {
let read_txn = self.db.begin_read().map_err(Error::from)?; let read_txn = self.db.begin_read().map_err(Error::from)?;
let table = read_txn.open_table(PROOFS_TABLE).map_err(Error::from)?; let table = read_txn.open_table(PROOFS_TABLE).map_err(Error::from)?;
@@ -192,7 +191,7 @@ impl WalletRedbDatabase {
#[async_trait] #[async_trait]
impl WalletDatabase for WalletRedbDatabase { impl WalletDatabase for WalletRedbDatabase {
type Err = cdk_database::Error; type Err = database::Error;
#[instrument(skip(self))] #[instrument(skip(self))]
async fn add_mint( async fn add_mint(

View File

@@ -12,21 +12,26 @@ rust-version = "1.66.0" # MSRV
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features] [features]
default = ["mint", "wallet"] default = ["mint", "wallet"]
mint = ["cdk/mint"] mint = []
wallet = ["cdk/wallet"] wallet = []
[dependencies] [dependencies]
async-trait = "0.1" 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 } bitcoin = { version = "0.32.2", default-features = false }
sqlx = { version = "0.6.3", default-features = false, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate", "uuid"] } sqlx = { version = "0.6.3", default-features = false, features = [
thiserror = "1" "runtime-tokio-rustls",
tokio = { version = "1", features = [ "sqlite",
"time",
"macros", "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" serde_json = "1"
lightning-invoice = { version = "0.32.0", features = ["serde", "std"] } lightning-invoice = { version = "0.32.0", features = ["serde", "std"] }
uuid = { version = "1", features = ["v4", "serde"] } uuid = { version = "1", features = ["v4", "serde"] }

View File

@@ -10,31 +10,31 @@ pub enum Error {
SQLX(#[from] sqlx::Error), SQLX(#[from] sqlx::Error),
/// NUT00 Error /// NUT00 Error
#[error(transparent)] #[error(transparent)]
CDKNUT00(#[from] cdk::nuts::nut00::Error), CDKNUT00(#[from] cdk_common::nuts::nut00::Error),
/// NUT01 Error /// NUT01 Error
#[error(transparent)] #[error(transparent)]
CDKNUT01(#[from] cdk::nuts::nut01::Error), CDKNUT01(#[from] cdk_common::nuts::nut01::Error),
/// NUT02 Error /// NUT02 Error
#[error(transparent)] #[error(transparent)]
CDKNUT02(#[from] cdk::nuts::nut02::Error), CDKNUT02(#[from] cdk_common::nuts::nut02::Error),
/// NUT04 Error /// NUT04 Error
#[error(transparent)] #[error(transparent)]
CDKNUT04(#[from] cdk::nuts::nut04::Error), CDKNUT04(#[from] cdk_common::nuts::nut04::Error),
/// NUT05 Error /// NUT05 Error
#[error(transparent)] #[error(transparent)]
CDKNUT05(#[from] cdk::nuts::nut05::Error), CDKNUT05(#[from] cdk_common::nuts::nut05::Error),
/// NUT07 Error /// NUT07 Error
#[error(transparent)] #[error(transparent)]
CDKNUT07(#[from] cdk::nuts::nut07::Error), CDKNUT07(#[from] cdk_common::nuts::nut07::Error),
/// Secret Error /// Secret Error
#[error(transparent)] #[error(transparent)]
CDKSECRET(#[from] cdk::secret::Error), CDKSECRET(#[from] cdk_common::secret::Error),
/// BIP32 Error /// BIP32 Error
#[error(transparent)] #[error(transparent)]
BIP32(#[from] bitcoin::bip32::Error), BIP32(#[from] bitcoin::bip32::Error),
/// Mint Url Error /// Mint Url Error
#[error(transparent)] #[error(transparent)]
MintUrl(#[from] cdk::mint_url::Error), MintUrl(#[from] cdk_common::mint_url::Error),
/// Could Not Initialize Database /// Could Not Initialize Database
#[error("Could not initialize database")] #[error("Could not initialize database")]
CouldNotInitialize, CouldNotInitialize,
@@ -46,7 +46,7 @@ pub enum Error {
Serde(#[from] serde_json::Error), Serde(#[from] serde_json::Error),
} }
impl From<Error> for cdk::cdk_database::Error { impl From<Error> for cdk_common::database::Error {
fn from(e: Error) -> Self { fn from(e: Error) -> Self {
Self::Database(Box::new(e)) Self::Database(Box::new(e))
} }

View File

@@ -7,18 +7,17 @@ use std::time::Duration;
use async_trait::async_trait; use async_trait::async_trait;
use bitcoin::bip32::DerivationPath; use bitcoin::bip32::DerivationPath;
use cdk::cdk_database::{self, MintDatabase}; use cdk_common::common::LnKey;
use cdk::mint::{MintKeySetInfo, MintQuote}; use cdk_common::database::{self, MintDatabase};
use cdk::mint_url::MintUrl; use cdk_common::mint::{self, MintKeySetInfo, MintQuote};
use cdk::nuts::nut00::ProofsMethods; use cdk_common::mint_url::MintUrl;
use cdk::nuts::nut05::QuoteState; use cdk_common::nut00::ProofsMethods;
use cdk::nuts::{ use cdk_common::nut05::QuoteState;
BlindSignature, BlindSignatureDleq, CurrencyUnit, Id, MeltBolt11Request, MeltQuoteState, use cdk_common::secret::Secret;
MintQuoteState, PaymentMethod, Proof, Proofs, PublicKey, SecretKey, State, 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 error::Error;
use lightning_invoice::Bolt11Invoice; use lightning_invoice::Bolt11Invoice;
use sqlx::sqlite::{SqliteConnectOptions, SqlitePool, SqlitePoolOptions, SqliteRow}; use sqlx::sqlite::{SqliteConnectOptions, SqlitePool, SqlitePoolOptions, SqliteRow};
@@ -63,7 +62,7 @@ impl MintSqliteDatabase {
#[async_trait] #[async_trait]
impl MintDatabase for MintSqliteDatabase { 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> { async fn set_active_keyset(&self, unit: CurrencyUnit, id: Id) -> Result<(), Self::Err> {
let mut transaction = self.pool.begin().await.map_err(Error::from)?; let mut transaction = self.pool.begin().await.map_err(Error::from)?;

View File

@@ -13,28 +13,28 @@ pub enum Error {
Serde(#[from] serde_json::Error), Serde(#[from] serde_json::Error),
/// NUT00 Error /// NUT00 Error
#[error(transparent)] #[error(transparent)]
CDKNUT00(#[from] cdk::nuts::nut00::Error), CDKNUT00(#[from] cdk_common::nuts::nut00::Error),
/// NUT01 Error /// NUT01 Error
#[error(transparent)] #[error(transparent)]
CDKNUT01(#[from] cdk::nuts::nut01::Error), CDKNUT01(#[from] cdk_common::nuts::nut01::Error),
/// NUT02 Error /// NUT02 Error
#[error(transparent)] #[error(transparent)]
CDKNUT02(#[from] cdk::nuts::nut02::Error), CDKNUT02(#[from] cdk_common::nuts::nut02::Error),
/// NUT04 Error /// NUT04 Error
#[error(transparent)] #[error(transparent)]
CDKNUT04(#[from] cdk::nuts::nut04::Error), CDKNUT04(#[from] cdk_common::nuts::nut04::Error),
/// NUT05 Error /// NUT05 Error
#[error(transparent)] #[error(transparent)]
CDKNUT05(#[from] cdk::nuts::nut05::Error), CDKNUT05(#[from] cdk_common::nuts::nut05::Error),
/// NUT07 Error /// NUT07 Error
#[error(transparent)] #[error(transparent)]
CDKNUT07(#[from] cdk::nuts::nut07::Error), CDKNUT07(#[from] cdk_common::nuts::nut07::Error),
/// Secret Error /// Secret Error
#[error(transparent)] #[error(transparent)]
CDKSECRET(#[from] cdk::secret::Error), CDKSECRET(#[from] cdk_common::secret::Error),
/// Mint Url /// Mint Url
#[error(transparent)] #[error(transparent)]
MintUrl(#[from] cdk::mint_url::Error), MintUrl(#[from] cdk_common::mint_url::Error),
/// BIP32 Error /// BIP32 Error
#[error(transparent)] #[error(transparent)]
BIP32(#[from] bitcoin::bip32::Error), BIP32(#[from] bitcoin::bip32::Error),
@@ -46,7 +46,7 @@ pub enum Error {
InvalidDbPath, InvalidDbPath,
} }
impl From<Error> for cdk::cdk_database::Error { impl From<Error> for cdk_common::database::Error {
fn from(e: Error) -> Self { fn from(e: Error) -> Self {
Self::Database(Box::new(e)) Self::Database(Box::new(e))
} }

View File

@@ -5,17 +5,16 @@ use std::path::Path;
use std::str::FromStr; use std::str::FromStr;
use async_trait::async_trait; use async_trait::async_trait;
use cdk::amount::Amount; use cdk_common::common::ProofInfo;
use cdk::cdk_database::{self, WalletDatabase}; use cdk_common::database::WalletDatabase;
use cdk::mint_url::MintUrl; use cdk_common::mint_url::MintUrl;
use cdk::nuts::{ use cdk_common::nuts::{MeltQuoteState, MintQuoteState};
CurrencyUnit, Id, KeySetInfo, Keys, MeltQuoteState, MintInfo, MintQuoteState, Proof, PublicKey, use cdk_common::secret::Secret;
SecretKey, SpendingConditions, State, 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 error::Error;
use sqlx::sqlite::{SqliteConnectOptions, SqlitePool, SqliteRow}; use sqlx::sqlite::{SqliteConnectOptions, SqlitePool, SqliteRow};
use sqlx::{ConnectOptions, Row}; use sqlx::{ConnectOptions, Row};
@@ -54,7 +53,7 @@ impl WalletSqliteDatabase {
.expect("Could not run migrations"); .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( sqlx::query(
r#" r#"
UPDATE proof UPDATE proof
@@ -74,7 +73,7 @@ impl WalletSqliteDatabase {
#[async_trait] #[async_trait]
impl WalletDatabase for WalletSqliteDatabase { impl WalletDatabase for WalletSqliteDatabase {
type Err = cdk_database::Error; type Err = database::Error;
#[instrument(skip(self, mint_info))] #[instrument(skip(self, mint_info))]
async fn add_mint( async fn add_mint(

View File

@@ -12,15 +12,17 @@ license = "MIT"
[features] [features]
default = ["mint", "wallet"] default = ["mint", "wallet"]
mint = ["dep:futures"] mint = ["dep:futures", "cdk-common/mint"]
# We do not commit to a MSRV with swagger enabled # We do not commit to a MSRV with swagger enabled
swagger = ["mint", "dep:utoipa"] swagger = ["mint", "dep:utoipa", "cdk-common/swagger"]
wallet = ["dep:reqwest"] wallet = ["dep:reqwest", "cdk-common/wallet"]
bench = [] bench = []
http_subscription = [] http_subscription = []
[dependencies] [dependencies]
cdk-common = { path = "../cdk-common", version = "0.6.0" }
cbor-diag = "0.1.12"
arc-swap = "1.7.1" arc-swap = "1.7.1"
async-trait = "0.1" async-trait = "0.1"
anyhow = { version = "1.0.43", features = ["backtrace"] } anyhow = { version = "1.0.43", features = ["backtrace"] }
@@ -31,16 +33,17 @@ bitcoin = { version = "0.32.2", features = [
"rand-std", "rand-std",
] } ] }
ciborium = { version = "0.2.2", default-features = false, features = ["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"] } lightning-invoice = { version = "0.32.0", features = ["serde", "std"] }
once_cell = "1.19"
regex = "1" regex = "1"
reqwest = { version = "0.12", default-features = false, features = [ reqwest = { version = "0.12", default-features = false, features = [
"json", "json",
"rustls-tls", "rustls-tls",
"rustls-tls-native-roots", "rustls-tls-native-roots",
"socks", "socks",
"zstd", "brotli", "gzip", "deflate" "zstd",
"brotli",
"gzip",
"deflate",
], optional = true } ], optional = true }
serde = { version = "1", default-features = false, features = ["derive"] } serde = { version = "1", default-features = false, features = ["derive"] }
serde_json = "1" serde_json = "1"
@@ -77,7 +80,6 @@ tokio-tungstenite = { version = "0.19.0", features = [
[target.'cfg(target_arch = "wasm32")'.dependencies] [target.'cfg(target_arch = "wasm32")'.dependencies]
tokio = { version = "1.21", features = ["rt", "macros", "sync", "time"] } tokio = { version = "1.21", features = ["rt", "macros", "sync", "time"] }
getrandom = { version = "0.2", features = ["js"] } getrandom = { version = "0.2", features = ["js"] }
instant = { version = "0.1", features = ["wasm-bindgen", "inaccurate"] }
[[example]] [[example]]
name = "mint-token" name = "mint-token"

View File

@@ -4,13 +4,14 @@ use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use async_trait::async_trait; 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 tokio::sync::{Mutex, RwLock};
use uuid::Uuid; use uuid::Uuid;
use super::{Error, MintDatabase};
use crate::dhke::hash_to_curve; use crate::dhke::hash_to_curve;
use crate::mint::{self, MintKeySetInfo, MintQuote}; use crate::mint::{self, MintQuote};
use crate::nuts::nut00::ProofsMethods;
use crate::nuts::nut07::State; use crate::nuts::nut07::State;
use crate::nuts::{ use crate::nuts::{
nut07, BlindSignature, CurrencyUnit, Id, MeltBolt11Request, MeltQuoteState, MintQuoteState, nut07, BlindSignature, CurrencyUnit, Id, MeltBolt11Request, MeltQuoteState, MintQuoteState,

View File

@@ -1,290 +1,11 @@
//! CDK Database //! 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")] #[cfg(feature = "mint")]
pub mod mint_memory; pub mod mint_memory;
#[cfg(feature = "wallet")] #[cfg(feature = "wallet")]
pub mod wallet_memory; pub mod wallet_memory;
/// re-export types
pub use cdk_common::database::{Error, MintDatabase, WalletDatabase};
#[cfg(feature = "wallet")] #[cfg(feature = "wallet")]
pub use wallet_memory::WalletMemoryDatabase; pub use wallet_memory::WalletMemoryDatabase;
/// CDK_database error
#[derive(Debug, Error)]
pub enum Error {
/// Database Error
#[error(transparent)]
Database(Box<dyn std::error::Error + Send + Sync>),
/// 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<Error> + From<Error>;
/// Add Mint to storage
async fn add_mint(
&self,
mint_url: MintUrl,
mint_info: Option<MintInfo>,
) -> 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<Option<MintInfo>, Self::Err>;
/// Get all mints from storage
async fn get_mints(&self) -> Result<HashMap<MintUrl, Option<MintInfo>>, 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<KeySetInfo>,
) -> Result<(), Self::Err>;
/// Get mint keysets for mint url
async fn get_mint_keysets(
&self,
mint_url: MintUrl,
) -> Result<Option<Vec<KeySetInfo>>, Self::Err>;
/// Get mint keyset by id
async fn get_keyset_by_id(&self, keyset_id: &Id) -> Result<Option<KeySetInfo>, 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<Option<WalletMintQuote>, Self::Err>;
/// Get mint quotes from storage
async fn get_mint_quotes(&self) -> Result<Vec<WalletMintQuote>, 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<Option<wallet::MeltQuote>, 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<Option<Keys>, 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<ProofInfo>,
removed_ys: Vec<PublicKey>,
) -> Result<(), Self::Err>;
/// Set proofs as pending in storage. Proofs are identified by their Y
/// value.
async fn set_pending_proofs(&self, ys: Vec<PublicKey>) -> Result<(), Self::Err>;
/// Reserve proofs in storage. Proofs are identified by their Y value.
async fn reserve_proofs(&self, ys: Vec<PublicKey>) -> Result<(), Self::Err>;
/// Set proofs as unspent in storage. Proofs are identified by their Y
/// value.
async fn set_unspent_proofs(&self, ys: Vec<PublicKey>) -> Result<(), Self::Err>;
/// Get proofs from storage
async fn get_proofs(
&self,
mint_url: Option<MintUrl>,
unit: Option<CurrencyUnit>,
state: Option<Vec<State>>,
spending_conditions: Option<Vec<SpendingConditions>>,
) -> Result<Vec<ProofInfo>, 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<Option<u32>, Self::Err>;
/// Get when nostr key was last checked
async fn get_nostr_last_checked(
&self,
verifying_key: &PublicKey,
) -> Result<Option<u32>, 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<Error> + From<Error>;
/// 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<Option<Id>, Self::Err>;
/// Get all Active Keyset
async fn get_active_keysets(&self) -> Result<HashMap<CurrencyUnit, Id>, 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<Option<MintMintQuote>, Self::Err>;
/// Update state of [`MintMintQuote`]
async fn update_mint_quote_state(
&self,
quote_id: &Uuid,
state: MintQuoteState,
) -> Result<MintQuoteState, Self::Err>;
/// Get all [`MintMintQuote`]s
async fn get_mint_quote_by_request(
&self,
request: &str,
) -> Result<Option<MintMintQuote>, Self::Err>;
/// Get all [`MintMintQuote`]s
async fn get_mint_quote_by_request_lookup_id(
&self,
request_lookup_id: &str,
) -> Result<Option<MintMintQuote>, Self::Err>;
/// Get Mint Quotes
async fn get_mint_quotes(&self) -> Result<Vec<MintMintQuote>, 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<Option<mint::MeltQuote>, Self::Err>;
/// Update [`mint::MeltQuote`] state
async fn update_melt_quote_state(
&self,
quote_id: &Uuid,
state: MeltQuoteState,
) -> Result<MeltQuoteState, Self::Err>;
/// Get all [`mint::MeltQuote`]s
async fn get_melt_quotes(&self) -> Result<Vec<mint::MeltQuote>, 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<Uuid>,
ln_key: LnKey,
) -> Result<(), Self::Err>;
/// Get melt request
async fn get_melt_request(
&self,
quote_id: &Uuid,
) -> Result<Option<(MeltBolt11Request<Uuid>, 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<Option<MintKeySetInfo>, Self::Err>;
/// Get [`MintKeySetInfo`]s
async fn get_keyset_infos(&self) -> Result<Vec<MintKeySetInfo>, Self::Err>;
/// Add spent [`Proofs`]
async fn add_proofs(&self, proof: Proofs, quote_id: Option<Uuid>) -> Result<(), Self::Err>;
/// Get [`Proofs`] by ys
async fn get_proofs_by_ys(&self, ys: &[PublicKey]) -> Result<Vec<Option<Proof>>, Self::Err>;
/// Get ys by quote id
async fn get_proof_ys_by_quote_id(&self, quote_id: &Uuid) -> Result<Vec<PublicKey>, Self::Err>;
/// Get [`Proofs`] state
async fn get_proofs_states(&self, ys: &[PublicKey]) -> Result<Vec<Option<State>>, Self::Err>;
/// Get [`Proofs`] state
async fn update_proofs_states(
&self,
ys: &[PublicKey],
proofs_state: State,
) -> Result<Vec<Option<State>>, Self::Err>;
/// Get [`Proofs`] by state
async fn get_proofs_by_keyset_id(
&self,
keyset_id: &Id,
) -> Result<(Proofs, Vec<Option<State>>), Self::Err>;
/// Add [`BlindSignature`]
async fn add_blind_signatures(
&self,
blinded_messages: &[PublicKey],
blind_signatures: &[BlindSignature],
quote_id: Option<Uuid>,
) -> Result<(), Self::Err>;
/// Get [`BlindSignature`]s
async fn get_blind_signatures(
&self,
blinded_messages: &[PublicKey],
) -> Result<Vec<Option<BlindSignature>>, Self::Err>;
/// Get [`BlindSignature`]s for keyset_id
async fn get_blind_signatures_for_keyset(
&self,
keyset_id: &Id,
) -> Result<Vec<BlindSignature>, Self::Err>;
/// Get [`BlindSignature`]s for quote
async fn get_blind_signatures_for_quote(
&self,
quote_id: &Uuid,
) -> Result<Vec<BlindSignature>, Self::Err>;
}

View File

@@ -4,10 +4,9 @@ use std::collections::{HashMap, HashSet};
use std::sync::Arc; use std::sync::Arc;
use async_trait::async_trait; use async_trait::async_trait;
use cdk_common::database::{Error, WalletDatabase};
use tokio::sync::RwLock; use tokio::sync::RwLock;
use super::WalletDatabase;
use crate::cdk_database::Error;
use crate::mint_url::MintUrl; use crate::mint_url::MintUrl;
use crate::nuts::{ use crate::nuts::{
CurrencyUnit, Id, KeySetInfo, Keys, MintInfo, PublicKey, SpendingConditions, State, CurrencyUnit, Id, KeySetInfo, Keys, MintInfo, PublicKey, SpendingConditions, State,

View File

@@ -6,9 +6,8 @@ use std::collections::HashMap;
use tracing::instrument; use tracing::instrument;
use crate::error::Error;
use crate::nuts::Id; use crate::nuts::Id;
use crate::Amount; use crate::{Amount, Error};
/// Fee required for proof set /// Fee required for proof set
#[instrument(skip_all)] #[instrument(skip_all)]

View File

@@ -3,33 +3,29 @@
#![warn(missing_docs)] #![warn(missing_docs)]
#![warn(rustdoc::bare_urls)] #![warn(rustdoc::bare_urls)]
pub mod amount;
#[cfg(any(feature = "wallet", feature = "mint"))] #[cfg(any(feature = "wallet", feature = "mint"))]
pub mod cdk_database; pub mod cdk_database;
#[cfg(feature = "mint")]
pub mod cdk_lightning;
pub mod dhke;
pub mod error;
#[cfg(feature = "mint")] #[cfg(feature = "mint")]
pub mod mint; pub mod mint;
pub mod mint_url;
pub mod nuts;
pub mod secret;
pub mod types;
pub mod util;
#[cfg(feature = "wallet")] #[cfg(feature = "wallet")]
pub mod wallet; pub mod wallet;
pub mod pub_sub; 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; pub mod fees;
#[doc(hidden)] #[doc(hidden)]
pub use bitcoin::secp256k1; pub use bitcoin::secp256k1;
#[doc(hidden)]
pub use error::Error;
#[doc(hidden)]
pub use lightning_invoice::{self, Bolt11Invoice};
#[cfg(feature = "mint")] #[cfg(feature = "mint")]
#[doc(hidden)] #[doc(hidden)]
pub use mint::Mint; pub use mint::Mint;
@@ -37,8 +33,6 @@ pub use mint::Mint;
#[doc(hidden)] #[doc(hidden)]
pub use wallet::{Wallet, WalletSubscription}; pub use wallet::{Wallet, WalletSubscription};
#[doc(hidden)]
pub use self::amount::Amount;
#[doc(hidden)] #[doc(hidden)]
pub use self::util::SECP256K1; pub use self::util::SECP256K1;
#[cfg(feature = "wallet")] #[cfg(feature = "wallet")]

View File

@@ -4,12 +4,12 @@ use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use anyhow::anyhow; use anyhow::anyhow;
use cdk_common::database::{self, MintDatabase};
use super::nut17::SupportedMethods; use super::nut17::SupportedMethods;
use super::nut19::{self, CachedEndpoint}; use super::nut19::{self, CachedEndpoint};
use super::Nuts; use super::Nuts;
use crate::amount::Amount; use crate::amount::Amount;
use crate::cdk_database::{self, MintDatabase};
use crate::cdk_lightning::{self, MintLightning}; use crate::cdk_lightning::{self, MintLightning};
use crate::mint::Mint; use crate::mint::Mint;
use crate::nuts::{ use crate::nuts::{
@@ -26,7 +26,7 @@ pub struct MintBuilder {
/// Mint Info /// Mint Info
mint_info: MintInfo, mint_info: MintInfo,
/// Mint Storage backend /// Mint Storage backend
localstore: Option<Arc<dyn MintDatabase<Err = cdk_database::Error> + Send + Sync>>, localstore: Option<Arc<dyn MintDatabase<Err = database::Error> + Send + Sync>>,
/// Ln backends for mint /// Ln backends for mint
ln: Option<HashMap<LnKey, Arc<dyn MintLightning<Err = cdk_lightning::Error> + Send + Sync>>>, ln: Option<HashMap<LnKey, Arc<dyn MintLightning<Err = cdk_lightning::Error> + Send + Sync>>>,
seed: Option<Vec<u8>>, seed: Option<Vec<u8>>,
@@ -57,7 +57,7 @@ impl MintBuilder {
/// Set localstore /// Set localstore
pub fn with_localstore( pub fn with_localstore(
mut self, mut self,
localstore: Arc<dyn MintDatabase<Err = cdk_database::Error> + Send + Sync>, localstore: Arc<dyn MintDatabase<Err = database::Error> + Send + Sync>,
) -> MintBuilder { ) -> MintBuilder {
self.localstore = Some(localstore); self.localstore = Some(localstore);
self self
@@ -149,8 +149,7 @@ impl MintBuilder {
self.mint_info.nuts.nut15 = mpp; self.mint_info.nuts.nut15 = mpp;
} }
match method { if method == PaymentMethod::Bolt11 {
PaymentMethod::Bolt11 => {
let mint_method_settings = MintMethodSettings { let mint_method_settings = MintMethodSettings {
method, method,
unit: unit.clone(), unit: unit.clone(),
@@ -171,7 +170,6 @@ impl MintBuilder {
self.mint_info.nuts.nut05.methods.push(melt_method_settings); self.mint_info.nuts.nut05.methods.push(melt_method_settings);
self.mint_info.nuts.nut05.disabled = false; self.mint_info.nuts.nut05.disabled = false;
} }
}
ln.insert(ln_key.clone(), ln_backend); ln.insert(ln_key.clone(), ln_backend);

View File

@@ -2,6 +2,7 @@ use std::collections::HashSet;
use std::str::FromStr; use std::str::FromStr;
use anyhow::bail; use anyhow::bail;
use cdk_common::nut00::ProofsMethods;
use lightning_invoice::Bolt11Invoice; use lightning_invoice::Bolt11Invoice;
use tracing::instrument; use tracing::instrument;
use uuid::Uuid; use uuid::Uuid;
@@ -13,7 +14,6 @@ use super::{
use crate::amount::to_unit; use crate::amount::to_unit;
use crate::cdk_lightning::{MintLightning, PayInvoiceResponse}; use crate::cdk_lightning::{MintLightning, PayInvoiceResponse};
use crate::mint::SigFlag; use crate::mint::SigFlag;
use crate::nuts::nut00::ProofsMethods;
use crate::nuts::nut11::{enforce_sig_flag, EnforceSigFlag}; use crate::nuts::nut11::{enforce_sig_flag, EnforceSigFlag};
use crate::nuts::{Id, MeltQuoteState}; use crate::nuts::{Id, MeltQuoteState};
use crate::types::LnKey; use crate::types::LnKey;

View File

@@ -6,22 +6,24 @@ use std::sync::Arc;
use bitcoin::bip32::{ChildNumber, DerivationPath, Xpriv}; use bitcoin::bip32::{ChildNumber, DerivationPath, Xpriv};
use bitcoin::secp256k1::{self, Secp256k1}; 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 config::SwappableConfig;
use futures::StreamExt; use futures::StreamExt;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use subscription::PubSubManager;
use tokio::sync::Notify; use tokio::sync::Notify;
use tokio::task::JoinSet; use tokio::task::JoinSet;
use tracing::instrument; use tracing::instrument;
use uuid::Uuid; use uuid::Uuid;
use crate::cdk_database::{self, MintDatabase};
use crate::cdk_lightning::{self, MintLightning}; use crate::cdk_lightning::{self, MintLightning};
use crate::dhke::{sign_message, verify_message}; use crate::dhke::{sign_message, verify_message};
use crate::error::Error; use crate::error::Error;
use crate::fees::calculate_fee; use crate::fees::calculate_fee;
use crate::mint_url::MintUrl; use crate::mint_url::MintUrl;
use crate::nuts::*; use crate::nuts::*;
use crate::types::{LnKey, QuoteTTL};
use crate::util::unix_time; use crate::util::unix_time;
use crate::Amount; use crate::Amount;
@@ -33,11 +35,11 @@ mod keysets;
mod melt; mod melt;
mod mint_nut04; mod mint_nut04;
mod start_up_check; mod start_up_check;
pub mod subscription;
mod swap; mod swap;
pub mod types;
pub use builder::{MintBuilder, MintMeltLimits}; pub use builder::{MintBuilder, MintMeltLimits};
pub use types::{MeltQuote, MintQuote}; pub use cdk_common::mint::{MeltQuote, MintQuote};
/// Cashu Mint /// Cashu Mint
#[derive(Clone)] #[derive(Clone)]
@@ -45,7 +47,7 @@ pub struct Mint {
/// Mint Config /// Mint Config
pub config: SwappableConfig, pub config: SwappableConfig,
/// Mint Storage backend /// Mint Storage backend
pub localstore: Arc<dyn MintDatabase<Err = cdk_database::Error> + Send + Sync>, pub localstore: Arc<dyn MintDatabase<Err = database::Error> + Send + Sync>,
/// Ln backends for mint /// Ln backends for mint
pub ln: HashMap<LnKey, Arc<dyn MintLightning<Err = cdk_lightning::Error> + Send + Sync>>, pub ln: HashMap<LnKey, Arc<dyn MintLightning<Err = cdk_lightning::Error> + Send + Sync>>,
/// Subscription manager /// Subscription manager
@@ -62,7 +64,7 @@ impl Mint {
seed: &[u8], seed: &[u8],
mint_info: MintInfo, mint_info: MintInfo,
quote_ttl: QuoteTTL, quote_ttl: QuoteTTL,
localstore: Arc<dyn MintDatabase<Err = cdk_database::Error> + Send + Sync>, localstore: Arc<dyn MintDatabase<Err = database::Error> + Send + Sync>,
ln: HashMap<LnKey, Arc<dyn MintLightning<Err = cdk_lightning::Error> + Send + Sync>>, ln: HashMap<LnKey, Arc<dyn MintLightning<Err = cdk_lightning::Error> + Send + Sync>>,
// Hashmap where the key is the unit and value is (input fee ppk, max_order) // Hashmap where the key is the unit and value is (input fee ppk, max_order)
supported_units: HashMap<CurrencyUnit, (u64, u8)>, supported_units: HashMap<CurrencyUnit, (u64, u8)>,
@@ -516,47 +518,6 @@ pub struct FeeReserve {
pub percent_fee_reserve: f32, 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<u64>,
/// [`DerivationPath`] keyset
pub derivation_path: DerivationPath,
/// DerivationPath index of Keyset
pub derivation_path_index: Option<u32>,
/// 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<MintKeySetInfo> 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 /// Generate new [`MintKeySetInfo`] from path
#[instrument(skip_all)] #[instrument(skip_all)]
fn create_new_keyset<C: secp256k1::Signing>( fn create_new_keyset<C: secp256k1::Signing>(
@@ -605,11 +566,11 @@ mod tests {
use std::collections::HashSet; use std::collections::HashSet;
use bitcoin::Network; use bitcoin::Network;
use cdk_common::common::{LnKey, QuoteTTL};
use secp256k1::Secp256k1; use secp256k1::Secp256k1;
use uuid::Uuid; use uuid::Uuid;
use super::*; use super::*;
use crate::types::LnKey;
#[test] #[test]
fn mint_mod_generate_keyset_from_seed() { fn mint_mod_generate_keyset_from_seed() {
@@ -697,7 +658,7 @@ mod tests {
assert_eq!(amounts_and_pubkeys, expected_amounts_and_pubkeys); assert_eq!(amounts_and_pubkeys, expected_amounts_and_pubkeys);
} }
use cdk_database::mint_memory::MintMemoryDatabase; use crate::cdk_database::mint_memory::MintMemoryDatabase;
#[derive(Default)] #[derive(Default)]
struct MintConfig<'a> { struct MintConfig<'a> {

View File

@@ -2,10 +2,12 @@
use std::ops::Deref; use std::ops::Deref;
use std::sync::Arc; use std::sync::Arc;
use cdk_common::database::{self, MintDatabase};
use cdk_common::nut17::Notification;
use cdk_common::NotificationPayload;
use uuid::Uuid; use uuid::Uuid;
use super::{Notification, NotificationPayload, OnSubscription}; use super::OnSubscription;
use crate::cdk_database::{self, MintDatabase};
use crate::nuts::{ use crate::nuts::{
BlindSignature, MeltQuoteBolt11Response, MeltQuoteState, MintQuoteBolt11Response, BlindSignature, MeltQuoteBolt11Response, MeltQuoteState, MintQuoteBolt11Response,
MintQuoteState, ProofState, MintQuoteState, ProofState,
@@ -26,8 +28,8 @@ impl Default for PubSubManager {
} }
} }
impl From<Arc<dyn MintDatabase<Err = cdk_database::Error> + Send + Sync>> for PubSubManager { impl From<Arc<dyn MintDatabase<Err = database::Error> + Send + Sync>> for PubSubManager {
fn from(val: Arc<dyn MintDatabase<Err = cdk_database::Error> + Send + Sync>) -> Self { fn from(val: Arc<dyn MintDatabase<Err = database::Error> + Send + Sync>) -> Self {
PubSubManager(OnSubscription(Some(val)).into()) PubSubManager(OnSubscription(Some(val)).into())
} }
} }
@@ -82,19 +84,21 @@ mod test {
use tokio::time::sleep; use tokio::time::sleep;
use super::*; use super::*;
use crate::nuts::nut17::{Kind, Params}; use crate::nuts::nut17::Kind;
use crate::nuts::{PublicKey, State}; use crate::nuts::{PublicKey, State};
use crate::subscription::{IndexableParams, Params};
#[tokio::test] #[tokio::test]
async fn active_and_drop() { async fn active_and_drop() {
let manager = PubSubManager::default(); let manager = PubSubManager::default();
let params = Params { let params: IndexableParams = Params {
kind: Kind::ProofState, kind: Kind::ProofState,
filters: vec![ filters: vec![
"02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2".to_owned(), "02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2".to_owned(),
], ],
id: "uno".into(), id: "uno".into(),
}; }
.into();
// Although the same param is used, two subscriptions are created, that // Although the same param is used, two subscriptions are created, that
// is because each index is unique, thanks to `Unique`, it is the // is because each index is unique, thanks to `Unique`, it is the
@@ -123,25 +127,31 @@ mod test {
let manager = PubSubManager::default(); let manager = PubSubManager::default();
let mut subscriptions = [ let mut subscriptions = [
manager manager
.try_subscribe(Params { .try_subscribe::<IndexableParams>(
Params {
kind: Kind::ProofState, kind: Kind::ProofState,
filters: vec![ filters: vec![
"02194603ffa36356f4a56b7df9371fc3192472351453ec7398b8da8117e7c3e104" "02194603ffa36356f4a56b7df9371fc3192472351453ec7398b8da8117e7c3e104"
.to_string(), .to_string(),
], ],
id: "uno".into(), id: "uno".into(),
}) }
.into(),
)
.await .await
.expect("valid subscription"), .expect("valid subscription"),
manager manager
.try_subscribe(Params { .try_subscribe::<IndexableParams>(
Params {
kind: Kind::ProofState, kind: Kind::ProofState,
filters: vec![ filters: vec![
"02194603ffa36356f4a56b7df9371fc3192472351453ec7398b8da8117e7c3e104" "02194603ffa36356f4a56b7df9371fc3192472351453ec7398b8da8117e7c3e104"
.to_string(), .to_string(),
], ],
id: "dos".into(), id: "dos".into(),
}) }
.into(),
)
.await .await
.expect("valid subscription"), .expect("valid subscription"),
]; ];
@@ -182,7 +192,7 @@ mod test {
async fn json_test() { async fn json_test() {
let manager = PubSubManager::default(); let manager = PubSubManager::default();
let mut subscription = manager let mut subscription = manager
.try_subscribe::<Params>( .try_subscribe::<IndexableParams>(
serde_json::from_str(r#"{"kind":"proof_state","filters":["02194603ffa36356f4a56b7df9371fc3192472351453ec7398b8da8117e7c3e104"],"subId":"uno"}"#) serde_json::from_str(r#"{"kind":"proof_state","filters":["02194603ffa36356f4a56b7df9371fc3192472351453ec7398b8da8117e7c3e104"],"subId":"uno"}"#)
.expect("valid json"), .expect("valid json"),
) )

View File

@@ -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;

View File

@@ -3,12 +3,13 @@
//! This module contains the code that is triggered when a new subscription is created. //! This module contains the code that is triggered when a new subscription is created.
use std::sync::Arc; 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 uuid::Uuid;
use super::{Notification, NotificationPayload};
use crate::cdk_database::{self, MintDatabase};
use crate::nuts::{MeltQuoteBolt11Response, MintQuoteBolt11Response, ProofState, PublicKey}; use crate::nuts::{MeltQuoteBolt11Response, MintQuoteBolt11Response, ProofState, PublicKey};
use crate::pub_sub::OnNewSubscription;
#[derive(Default)] #[derive(Default)]
/// Subscription Init /// 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. /// It is used to send the initial state of the subscription to the client.
pub struct OnSubscription( pub struct OnSubscription(
pub(crate) Option<Arc<dyn MintDatabase<Err = cdk_database::Error> + Send + Sync>>, pub(crate) Option<Arc<dyn MintDatabase<Err = database::Error> + Send + Sync>>,
); );
#[async_trait::async_trait] #[async_trait::async_trait]

View File

@@ -12,18 +12,15 @@ use std::cmp::Ordering;
use std::collections::{BTreeMap, HashSet}; use std::collections::{BTreeMap, HashSet};
use std::fmt::Debug; use std::fmt::Debug;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::str::FromStr;
use std::sync::atomic::{self, AtomicUsize}; use std::sync::atomic::{self, AtomicUsize};
use std::sync::Arc; 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::sync::{mpsc, RwLock};
use tokio::task::JoinHandle; use tokio::task::JoinHandle;
mod index;
pub use index::{Index, Indexable, SubscriptionGlobalId};
type IndexTree<T, I> = Arc<RwLock<BTreeMap<Index<I>, mpsc::Sender<(SubId, T)>>>>; type IndexTree<T, I> = Arc<RwLock<BTreeMap<Index<I>, mpsc::Sender<(SubId, T)>>>>;
/// Default size of the remove channel /// Default size of the remove channel
@@ -32,25 +29,6 @@ pub const DEFAULT_REMOVE_SIZE: usize = 10_000;
/// Default channel size for subscription buffering /// Default channel size for subscription buffering
pub const DEFAULT_CHANNEL_SIZE: usize = 10; 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<Vec<Self::Event>, String>;
}
/// Subscription manager /// Subscription manager
/// ///
/// This object keep track of all subscription listener and it is also /// 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<String> for SubId {
fn from(s: String) -> Self {
Self(s)
}
}
impl FromStr for SubId {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self(s.to_string()))
}
}
impl Deref for SubId {
type Target = String;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use tokio::sync::mpsc; use tokio::sync::mpsc;

View File

@@ -3,7 +3,9 @@
use std::fmt::Debug; use std::fmt::Debug;
use async_trait::async_trait; use async_trait::async_trait;
use reqwest::Client; use reqwest::{Client, IntoUrl};
use serde::de::DeserializeOwned;
use serde::Serialize;
use tracing::instrument; use tracing::instrument;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use url::Url; use url::Url;
@@ -18,18 +20,6 @@ use crate::nuts::{
RestoreResponse, SwapRequest, SwapResponse, 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) => <ErrorResponse as Into<Error>>::into(ok),
Err(err) => err.into(),
}
})
};
}
/// Http Client /// Http Client
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct HttpClient { pub struct HttpClient {
@@ -46,6 +36,53 @@ impl HttpClient {
} }
} }
#[inline]
async fn http_get<U: IntoUrl, R: DeserializeOwned>(&self, url: U) -> Result<R, Error> {
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::<R>(&response).map_err(|err| {
tracing::warn!("Http Response error: {}", err);
match ErrorResponse::from_json(&response) {
Ok(ok) => <ErrorResponse as Into<Error>>::into(ok),
Err(err) => err.into(),
}
})
}
#[inline]
async fn http_post<U: IntoUrl, P: Serialize + ?Sized, R: DeserializeOwned>(
&self,
url: U,
payload: &P,
) -> Result<R, Error> {
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::<R>(&response).map_err(|err| {
tracing::warn!("Http Response error: {}", err);
match ErrorResponse::from_json(&response) {
Ok(ok) => <ErrorResponse as Into<Error>>::into(ok),
Err(err) => err.into(),
}
})
}
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
/// Create new [`HttpClient`] with a proxy for specific TLDs. /// Create new [`HttpClient`] with a proxy for specific TLDs.
/// Specifying `None` for `host_matcher` will use the proxy for all /// Specifying `None` for `host_matcher` will use the proxy for all
@@ -72,7 +109,8 @@ impl HttpClient {
None None
})) }))
.danger_accept_invalid_certs(accept_invalid_certs) // Allow self-signed certs .danger_accept_invalid_certs(accept_invalid_certs) // Allow self-signed certs
.build()?; .build()
.map_err(|e| Error::HttpError(e.to_string()))?;
Ok(Self { Ok(Self {
inner: client, inner: client,
@@ -88,9 +126,7 @@ impl MintConnector for HttpClient {
#[instrument(skip(self), fields(mint_url = %self.mint_url))] #[instrument(skip(self), fields(mint_url = %self.mint_url))]
async fn get_mint_keys(&self) -> Result<Vec<KeySet>, Error> { async fn get_mint_keys(&self) -> Result<Vec<KeySet>, Error> {
let url = self.mint_url.join_paths(&["v1", "keys"])?; let url = self.mint_url.join_paths(&["v1", "keys"])?;
let keys = self.inner.get(url).send().await?.text().await?; Ok(self.http_get::<_, KeysResponse>(url).await?.keysets)
Ok(convert_http_response!(KeysResponse, keys)?.keysets)
} }
/// Get Keyset Keys [NUT-01] /// Get Keyset Keys [NUT-01]
@@ -99,9 +135,8 @@ impl MintConnector for HttpClient {
let url = self let url = self
.mint_url .mint_url
.join_paths(&["v1", "keys", &keyset_id.to_string()])?; .join_paths(&["v1", "keys", &keyset_id.to_string()])?;
let keys = self.inner.get(url).send().await?.text().await?; self.http_get::<_, KeysResponse>(url)
.await?
convert_http_response!(KeysResponse, keys)?
.keysets .keysets
.drain(0..1) .drain(0..1)
.next() .next()
@@ -112,9 +147,7 @@ impl MintConnector for HttpClient {
#[instrument(skip(self), fields(mint_url = %self.mint_url))] #[instrument(skip(self), fields(mint_url = %self.mint_url))]
async fn get_mint_keysets(&self) -> Result<KeysetResponse, Error> { async fn get_mint_keysets(&self) -> Result<KeysetResponse, Error> {
let url = self.mint_url.join_paths(&["v1", "keysets"])?; let url = self.mint_url.join_paths(&["v1", "keysets"])?;
let res = self.inner.get(url).send().await?.text().await?; self.http_get(url).await
convert_http_response!(KeysetResponse, res)
} }
/// Mint Quote [NUT-04] /// Mint Quote [NUT-04]
@@ -126,17 +159,7 @@ impl MintConnector for HttpClient {
let url = self let url = self
.mint_url .mint_url
.join_paths(&["v1", "mint", "quote", "bolt11"])?; .join_paths(&["v1", "mint", "quote", "bolt11"])?;
self.http_post(url, &request).await
let res = self
.inner
.post(url)
.json(&request)
.send()
.await?
.text()
.await?;
convert_http_response!(MintQuoteBolt11Response<String>, res)
} }
/// Mint Quote status /// Mint Quote status
@@ -149,9 +172,7 @@ impl MintConnector for HttpClient {
.mint_url .mint_url
.join_paths(&["v1", "mint", "quote", "bolt11", quote_id])?; .join_paths(&["v1", "mint", "quote", "bolt11", quote_id])?;
let res = self.inner.get(url).send().await?.text().await?; self.http_get(url).await
convert_http_response!(MintQuoteBolt11Response<String>, res)
} }
/// Mint Tokens [NUT-04] /// Mint Tokens [NUT-04]
@@ -161,17 +182,7 @@ impl MintConnector for HttpClient {
request: MintBolt11Request<String>, request: MintBolt11Request<String>,
) -> Result<MintBolt11Response, Error> { ) -> Result<MintBolt11Response, Error> {
let url = self.mint_url.join_paths(&["v1", "mint", "bolt11"])?; let url = self.mint_url.join_paths(&["v1", "mint", "bolt11"])?;
self.http_post(url, &request).await
let res = self
.inner
.post(url)
.json(&request)
.send()
.await?
.text()
.await?;
convert_http_response!(MintBolt11Response, res)
} }
/// Melt Quote [NUT-05] /// Melt Quote [NUT-05]
@@ -183,17 +194,7 @@ impl MintConnector for HttpClient {
let url = self let url = self
.mint_url .mint_url
.join_paths(&["v1", "melt", "quote", "bolt11"])?; .join_paths(&["v1", "melt", "quote", "bolt11"])?;
self.http_post(url, &request).await
let res = self
.inner
.post(url)
.json(&request)
.send()
.await?
.text()
.await?;
convert_http_response!(MeltQuoteBolt11Response<String>, res)
} }
/// Melt Quote Status /// Melt Quote Status
@@ -206,9 +207,7 @@ impl MintConnector for HttpClient {
.mint_url .mint_url
.join_paths(&["v1", "melt", "quote", "bolt11", quote_id])?; .join_paths(&["v1", "melt", "quote", "bolt11", quote_id])?;
let res = self.inner.get(url).send().await?.text().await?; self.http_get(url).await
convert_http_response!(MeltQuoteBolt11Response<String>, res)
} }
/// Melt [NUT-05] /// Melt [NUT-05]
@@ -219,44 +218,21 @@ impl MintConnector for HttpClient {
request: MeltBolt11Request<String>, request: MeltBolt11Request<String>,
) -> Result<MeltQuoteBolt11Response<String>, Error> { ) -> Result<MeltQuoteBolt11Response<String>, Error> {
let url = self.mint_url.join_paths(&["v1", "melt", "bolt11"])?; let url = self.mint_url.join_paths(&["v1", "melt", "bolt11"])?;
self.http_post(url, &request).await
let res = self
.inner
.post(url)
.json(&request)
.send()
.await?
.text()
.await?;
convert_http_response!(MeltQuoteBolt11Response<String>, res)
} }
/// Swap Token [NUT-03] /// Swap Token [NUT-03]
#[instrument(skip(self, swap_request), fields(mint_url = %self.mint_url))] #[instrument(skip(self, swap_request), fields(mint_url = %self.mint_url))]
async fn post_swap(&self, swap_request: SwapRequest) -> Result<SwapResponse, Error> { async fn post_swap(&self, swap_request: SwapRequest) -> Result<SwapResponse, Error> {
let url = self.mint_url.join_paths(&["v1", "swap"])?; let url = self.mint_url.join_paths(&["v1", "swap"])?;
self.http_post(url, &swap_request).await
let res = self
.inner
.post(url)
.json(&swap_request)
.send()
.await?
.text()
.await?;
convert_http_response!(SwapResponse, res)
} }
/// Get Mint Info [NUT-06] /// Get Mint Info [NUT-06]
#[instrument(skip(self), fields(mint_url = %self.mint_url))] #[instrument(skip(self), fields(mint_url = %self.mint_url))]
async fn get_mint_info(&self) -> Result<MintInfo, Error> { async fn get_mint_info(&self) -> Result<MintInfo, Error> {
let url = self.mint_url.join_paths(&["v1", "info"])?; let url = self.mint_url.join_paths(&["v1", "info"])?;
self.http_get(url).await
let res = self.inner.get(url).send().await?.text().await?;
convert_http_response!(MintInfo, res)
} }
/// Spendable check [NUT-07] /// Spendable check [NUT-07]
@@ -266,34 +242,14 @@ impl MintConnector for HttpClient {
request: CheckStateRequest, request: CheckStateRequest,
) -> Result<CheckStateResponse, Error> { ) -> Result<CheckStateResponse, Error> {
let url = self.mint_url.join_paths(&["v1", "checkstate"])?; let url = self.mint_url.join_paths(&["v1", "checkstate"])?;
self.http_post(url, &request).await
let res = self
.inner
.post(url)
.json(&request)
.send()
.await?
.text()
.await?;
convert_http_response!(CheckStateResponse, res)
} }
/// Restore request [NUT-13] /// Restore request [NUT-13]
#[instrument(skip(self, request), fields(mint_url = %self.mint_url))] #[instrument(skip(self, request), fields(mint_url = %self.mint_url))]
async fn post_restore(&self, request: RestoreRequest) -> Result<RestoreResponse, Error> { async fn post_restore(&self, request: RestoreRequest) -> Result<RestoreResponse, Error> {
let url = self.mint_url.join_paths(&["v1", "restore"])?; let url = self.mint_url.join_paths(&["v1", "restore"])?;
self.http_post(url, &request).await
let res = self
.inner
.post(url)
.json(&request)
.send()
.await?
.text()
.await?;
convert_http_response!(RestoreResponse, res)
} }
} }

View File

@@ -6,6 +6,8 @@ use std::sync::Arc;
use bitcoin::bip32::Xpriv; use bitcoin::bip32::Xpriv;
use bitcoin::Network; use bitcoin::Network;
use cdk_common::database::{self, WalletDatabase};
use cdk_common::subscription::Params;
use client::MintConnector; use client::MintConnector;
use getrandom::getrandom; use getrandom::getrandom;
pub use multi_mint_wallet::MultiMintWallet; pub use multi_mint_wallet::MultiMintWallet;
@@ -14,13 +16,12 @@ use tracing::instrument;
pub use types::{MeltQuote, MintQuote, SendKind}; pub use types::{MeltQuote, MintQuote, SendKind};
use crate::amount::SplitTarget; use crate::amount::SplitTarget;
use crate::cdk_database::{self, WalletDatabase};
use crate::dhke::construct_proofs; use crate::dhke::construct_proofs;
use crate::error::Error; use crate::error::Error;
use crate::fees::calculate_fee; use crate::fees::calculate_fee;
use crate::mint_url::MintUrl; use crate::mint_url::MintUrl;
use crate::nuts::nut00::token::Token; use crate::nuts::nut00::token::Token;
use crate::nuts::nut17::{Kind, Params}; use crate::nuts::nut17::Kind;
use crate::nuts::{ use crate::nuts::{
nut10, CurrencyUnit, Id, Keys, MintInfo, MintQuoteState, PreMintSecrets, Proof, Proofs, nut10, CurrencyUnit, Id, Keys, MintInfo, MintQuoteState, PreMintSecrets, Proof, Proofs,
RestoreRequest, SpendingConditions, State, RestoreRequest, SpendingConditions, State,
@@ -39,9 +40,10 @@ mod receive;
mod send; mod send;
pub mod subscription; pub mod subscription;
mod swap; mod swap;
pub mod types;
pub mod util; pub mod util;
pub use cdk_common::wallet as types;
use crate::nuts::nut00::ProofsMethods; use crate::nuts::nut00::ProofsMethods;
/// CDK Wallet /// CDK Wallet
@@ -56,7 +58,7 @@ pub struct Wallet {
/// Unit /// Unit
pub unit: CurrencyUnit, pub unit: CurrencyUnit,
/// Storage backend /// Storage backend
pub localstore: Arc<dyn WalletDatabase<Err = cdk_database::Error> + Send + Sync>, pub localstore: Arc<dyn WalletDatabase<Err = database::Error> + Send + Sync>,
/// The targeted amount of proofs to have at each size /// The targeted amount of proofs to have at each size
pub target_proof_count: usize, pub target_proof_count: usize,
xpriv: Xpriv, xpriv: Xpriv,
@@ -132,7 +134,7 @@ impl Wallet {
pub fn new( pub fn new(
mint_url: &str, mint_url: &str,
unit: CurrencyUnit, unit: CurrencyUnit,
localstore: Arc<dyn WalletDatabase<Err = cdk_database::Error> + Send + Sync>, localstore: Arc<dyn WalletDatabase<Err = database::Error> + Send + Sync>,
seed: &[u8], seed: &[u8],
target_proof_count: Option<usize>, target_proof_count: Option<usize>,
) -> Result<Self, Error> { ) -> Result<Self, Error> {

View File

@@ -4,11 +4,10 @@
//! pairs //! pairs
use std::collections::{BTreeMap, HashMap}; use std::collections::{BTreeMap, HashMap};
use std::fmt;
use std::str::FromStr; use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use serde::{Deserialize, Serialize}; use cdk_common::wallet::WalletKey;
use tokio::sync::Mutex; use tokio::sync::Mutex;
use tracing::instrument; use tracing::instrument;
@@ -28,26 +27,6 @@ pub struct MultiMintWallet {
pub wallets: Arc<Mutex<BTreeMap<WalletKey, Wallet>>>, pub wallets: Arc<Mutex<BTreeMap<WalletKey, Wallet>>>,
} }
/// 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 { impl MultiMintWallet {
/// New Multimint wallet /// New Multimint wallet
pub fn new(wallets: Vec<Wallet>) -> Self { pub fn new(wallets: Vec<Wallet>) -> Self {

View File

@@ -9,12 +9,12 @@ use std::collections::HashMap;
use std::fmt::Debug; use std::fmt::Debug;
use std::sync::Arc; use std::sync::Arc;
use cdk_common::subscription::Params;
use tokio::sync::{mpsc, RwLock}; use tokio::sync::{mpsc, RwLock};
use tokio::task::JoinHandle; use tokio::task::JoinHandle;
use tracing::error; use tracing::error;
use crate::mint_url::MintUrl; use crate::mint_url::MintUrl;
use crate::nuts::nut17::Params;
use crate::pub_sub::SubId; use crate::pub_sub::SubId;
use crate::wallet::client::MintConnector; use crate::wallet::client::MintConnector;

View File

@@ -2,6 +2,8 @@ use std::collections::{HashMap, HashSet};
use std::sync::atomic::AtomicUsize; use std::sync::atomic::AtomicUsize;
use std::sync::Arc; use std::sync::Arc;
use cdk_common::subscription::Params;
use cdk_common::ws::{WsMessageOrResponse, WsMethodRequest, WsRequest, WsUnsubscribeRequest};
use futures::{SinkExt, StreamExt}; use futures::{SinkExt, StreamExt};
use tokio::sync::{mpsc, RwLock}; use tokio::sync::{mpsc, RwLock};
use tokio_tungstenite::connect_async; use tokio_tungstenite::connect_async;
@@ -10,10 +12,6 @@ use tokio_tungstenite::tungstenite::Message;
use super::http::http_main; use super::http::http_main;
use super::WsSubscriptionBody; use super::WsSubscriptionBody;
use crate::mint_url::MintUrl; 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::pub_sub::SubId;
use crate::wallet::client::MintConnector; use crate::wallet::client::MintConnector;