chore: readmes

chore: doc comments on public
This commit is contained in:
thesimplekid
2024-06-28 10:40:09 +01:00
parent aa65879482
commit b528964fb6
54 changed files with 598 additions and 109 deletions

View File

@@ -6,7 +6,19 @@
CDK is a collection of rust crates for [Cashu](https://github.com/cashubtc) wallets and mints written in Rust.
**ALPHA** This library is in early development, the api will change.
**ALPHA** This library is in early development, the api will change and should be used with caution.
## Project structure
The project is split up into several crates in the `crates/` directory:
* Libraries:
* [**cdk**](./crates/cdk/): Rust implementation of Cashu protocol.
* [**cdk-sqlite**](./crates/cdk-sqlite/): Sqlite Storage backend
* [**cdk-redb**](./crates/cdk-redb/): Redb Storage backend
* Binaries:
* [**cdk-cli**](./crates/cdk-cli/): Cashu wallet CLI
## Implemented [NUTs](https://github.com/cashubtc/nuts/):

View File

@@ -7,7 +7,7 @@ use cdk::amount::SplitTarget;
use cdk::nuts::{Proofs, SecretKey};
use cdk::wallet::Wallet;
use cdk::Amount;
use cdk_rexie::RexieWalletDatabase;
use cdk_rexie::WalletRexieDatabase;
use wasm_bindgen::prelude::*;
use crate::error::{into_err, Result};
@@ -42,7 +42,7 @@ impl From<Wallet> for JsWallet {
impl JsWallet {
#[wasm_bindgen(constructor)]
pub async fn new(mints_url: String, unit: JsCurrencyUnit, seed: Vec<u8>) -> Self {
let db = RexieWalletDatabase::new().await.unwrap();
let db = WalletRexieDatabase::new().await.unwrap();
Wallet::new(&mints_url, unit.into(), Arc::new(db), &seed).into()
}

View File

@@ -9,8 +9,8 @@ use bip39::Mnemonic;
use cdk::cdk_database::WalletDatabase;
use cdk::wallet::Wallet;
use cdk::{cdk_database, UncheckedUrl};
use cdk_redb::RedbWalletDatabase;
use cdk_sqlite::WalletSQLiteDatabase;
use cdk_redb::WalletRedbDatabase;
use cdk_sqlite::WalletSqliteDatabase;
use clap::{Parser, Subcommand};
use rand::Rng;
@@ -86,7 +86,7 @@ async fn main() -> Result<()> {
match args.engine.as_str() {
"sqlite" => {
let sql_path = work_dir.join("cdk-cli.sqlite");
let sql = WalletSQLiteDatabase::new(&sql_path).await?;
let sql = WalletSqliteDatabase::new(&sql_path).await?;
sql.migrate().await;
@@ -95,7 +95,7 @@ async fn main() -> Result<()> {
"redb" => {
let redb_path = work_dir.join("cdk-cli.redb");
Arc::new(RedbWalletDatabase::new(&redb_path)?)
Arc::new(WalletRedbDatabase::new(&redb_path)?)
}
_ => bail!("Unknown DB engine"),
};

23
crates/cdk-redb/README.md Normal file
View File

@@ -0,0 +1,23 @@
# Cashu Development Kit Redb Storage Backend
**ALPHA** This library is in early development, the api will change and should be used with caution.
cdk-redb is the [redb](https://docs.rs/redb/latest/redb/) storage backend for cdk.
## Crate Feature Flags
The following crate feature flags are available:
| Feature | Default | Description |
|-------------|:-------:|------------------------------------|
| `wallet` | Yes | Enable cashu wallet features |
| `mint` | Yes | Enable cashu mint wallet features |
## Implemented [NUTs](https://github.com/cashubtc/nuts/):
See <https://github.com/cashubtc/cdk/blob/main/README.md>
## License
This project is distributed under the MIT software license - see the [LICENSE](../../LICENSE) file for details

View File

@@ -1,7 +1,10 @@
//! Redb Error
use std::num::ParseIntError;
use thiserror::Error;
/// Redb Database Error
#[derive(Debug, Error)]
pub enum Error {
/// Redb Error

View File

@@ -1,3 +1,8 @@
//! SQLite Storage backend for CDK
#![warn(missing_docs)]
#![warn(rustdoc::bare_urls)]
pub mod error;
mod migrations;
@@ -9,4 +14,4 @@ pub mod wallet;
#[cfg(feature = "mint")]
pub use mint::MintRedbDatabase;
#[cfg(feature = "wallet")]
pub use wallet::RedbWalletDatabase;
pub use wallet::WalletRedbDatabase;

View File

@@ -1,3 +1,5 @@
//! SQLite Storage for CDK
use std::cmp::Ordering;
use std::collections::HashMap;
use std::path::Path;
@@ -34,12 +36,14 @@ const BLINDED_SIGNATURES: TableDefinition<[u8; 33], &str> =
const DATABASE_VERSION: u32 = 0;
/// Mint Redbdatabase
#[derive(Debug, Clone)]
pub struct MintRedbDatabase {
db: Arc<Mutex<Database>>,
}
impl MintRedbDatabase {
/// Create new [`MintRedbDatabase`]
pub fn new(path: &Path) -> Result<Self, Error> {
{
// Check database version

View File

@@ -41,12 +41,14 @@ const NOSTR_LAST_CHECKED: TableDefinition<&str, u32> = TableDefinition::new("key
const DATABASE_VERSION: u32 = 1;
/// Wallet Redb Database
#[derive(Debug, Clone)]
pub struct RedbWalletDatabase {
pub struct WalletRedbDatabase {
db: Arc<Mutex<Database>>,
}
impl RedbWalletDatabase {
impl WalletRedbDatabase {
/// Create new [`WalletRedbDatabase`]
pub fn new(path: &Path) -> Result<Self, Error> {
{
let db = Arc::new(Database::create(path)?);
@@ -132,7 +134,7 @@ impl RedbWalletDatabase {
}
#[async_trait]
impl WalletDatabase for RedbWalletDatabase {
impl WalletDatabase for WalletRedbDatabase {
type Err = cdk_database::Error;
#[instrument(skip(self))]

View File

@@ -0,0 +1,22 @@
# Cashu Development Kit Redb Storage Backend
**ALPHA** This library is in early development, the api will change and should be used with caution.
cdk-rexie is the [rexie](https://docs.rs/rexie/latest/rexie/) storage backend for wasm cdk wallets in the browser.
## Crate Feature Flags
The following crate feature flags are available:
| Feature | Default | Description |
|-------------|:-------:|------------------------------------|
| `wallet` | Yes | Enable cashu wallet features |
## Implemented [NUTs](https://github.com/cashubtc/nuts/):
See <https://github.com/cashubtc/cdk/blob/main/README.md>
## License
This project is distributed under the MIT software license - see the [LICENSE](../../LICENSE) file for details.

View File

@@ -1,5 +1,10 @@
//! Rexie Indexdb database
#![warn(missing_docs)]
#![warn(rustdoc::bare_urls)]
#[cfg(all(feature = "wallet", target_arch = "wasm32"))]
pub mod wallet;
#[cfg(all(feature = "wallet", target_arch = "wasm32"))]
pub use wallet::RexieWalletDatabase;
pub use wallet::WalletRexieDatabase;

View File

@@ -1,3 +1,5 @@
//! Rexie Browser Database
use std::collections::{HashMap, HashSet};
use std::rc::Rc;
use std::result::Result;
@@ -28,6 +30,7 @@ const NOSTR_LAST_CHECKED: &str = "nostr_last_check";
const DATABASE_VERSION: u32 = 3;
/// Rexie Database Error
#[derive(Debug, Error)]
pub enum Error {
/// CDK Database Error
@@ -39,6 +42,7 @@ pub enum Error {
/// Serde Wasm Error
#[error(transparent)]
SerdeBindgen(#[from] serde_wasm_bindgen::Error),
/// NUT00 Error
#[error(transparent)]
NUT00(cdk::nuts::nut00::Error),
}
@@ -52,16 +56,18 @@ impl From<Error> for cdk::cdk_database::Error {
unsafe impl Send for Error {}
unsafe impl Sync for Error {}
/// Wallet Rexie Database
#[derive(Debug, Clone)]
pub struct RexieWalletDatabase {
pub struct WalletRexieDatabase {
db: Rc<Mutex<Rexie>>,
}
// These are okay because we never actually send across threads in the browser
unsafe impl Send for RexieWalletDatabase {}
unsafe impl Sync for RexieWalletDatabase {}
unsafe impl Send for WalletRexieDatabase {}
unsafe impl Sync for WalletRexieDatabase {}
impl RexieWalletDatabase {
impl WalletRexieDatabase {
/// Create new [`WalletRexieDatabase`]
pub async fn new() -> Result<Self, Error> {
let rexie = Rexie::builder("cdk")
.version(DATABASE_VERSION)
@@ -102,7 +108,7 @@ impl RexieWalletDatabase {
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl WalletDatabase for RexieWalletDatabase {
impl WalletDatabase for WalletRexieDatabase {
type Err = cdk::cdk_database::Error;
async fn add_mint(

View File

@@ -0,0 +1,23 @@
# Cashu Development Kit Redb Storage Backend
**ALPHA** This library is in early development, the api will change and should be used with caution.
cdk-sqlite is the sqlite storage backend for cdk.
## Crate Feature Flags
The following crate feature flags are available:
| Feature | Default | Description |
|-------------|:-------:|------------------------------------|
| `wallet` | Yes | Enable cashu wallet features |
| `mint` | Yes | Enable cashu mint wallet features |
## Implemented [NUTs](https://github.com/cashubtc/nuts/):
See <https://github.com/cashubtc/cdk/blob/main/README.md>
## License
This project is distributed under the MIT software license - see the [LICENSE](../../LICENSE) file for details.

View File

@@ -1,3 +1,8 @@
//! SQLite storage backend for cdk
#![warn(missing_docs)]
#![warn(rustdoc::bare_urls)]
#[cfg(feature = "mint")]
pub mod mint;
#[cfg(feature = "wallet")]
@@ -6,4 +11,4 @@ pub mod wallet;
#[cfg(feature = "mint")]
pub use mint::MintSqliteDatabase;
#[cfg(feature = "wallet")]
pub use wallet::WalletSQLiteDatabase;
pub use wallet::WalletSqliteDatabase;

View File

@@ -1,5 +1,8 @@
//! SQLite Database Error
use thiserror::Error;
/// SQLite Database Error
#[derive(Debug, Error)]
pub enum Error {
/// SQLX Error

View File

@@ -21,12 +21,14 @@ use sqlx::{ConnectOptions, Row};
pub mod error;
/// Mint SQLite Database
#[derive(Debug, Clone)]
pub struct MintSqliteDatabase {
pool: SqlitePool,
}
impl MintSqliteDatabase {
/// Create new [`MintSqliteDatabase`]
pub async fn new(path: &Path) -> Result<Self, Error> {
let path = path.to_str().ok_or(Error::InvalidDbPath)?;
let _conn = SqliteConnectOptions::from_str(path)?
@@ -42,6 +44,7 @@ impl MintSqliteDatabase {
Ok(Self { pool })
}
/// Migrate [`MintSqliteDatabase`]
pub async fn migrate(&self) {
sqlx::migrate!("./src/mint/migrations")
.run(&self.pool)

View File

@@ -1,5 +1,8 @@
//! SQLite Wallet Error
use thiserror::Error;
/// SQLite Wallet Error
#[derive(Debug, Error)]
pub enum Error {
/// SQLX Error

View File

@@ -20,12 +20,14 @@ use sqlx::{ConnectOptions, Row};
pub mod error;
/// Wallet SQLite Database
#[derive(Debug, Clone)]
pub struct WalletSQLiteDatabase {
pub struct WalletSqliteDatabase {
pool: SqlitePool,
}
impl WalletSQLiteDatabase {
impl WalletSqliteDatabase {
/// Create new [`WalletSqliteDatabase`]
pub async fn new(path: &Path) -> Result<Self, Error> {
let path = path.to_str().ok_or(Error::InvalidDbPath)?;
let _conn = SqliteConnectOptions::from_str(path)?
@@ -41,6 +43,7 @@ impl WalletSQLiteDatabase {
Ok(Self { pool })
}
/// Migrate [`WalletSqliteDatabase`]
pub async fn migrate(&self) {
sqlx::migrate!("./src/wallet/migrations")
.run(&self.pool)
@@ -50,7 +53,7 @@ impl WalletSQLiteDatabase {
}
#[async_trait]
impl WalletDatabase for WalletSQLiteDatabase {
impl WalletDatabase for WalletSqliteDatabase {
type Err = cdk_database::Error;
async fn add_mint(

View File

@@ -1,19 +1,23 @@
# Cashu Development Kit
**ALPHA** This library is in early development, the api will change and should be used with caution.
CDK is the core crate implementing the cashu protocol for both the Wallet and Mint.
## Crate Feature Flags
The following crate feature flags are available:
| Feature | Default | Description |
|-------------|:-------:|------------------------------------|
| `wallet` | Yes | Enable cashu wallet features |
| `mint` | Yes | Enable cashu mint wallet features |
## Implemented [NUTs](https://github.com/cashubtc/nuts/):
- :heavy_check_mark: [NUT-00](https://github.com/cashubtc/nuts/blob/main/00.md)
- :heavy_check_mark: [NUT-01](https://github.com/cashubtc/nuts/blob/main/01.md)
- :heavy_check_mark: [NUT-02](https://github.com/cashubtc/nuts/blob/main/02.md)
- :heavy_check_mark: [NUT-03](https://github.com/cashubtc/nuts/blob/main/03.md)
- :heavy_check_mark: [NUT-04](https://github.com/cashubtc/nuts/blob/main/04.md)
- :heavy_check_mark: [NUT-05](https://github.com/cashubtc/nuts/blob/main/05.md)
- :heavy_check_mark: [NUT-06](https://github.com/cashubtc/nuts/blob/main/06.md)
- :heavy_check_mark: [NUT-07](https://github.com/cashubtc/nuts/blob/main/07.md)
- :heavy_check_mark: [NUT-08](https://github.com/cashubtc/nuts/blob/main/08.md)
- :heavy_check_mark: [NUT-09](https://github.com/cashubtc/nuts/blob/main/09.md)
- :heavy_check_mark: [NUT-10](https://github.com/cashubtc/nuts/blob/main/10.md)
- :heavy_check_mark: [NUT-11](https://github.com/cashubtc/nuts/blob/main/11.md)
- :heavy_check_mark: [NUT-12](https://github.com/cashubtc/nuts/blob/main/12.md)
- :heavy_check_mark: [NUT-13](https://github.com/cashubtc/nuts/blob/main/13.md)
See <https://github.com/cashubtc/cdk/blob/main/README.md>
## License
This project is distributed under the MIT software license - see the [LICENSE](../../LICENSE) file for details

View File

@@ -1,3 +1,7 @@
//! CDK Amount
//!
//! Is any and will be treated as the unit of the wallet
use std::fmt;
use serde::{Deserialize, Serialize};
@@ -8,6 +12,7 @@ use serde::{Deserialize, Serialize};
pub struct Amount(u64);
impl Amount {
/// Amount zero
pub const ZERO: Amount = Amount(0);
/// Split into parts that are powers of two

View File

@@ -1,3 +1,5 @@
//! Mint in memory database
use std::collections::HashMap;
use std::sync::Arc;
@@ -13,6 +15,7 @@ use crate::nuts::{
use crate::secret::Secret;
use crate::types::{MeltQuote, MintQuote};
/// Mint Memory Database
#[derive(Debug, Clone)]
pub struct MintMemoryDatabase {
active_keysets: Arc<RwLock<HashMap<CurrencyUnit, Id>>>,
@@ -25,6 +28,7 @@ pub struct MintMemoryDatabase {
}
impl MintMemoryDatabase {
/// Create new [`MintMemoryDatabase`]
#[allow(clippy::too_many_arguments)]
pub fn new(
active_keysets: HashMap<CurrencyUnit, Id>,

View File

@@ -35,63 +35,90 @@ pub mod wallet_memory;
#[cfg(feature = "wallet")]
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>),
/// CDK Error
#[error(transparent)]
Cdk(#[from] crate::error::Error),
/// NUT01 Error
#[error(transparent)]
NUT01(#[from] crate::nuts::nut00::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: UncheckedUrl,
mint_info: Option<MintInfo>,
) -> Result<(), Self::Err>;
/// Remove Mint from storage
async fn remove_mint(&self, mint_url: UncheckedUrl) -> Result<(), Self::Err>;
/// Get mint from storage
async fn get_mint(&self, mint_url: UncheckedUrl) -> Result<Option<MintInfo>, Self::Err>;
/// Get all mints from storage
async fn get_mints(&self) -> Result<HashMap<UncheckedUrl, Option<MintInfo>>, Self::Err>;
/// Update mint url
async fn update_mint_url(
&self,
old_mint_url: UncheckedUrl,
new_mint_url: UncheckedUrl,
) -> Result<(), Self::Err>;
/// Add mint keyset to storage
async fn add_mint_keysets(
&self,
mint_url: UncheckedUrl,
keysets: Vec<KeySetInfo>,
) -> Result<(), Self::Err>;
/// Get mint keysets for mint url
async fn get_mint_keysets(
&self,
mint_url: UncheckedUrl,
) -> 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: MintQuote) -> Result<(), Self::Err>;
/// Get mint quote from storage
async fn get_mint_quote(&self, quote_id: &str) -> Result<Option<MintQuote>, Self::Err>;
/// Get mint quotes from storage
async fn get_mint_quotes(&self) -> Result<Vec<MintQuote>, 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: MeltQuote) -> Result<(), Self::Err>;
/// Get melt quote from storage
async fn get_melt_quote(&self, quote_id: &str) -> Result<Option<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>;
/// Add [`Proofs`] to storage
async fn add_proofs(&self, proof_info: Vec<ProofInfo>) -> Result<(), Self::Err>;
/// Get proofs from storage
async fn get_proofs(
&self,
mint_url: Option<UncheckedUrl>,
@@ -99,17 +126,23 @@ pub trait WalletDatabase: Debug {
state: Option<Vec<State>>,
spending_conditions: Option<Vec<SpendingConditions>>,
) -> Result<Option<Vec<ProofInfo>>, Self::Err>;
/// Remove proofs from storage
async fn remove_proofs(&self, proofs: &Proofs) -> Result<(), Self::Err>;
/// Set Proof state
async fn set_proof_state(&self, y: PublicKey, state: State) -> Result<(), Self::Err>;
/// Increment Keyset counter
async fn increment_keyset_counter(&self, keyset_id: &Id, count: u32) -> Result<(), Self::Err>;
/// Get current Keyset counter
async fn get_keyset_counter(&self, keyset_id: &Id) -> Result<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,
@@ -117,60 +150,88 @@ pub trait WalletDatabase: Debug {
) -> 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 add_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 [`MintQuote`]
async fn add_mint_quote(&self, quote: MintQuote) -> Result<(), Self::Err>;
/// Get [`MintQuote`]
async fn get_mint_quote(&self, quote_id: &str) -> Result<Option<MintQuote>, Self::Err>;
/// Update state of [`MintQuote`]
async fn update_mint_quote_state(
&self,
quote_id: &str,
state: MintQuoteState,
) -> Result<MintQuoteState, Self::Err>;
/// Get all [`MintQuote`]s
async fn get_mint_quotes(&self) -> Result<Vec<MintQuote>, Self::Err>;
/// Remove [`MintQuote`]
async fn remove_mint_quote(&self, quote_id: &str) -> Result<(), Self::Err>;
/// Add [`MeltQuote`]
async fn add_melt_quote(&self, quote: MeltQuote) -> Result<(), Self::Err>;
/// Get [`MeltQuote`]
async fn get_melt_quote(&self, quote_id: &str) -> Result<Option<MeltQuote>, Self::Err>;
/// Update [`MeltQuote`] state
async fn update_melt_quote_state(
&self,
quote_id: &str,
state: MeltQuoteState,
) -> Result<MeltQuoteState, Self::Err>;
/// Get all [`MeltQuote`]s
async fn get_melt_quotes(&self) -> Result<Vec<MeltQuote>, Self::Err>;
/// Remove [`MeltQuote`]
async fn remove_melt_quote(&self, quote_id: &str) -> Result<(), 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_spent_proofs(&self, proof: Proofs) -> Result<(), Self::Err>;
/// Get spent [`Proof`] by secret
async fn get_spent_proof_by_secret(&self, secret: &Secret) -> Result<Option<Proof>, Self::Err>;
/// Get spent [`Proof`] by y
async fn get_spent_proof_by_y(&self, y: &PublicKey) -> Result<Option<Proof>, Self::Err>;
/// Add pending [`Proofs`]
async fn add_pending_proofs(&self, proof: Proofs) -> Result<(), Self::Err>;
/// Get pending [`Proof`] by secret
async fn get_pending_proof_by_secret(
&self,
secret: &Secret,
) -> Result<Option<Proof>, Self::Err>;
/// Get pending [`Proof`] by y
async fn get_pending_proof_by_y(&self, y: &PublicKey) -> Result<Option<Proof>, Self::Err>;
/// Remove pending [`Proofs`]
async fn remove_pending_proofs(&self, secret: Vec<&Secret>) -> Result<(), Self::Err>;
/// Add [`BlindSignature`]
async fn add_blinded_signature(
&self,
blinded_message: PublicKey,
blinded_signature: BlindSignature,
) -> Result<(), Self::Err>;
/// Get [`BlindSignature`]
async fn get_blinded_signature(
&self,
blinded_message: &PublicKey,
) -> Result<Option<BlindSignature>, Self::Err>;
/// Get [`BlindSignature`]s
async fn get_blinded_signatures(
&self,
blinded_messages: Vec<PublicKey>,

View File

@@ -1,4 +1,4 @@
//! Memory Database
//! Wallet in memory database
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
@@ -15,6 +15,7 @@ use crate::types::{MeltQuote, MintQuote, ProofInfo};
use crate::url::UncheckedUrl;
use crate::util::unix_time;
/// Wallet in Memory Database
#[derive(Default, Debug, Clone)]
pub struct WalletMemoryDatabase {
mints: Arc<RwLock<HashMap<UncheckedUrl, Option<MintInfo>>>>,
@@ -29,6 +30,7 @@ pub struct WalletMemoryDatabase {
}
impl WalletMemoryDatabase {
/// Create new [`WalletMemoryDatabase`]
pub fn new(
mint_quotes: Vec<MintQuote>,
melt_quotes: Vec<MeltQuote>,

View File

@@ -18,6 +18,9 @@ use crate::SECP256K1;
const DOMAIN_SEPARATOR: &[u8; 28] = b"Secp256k1_HashToCurve_Cashu_";
/// Deterministically maps a message to a public key point on the secp256k1 curve, utilizing a domain separator to ensure uniqueness.
///
/// For definationn in NUT see [NUT-00](https://github.com/cashubtc/nuts/blob/main/00.md)
pub fn hash_to_curve(message: &[u8]) -> Result<PublicKey, Error> {
let msg_to_hash: Vec<u8> = [DOMAIN_SEPARATOR, message].concat();
@@ -44,6 +47,7 @@ pub fn hash_to_curve(message: &[u8]) -> Result<PublicKey, Error> {
Err(Error::NoValidPoint)
}
/// Convert iterator of [`PublicKey`] to byte array
pub fn hash_e<I>(public_keys: I) -> [u8; 32]
where
I: IntoIterator<Item = PublicKey>,
@@ -158,7 +162,7 @@ pub fn verify_message(
#[cfg(test)]
mod tests {
use core::str::FromStr;
use std::str::FromStr;
use super::*;

View File

@@ -9,6 +9,7 @@ use thiserror::Error;
use crate::util::hex;
/// CDK Error
#[derive(Debug, Error)]
pub enum Error {
/// Mint does not have a key for amount
@@ -77,10 +78,16 @@ pub enum Error {
CustomError(String),
}
/// CDK Error Response
///
/// See NUT defenation in [00](https://github.com/cashubtc/nuts/blob/main/00.md)
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ErrorResponse {
/// Error Code
pub code: ErrorCode,
/// Human readable Text
pub error: Option<String>,
/// Longer human readable description
pub detail: Option<String>,
}
@@ -97,12 +104,14 @@ impl fmt::Display for ErrorResponse {
}
impl ErrorResponse {
/// Error response from json
pub fn from_json(json: &str) -> Result<Self, serde_json::Error> {
let value: Value = serde_json::from_str(json)?;
Self::from_value(value)
}
/// Error response from json Value
pub fn from_value(value: Value) -> Result<Self, serde_json::Error> {
match serde_json::from_value::<ErrorResponse>(value.clone()) {
Ok(res) => Ok(res),
@@ -115,11 +124,16 @@ impl ErrorResponse {
}
}
/// Possible Error Codes
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub enum ErrorCode {
/// Token is already spent
TokenAlreadySpent,
/// Quote is not paid
QuoteNotPaid,
/// Keyset is not found
KeysetNotFound,
/// Unknown error code
Unknown(u16),
}

View File

@@ -1,8 +1,7 @@
extern crate core;
//! Rust implementation of the Cashu Protocol
pub use bitcoin::hashes::sha256::Hash as Sha256;
pub use bitcoin::secp256k1;
pub use lightning_invoice::{self, Bolt11Invoice};
#![warn(missing_docs)]
#![warn(rustdoc::bare_urls)]
pub mod amount;
pub mod cdk_database;
@@ -18,15 +17,27 @@ pub mod util;
#[cfg(feature = "wallet")]
pub mod wallet;
#[doc(hidden)]
pub use bitcoin::secp256k1;
#[doc(hidden)]
pub use lightning_invoice::{self, Bolt11Invoice};
#[cfg(feature = "mint")]
#[doc(hidden)]
pub use mint::Mint;
#[cfg(feature = "wallet")]
#[doc(hidden)]
pub use wallet::Wallet;
#[doc(hidden)]
pub use self::amount::Amount;
#[doc(hidden)]
pub use self::url::UncheckedUrl;
#[doc(hidden)]
pub use self::util::SECP256K1;
#[cfg(feature = "wallet")]
#[doc(hidden)]
pub use self::wallet::client::HttpClient;
/// Result
#[doc(hidden)]
pub type Result<T, E = Box<dyn std::error::Error>> = std::result::Result<T, E>;

View File

@@ -6,6 +6,7 @@ use thiserror::Error;
use crate::cdk_database;
use crate::error::{ErrorCode, ErrorResponse};
/// CDK Mint Error
#[derive(Debug, Error)]
pub enum Error {
/// Unknown Keyset
@@ -14,47 +15,67 @@ pub enum Error {
/// Inactive Keyset
#[error("Inactive Keyset")]
InactiveKeyset,
/// There is not key for amount given
#[error("No key for amount")]
AmountKey,
/// Amount is not what is expected
#[error("Amount")]
Amount,
/// Duplicate proofs provided
#[error("Duplicate proofs")]
DuplicateProofs,
/// Token is already spent
#[error("Token Already Spent")]
TokenAlreadySpent,
/// Token is already pending
#[error("Token Pending")]
TokenPending,
/// Quote is not paiud
#[error("Quote not paid")]
UnpaidQuote,
/// Quote has already been paid
#[error("Quote is already paid")]
PaidQuote,
/// Quote is not known
#[error("Unknown quote")]
UnknownQuote,
/// Quote is pending
#[error("Quote pending")]
PendingQuote,
/// ecash already issued for quote
#[error("Quote already issued")]
IssuedQuote,
/// Unknown secret kind
#[error("Unknown secret kind")]
UnknownSecretKind,
/// Multiple units provided
#[error("Cannot have multiple units")]
MultipleUnits,
/// BlindMessage is already signed
#[error("Blinded Message is already signed")]
BlindedMessageAlreadySigned,
/// Cashu Error
#[error(transparent)]
Cashu(#[from] crate::error::Error),
/// Secret Error
#[error(transparent)]
Secret(#[from] crate::secret::Error),
/// NUT00 Error
#[error(transparent)]
NUT00(#[from] crate::nuts::nut00::Error),
/// NUT11 Error
#[error(transparent)]
NUT11(#[from] crate::nuts::nut11::Error),
/// NUT12 Error
#[error(transparent)]
Nut12(#[from] crate::nuts::nut12::Error),
/// NUT14 Error
#[error(transparent)]
Nut14(#[from] crate::nuts::nut14::Error),
/// Database Error
#[error(transparent)]
Database(#[from] cdk_database::Error),
/// Custom Error
#[error("`{0}`")]
Custom(String),
}

View File

@@ -21,18 +21,23 @@ use crate::Amount;
pub mod error;
/// Cashu Mint
#[derive(Clone)]
pub struct Mint {
/// Mint Url
pub mint_url: UncheckedUrl,
mint_info: MintInfo,
keysets: Arc<RwLock<HashMap<Id, MintKeySet>>>,
secp_ctx: Secp256k1<secp256k1::All>,
xpriv: ExtendedPrivKey,
/// Mint Expected [`FeeReserve`]
pub fee_reserve: FeeReserve,
/// Mint Storage backend
pub localstore: Arc<dyn MintDatabase<Err = cdk_database::Error> + Send + Sync>,
}
impl Mint {
/// Create new [`Mint`]
pub async fn new(
mint_url: &str,
seed: &[u8],
@@ -844,19 +849,30 @@ impl Mint {
/// Mint Fee Reserve
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FeeReserve {
/// Absolute expected min fee
pub min_fee_reserve: Amount,
/// Percentage expected fee
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`] of Keyset
pub derivation_path: DerivationPath,
/// Max order of keyset
pub max_order: u8,
}

View File

@@ -1,3 +1,7 @@
//! Nuts
//!
//! See all at <https://github.com/cashubtc/nuts>
pub mod nut00;
pub mod nut01;
pub mod nut02;

View File

@@ -30,6 +30,7 @@ use crate::Amount;
/// List of [Proof]
pub type Proofs = Vec<Proof>;
/// NUT00 Error
#[derive(Debug, Error)]
pub enum Error {
/// Proofs required
@@ -141,6 +142,7 @@ pub enum Witness {
}
impl Witness {
/// Add signatures to [`Witness`]
pub fn add_signatures(&mut self, signatues: Vec<String>) {
match self {
Self::P2PKWitness(p2pk_witness) => p2pk_witness.signatures.extend(signatues),
@@ -154,6 +156,7 @@ impl Witness {
}
}
/// Get signatures on [`Witness`]
pub fn signatures(&self) -> Option<Vec<String>> {
match self {
Self::P2PKWitness(witness) => Some(witness.signatures.clone()),
@@ -161,6 +164,7 @@ impl Witness {
}
}
/// Get preimage from [`Witness`]
pub fn preimage(&self) -> Option<String> {
match self {
Self::P2PKWitness(_witness) => None,
@@ -191,6 +195,7 @@ pub struct Proof {
}
impl Proof {
/// Create new [`Proof`]
pub fn new(amount: Amount, keyset_id: Id, secret: Secret, c: PublicKey) -> Self {
Proof {
amount,
@@ -202,6 +207,9 @@ impl Proof {
}
}
/// Get y from proof
///
/// Where y is `hash_to_curve(secret)`
pub fn y(&self) -> Result<PublicKey, Error> {
Ok(hash_to_curve(self.secret.as_bytes())?)
}
@@ -225,12 +233,17 @@ impl PartialOrd for Proof {
}
}
/// Currency Unit
#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
pub enum CurrencyUnit {
/// Sat
#[default]
Sat,
/// Msat
Msat,
/// Usd
Usd,
/// Custom unit
Custom(String),
}
@@ -278,10 +291,13 @@ impl<'de> Deserialize<'de> for CurrencyUnit {
}
}
/// Payment Method
#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
pub enum PaymentMethod {
/// Bolt11 payment type
#[default]
Bolt11,
/// Custom payment type:
Custom(String),
}
@@ -325,6 +341,7 @@ impl<'de> Deserialize<'de> for PaymentMethod {
}
}
/// PreMint
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct PreMint {
/// Blinded message
@@ -349,8 +366,10 @@ impl PartialOrd for PreMint {
}
}
/// Premint Secrets
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize)]
pub struct PreMintSecrets {
/// Secrets
pub secrets: Vec<PreMint>,
}
@@ -382,6 +401,7 @@ impl PreMintSecrets {
Ok(PreMintSecrets { secrets: output })
}
/// Outputs from pre defined secrets
pub fn from_secrets(
keyset_id: Id,
amounts: Vec<Amount>,
@@ -428,6 +448,7 @@ impl PreMintSecrets {
Ok(PreMintSecrets { secrets: output })
}
/// Outputs with specific spending conditions
pub fn with_conditions(
keyset_id: Id,
amount: Amount,
@@ -457,18 +478,25 @@ impl PreMintSecrets {
Ok(PreMintSecrets { secrets: output })
}
/// Iterate over secrets
#[inline]
pub fn iter(&self) -> impl Iterator<Item = &PreMint> {
self.secrets.iter()
}
/// Length of secrets
#[inline]
pub fn len(&self) -> usize {
self.secrets.len()
}
/// If secrets is empty
#[inline]
pub fn is_empty(&self) -> bool {
self.secrets.is_empty()
}
/// Totoal amount of secrets
pub fn total_amount(&self) -> Amount {
self.secrets
.iter()
@@ -476,26 +504,38 @@ impl PreMintSecrets {
.sum()
}
/// [`BlindedMessage`]s from [`PreMintSecrets`]
#[inline]
pub fn blinded_messages(&self) -> Vec<BlindedMessage> {
self.iter().map(|pm| pm.blinded_message.clone()).collect()
}
/// [`Secret`]s from [`PreMintSecrets`]
#[inline]
pub fn secrets(&self) -> Vec<Secret> {
self.iter().map(|pm| pm.secret.clone()).collect()
}
/// Blinding factor from [`PreMintSecrets`]
#[inline]
pub fn rs(&self) -> Vec<SecretKey> {
self.iter().map(|pm| pm.r.clone()).collect()
}
/// Amounts from [`PreMintSecrets`]
#[inline]
pub fn amounts(&self) -> Vec<Amount> {
self.iter().map(|pm| pm.amount).collect()
}
/// Combine [`PreMintSecrets`]
#[inline]
pub fn combine(&mut self, mut other: Self) {
self.secrets.append(&mut other.secrets)
}
/// Sort [`PreMintSecrets`] by [`Amount`]
#[inline]
pub fn sort_secrets(&mut self) {
self.secrets.sort();
}
@@ -523,8 +563,10 @@ impl PartialOrd for PreMintSecrets {
}
}
/// Token
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Token {
/// Proofs in [`Token`] by mint
pub token: Vec<MintProofs>,
/// Memo for token
#[serde(skip_serializing_if = "Option::is_none")]
@@ -535,6 +577,7 @@ pub struct Token {
}
impl Token {
/// Create new [`Token`]
pub fn new(
mint_url: UncheckedUrl,
proofs: Proofs,
@@ -555,6 +598,8 @@ impl Token {
})
}
/// Token Info
/// Assumes only one mint in [`Token`]
pub fn token_info(&self) -> (Amount, String) {
let mut amount = Amount::ZERO;
@@ -595,13 +640,17 @@ impl fmt::Display for Token {
}
}
/// Mint Proofs
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct MintProofs {
/// Url of mint
pub mint: UncheckedUrl,
/// [`Proofs`]
pub proofs: Proofs,
}
impl MintProofs {
/// Create new [`MintProofs`]
pub fn new(mint_url: UncheckedUrl, proofs: Proofs) -> Self {
Self {
mint: mint_url,

View File

@@ -18,17 +18,23 @@ pub use self::secret_key::SecretKey;
use super::nut02::KeySet;
use crate::amount::Amount;
/// Nut01 Error
#[derive(Debug, Error)]
pub enum Error {
/// Secp256k1 Error
#[error(transparent)]
Secp256k1(#[from] secp256k1::Error),
/// Json Error
#[error(transparent)]
Json(#[from] serde_json::Error),
#[cfg(feature = "nostr")]
#[error(transparent)]
NostrKey(#[from] nostr_sdk::key::Error),
/// Invalid Pubkey size
#[error("Invalid public key size: expected={expected}, found={found}")]
InvalidPublicKeySize { expected: usize, found: usize },
InvalidPublicKeySize {
/// Expected size
expected: usize,
/// Actual size
found: usize,
},
}
/// Mint Keys [NUT-01]
@@ -47,16 +53,19 @@ impl From<MintKeys> for Keys {
}
impl Keys {
/// Create new [`Keys`]
#[inline]
pub fn new(keys: BTreeMap<String, PublicKey>) -> Self {
Self(keys)
}
/// Get [`Keys`]
#[inline]
pub fn keys(&self) -> &BTreeMap<String, PublicKey> {
&self.0
}
/// Get [`PublicKey`] for [`Amount`]
#[inline]
pub fn amount_key(&self, amount: Amount) -> Option<PublicKey> {
self.0.get(&amount.to_string()).copied()
@@ -72,6 +81,7 @@ impl Keys {
/// Mint Public Keys [NUT-01]
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct KeysResponse {
/// Keysets
pub keysets: Vec<KeySet>,
}
@@ -116,19 +126,24 @@ impl DerefMut for MintKeys {
}
impl MintKeys {
/// Create new [`MintKeys`]
#[inline]
pub fn new(map: BTreeMap<Amount, MintKeyPair>) -> Self {
Self(map)
}
}
/// Mint Public Private key pair
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct MintKeyPair {
/// Publickey
pub public_key: PublicKey,
/// Secretkey
pub secret_key: SecretKey,
}
impl MintKeyPair {
/// [`MintKeyPair`] from secret key
#[inline]
pub fn from_secret_key(secret_key: SecretKey) -> Self {
Self {

View File

@@ -11,6 +11,7 @@ use serde::{Deserialize, Deserializer, Serialize};
use super::Error;
use crate::SECP256K1;
/// PublicKey
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PublicKey {
inner: secp256k1::PublicKey,
@@ -30,38 +31,6 @@ impl From<secp256k1::PublicKey> for PublicKey {
}
}
#[cfg(feature = "nostr")]
impl TryFrom<PublicKey> for nostr_sdk::PublicKey {
type Error = Error;
fn try_from(pubkey: PublicKey) -> Result<Self, Self::Error> {
Ok(nostr_sdk::PublicKey::from_slice(&pubkey.to_bytes())?)
}
}
#[cfg(feature = "nostr")]
impl TryFrom<&PublicKey> for nostr_sdk::PublicKey {
type Error = Error;
fn try_from(pubkey: &PublicKey) -> Result<Self, Self::Error> {
Ok(nostr_sdk::PublicKey::from_slice(&pubkey.to_bytes())?)
}
}
#[cfg(feature = "nostr")]
impl TryFrom<nostr_sdk::PublicKey> for PublicKey {
type Error = Error;
fn try_from(pubkey: nostr_sdk::PublicKey) -> Result<Self, Self::Error> {
(&pubkey).try_into()
}
}
#[cfg(feature = "nostr")]
impl TryFrom<&nostr_sdk::PublicKey> for PublicKey {
type Error = Error;
fn try_from(pubkey: &nostr_sdk::PublicKey) -> Result<Self, Self::Error> {
PublicKey::from_slice(&pubkey.to_bytes())
}
}
impl PublicKey {
/// Parse from `bytes`
#[inline]
@@ -92,16 +61,19 @@ impl PublicKey {
})
}
/// [`PublicKey`] to bytes
#[inline]
pub fn to_bytes(&self) -> [u8; 33] {
self.inner.serialize()
}
/// To uncompressed bytes
#[inline]
pub fn to_uncompressed_bytes(&self) -> [u8; 65] {
self.inner.serialize_uncompressed()
}
/// To [`XOnlyPublicKey`]
#[inline]
pub fn x_only_public_key(&self) -> XOnlyPublicKey {
self.inner.x_only_public_key().0

View File

@@ -13,6 +13,7 @@ use serde::{Deserialize, Deserializer, Serialize};
use super::{Error, PublicKey};
use crate::SECP256K1;
/// SecretKey
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SecretKey {
inner: secp256k1::SecretKey,
@@ -32,22 +33,6 @@ impl From<secp256k1::SecretKey> for SecretKey {
}
}
#[cfg(feature = "nostr")]
impl TryFrom<SecretKey> for nostr_sdk::SecretKey {
type Error = Error;
fn try_from(seckey: SecretKey) -> Result<Self, Self::Error> {
(&seckey).try_into()
}
}
#[cfg(feature = "nostr")]
impl TryFrom<&SecretKey> for nostr_sdk::SecretKey {
type Error = Error;
fn try_from(seckey: &SecretKey) -> Result<Self, Self::Error> {
Ok(nostr_sdk::SecretKey::from_slice(&seckey.to_secret_bytes())?)
}
}
impl fmt::Display for SecretKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_secret_hex())
@@ -105,11 +90,13 @@ impl SecretKey {
self.inner.public_key(&SECP256K1).into()
}
/// [`SecretKey`] to [`Scalar`]
#[inline]
pub fn to_scalar(self) -> Scalar {
Scalar::from(self.inner)
}
/// [`SecretKey`] as [`Scalar`]
#[inline]
pub fn as_scalar(&self) -> Scalar {
Scalar::from(self.inner)

View File

@@ -30,30 +30,39 @@ use crate::util::hex;
#[cfg(feature = "mint")]
use crate::Amount;
/// NUT02 Error
#[derive(Debug, Error)]
pub enum Error {
/// Hex Error
#[error(transparent)]
HexError(#[from] hex::Error),
/// Keyset length error
#[error("NUT02: ID length invalid")]
Length,
/// Unknown version
#[error("NUT02: Unknown Version")]
UnknownVersion,
/// Slice Error
#[error(transparent)]
Slice(#[from] TryFromSliceError),
}
/// Keyset version
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum KeySetVersion {
/// Current Version 00
Version00,
}
impl KeySetVersion {
/// [`KeySetVersion`] to byte
pub fn to_byte(&self) -> u8 {
match self {
Self::Version00 => 0,
}
}
/// [`KeySetVersion`] from byte
pub fn from_byte(byte: &u8) -> Result<Self, Error> {
match byte {
0 => Ok(Self::Version00),
@@ -84,10 +93,12 @@ impl Id {
const STRLEN: usize = 14;
const BYTELEN: usize = 7;
/// [`Id`] to bytes
pub fn to_bytes(&self) -> Vec<u8> {
[vec![self.version.to_byte()], self.id.to_vec()].concat()
}
/// [`Id`] from bytes
pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
Ok(Self {
version: KeySetVersion::from_byte(&bytes[0])?,
@@ -220,6 +231,7 @@ pub struct KeysetResponse {
}
impl KeysetResponse {
/// Create new [`KeysetResponse`]
pub fn new(keysets: Vec<KeySet>) -> Self {
Self {
keysets: keysets.into_iter().map(|keyset| keyset.into()).collect(),
@@ -227,10 +239,14 @@ impl KeysetResponse {
}
}
/// Keyset
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct KeySet {
/// Keyset [`Id`]
pub id: Id,
/// Keyset [`CurrencyUnit`]
pub unit: CurrencyUnit,
/// Keyset [`Keys`]
pub keys: Keys,
}
@@ -245,10 +261,15 @@ impl From<MintKeySet> for KeySet {
}
}
/// KeySetInfo
#[derive(Debug, Clone, Hash, PartialEq, Eq, Deserialize, Serialize)]
pub struct KeySetInfo {
/// Keyset [`Id`]
pub id: Id,
/// Keyset [`CurrencyUnit`]
pub unit: CurrencyUnit,
/// Keyset state
/// Mint will only sign from an active keyset
pub active: bool,
}
@@ -262,16 +283,21 @@ impl From<KeySet> for KeySetInfo {
}
}
/// MintKeyset
#[cfg(feature = "mint")]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct MintKeySet {
/// Keyset [`Id`]
pub id: Id,
/// Keyset [`CurrencyUnit`]
pub unit: CurrencyUnit,
/// Keyset [`MintKeys`]
pub keys: MintKeys,
}
#[cfg(feature = "mint")]
impl MintKeySet {
/// Generate new [`MintKeySet`]
pub fn generate<C: secp256k1::Signing>(
secp: &Secp256k1<C>,
xpriv: ExtendedPrivKey,
@@ -306,6 +332,7 @@ impl MintKeySet {
}
}
/// Generate new [`MintKeySet`] from seed
pub fn generate_from_seed<C: secp256k1::Signing>(
secp: &Secp256k1<C>,
seed: &[u8],
@@ -325,6 +352,7 @@ impl MintKeySet {
)
}
/// Generate new [`MintKeySet`] from xpriv
pub fn generate_from_xpriv<C: secp256k1::Signing>(
secp: &Secp256k1<C>,
xpriv: ExtendedPrivKey,

View File

@@ -7,9 +7,12 @@ use serde::{Deserialize, Serialize};
use super::nut00::{BlindSignature, BlindedMessage, PreMintSecrets, Proofs};
use crate::Amount;
/// Preswap information
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct PreSwap {
/// Preswap mint secrets
pub pre_mint_secrets: PreMintSecrets,
/// Swap request
pub swap_request: SwapRequest,
}
@@ -23,16 +26,17 @@ pub struct SwapRequest {
}
impl SwapRequest {
/// Create new [`SwapRequest`]
pub fn new(inputs: Proofs, outputs: Vec<BlindedMessage>) -> Self {
Self { inputs, outputs }
}
/// Total value of proofs in `SplitRequest`
/// Total value of proofs in [`SwapRequest`]
pub fn input_amount(&self) -> Amount {
self.inputs.iter().map(|proof| proof.amount).sum()
}
/// Total value of outputs in `SplitRequest`
/// Total value of outputs in [`SwapRequest`]
pub fn output_amount(&self) -> Amount {
self.outputs.iter().map(|proof| proof.amount).sum()
}
@@ -46,12 +50,14 @@ pub struct SwapResponse {
}
impl SwapResponse {
/// Create new [`SwapRequest`]
pub fn new(promises: Vec<BlindSignature>) -> SwapResponse {
SwapResponse {
signatures: promises,
}
}
/// Total [`Amount`] of promises
pub fn promises_amount(&self) -> Amount {
self.signatures
.iter()

View File

@@ -14,6 +14,7 @@ use super::MintQuoteState;
use crate::types::MintQuote;
use crate::Amount;
/// NUT04 Error
#[derive(Debug, Error)]
pub enum Error {
/// Unknown Quote State
@@ -34,10 +35,14 @@ pub struct MintQuoteBolt11Request {
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum QuoteState {
/// Quote has not been paid
#[default]
Unpaid,
/// Quote has been paid and wallet can mint
Paid,
/// Minting is in progress
Pending,
/// ecash issued for quote
Issued,
}
@@ -171,6 +176,7 @@ pub struct MintBolt11Request {
}
impl MintBolt11Request {
/// Total [`Amount`] of outputs
pub fn total_amount(&self) -> Amount {
self.outputs
.iter()

View File

@@ -14,6 +14,7 @@ use super::nut15::Mpp;
use crate::types::MeltQuote;
use crate::{Amount, Bolt11Invoice};
/// NUT05 Error
#[derive(Debug, Error)]
pub enum Error {
/// Unknown Quote State
@@ -36,9 +37,12 @@ pub struct MeltQuoteBolt11Request {
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum QuoteState {
/// Quote has not been paid
#[default]
Unpaid,
/// Quote has been paid
Paid,
/// Paying quote is in progress
Pending,
}
@@ -203,6 +207,7 @@ pub struct MeltBolt11Request {
}
impl MeltBolt11Request {
/// Total [`Amount`] of [`Proofs`]
pub fn proofs_amount(&self) -> Amount {
self.inputs.iter().map(|proof| proof.amount).sum()
}

View File

@@ -13,7 +13,9 @@ use super::{nut04, nut05, nut15};
/// Mint Version
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct MintVersion {
/// Mint Software name
pub name: String,
/// Mint Version
pub version: String,
}
@@ -76,36 +78,47 @@ pub struct MintInfo {
/// Supported nuts and settings
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Nuts {
/// NUT04 Settings
#[serde(default)]
#[serde(rename = "4")]
pub nut04: nut04::Settings,
/// NUT05 Settings
#[serde(default)]
#[serde(rename = "5")]
pub nut05: nut05::Settings,
/// NUT07 Settings
#[serde(default)]
#[serde(rename = "7")]
pub nut07: SupportedSettings,
/// NUT08 Settings
#[serde(default)]
#[serde(rename = "8")]
pub nut08: SupportedSettings,
/// NUT09 Settings
#[serde(default)]
#[serde(rename = "9")]
pub nut09: SupportedSettings,
/// NUT10 Settings
#[serde(rename = "10")]
#[serde(default)]
pub nut10: SupportedSettings,
/// NUT11 Settings
#[serde(rename = "11")]
#[serde(default)]
pub nut11: SupportedSettings,
/// NUT12 Settings
#[serde(default)]
#[serde(rename = "12")]
pub nut12: SupportedSettings,
/// NUT13 Settings
#[serde(default)]
#[serde(rename = "13")]
pub nut13: SupportedSettings,
/// NUT14 Settings
#[serde(default)]
#[serde(rename = "14")]
pub nut14: SupportedSettings,
/// NUT15 Settings
#[serde(default)]
#[serde(rename = "15")]
pub nut15: nut15::MppMethodSettings,

View File

@@ -18,12 +18,21 @@ pub enum Error {
UnknownState,
}
/// State of Proof
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum State {
/// Spent
Spent,
/// Unspent
Unspent,
/// Pending
///
/// Currently being used in a transaction i.e. melt in progress
Pending,
/// Proof is reserved
///
/// i.e. used to create a token
Reserved,
}
@@ -77,5 +86,6 @@ pub struct ProofState {
/// Check Spendable Response [NUT-07]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CheckStateResponse {
/// Proof states
pub states: Vec<ProofState>,
}

View File

@@ -6,6 +6,7 @@ use super::nut05::{MeltBolt11Request, MeltQuoteBolt11Response};
use crate::Amount;
impl MeltBolt11Request {
/// Total output [`Amount`]
pub fn output_amount(&self) -> Option<Amount> {
self.outputs
.as_ref()
@@ -14,6 +15,7 @@ impl MeltBolt11Request {
}
impl MeltQuoteBolt11Response {
/// Total change [`Amount`]
pub fn change_amount(&self) -> Option<Amount> {
self.change
.as_ref()

View File

@@ -18,6 +18,7 @@ pub enum Kind {
HTLC,
}
/// Secert Date
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct SecretData {
/// Unique random string
@@ -29,6 +30,7 @@ pub struct SecretData {
pub tags: Option<Vec<Vec<String>>>,
}
/// NUT10 Secret
#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize)]
pub struct Secret {
/// Kind of the spending condition
@@ -38,6 +40,7 @@ pub struct Secret {
}
impl Secret {
/// Create new [`Secret`]
pub fn new<S, V>(kind: Kind, data: S, tags: Option<V>) -> Self
where
S: Into<String>,

View File

@@ -23,6 +23,7 @@ use crate::util::{hex, unix_time};
pub mod serde_p2pk_witness;
/// Nut11 Error
#[derive(Debug, Error)]
pub enum Error {
/// Incorrect secret kind
@@ -84,11 +85,13 @@ pub enum Error {
/// P2Pk Witness
#[derive(Default, Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct P2PKWitness {
/// Signatures
pub signatures: Vec<String>,
}
impl P2PKWitness {
#[inline]
/// Check id Witness is empty
pub fn is_empty(&self) -> bool {
self.signatures.is_empty()
}
@@ -250,16 +253,27 @@ impl BlindedMessage {
}
}
/// Spending Conditions
///
/// Defined in [NUT10](https://github.com/cashubtc/nuts/blob/main/10.md)
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum SpendingConditions {
/// NUT11 Spending conditions
///
/// Defined in [NUT11](https://github.com/cashubtc/nuts/blob/main/11.md)
P2PKConditions {
/// The public key of the recipient of the locked ecash
data: PublicKey,
/// Additional Optional Spending [`Conditions`]
conditions: Option<Conditions>,
},
/// NUT14 Spending conditions
///
/// Dedined in [NUT14](https://github.com/cashubtc/nuts/blob/main/14.md)
HTLCConditions {
/// Hash Lock of ecash
data: Sha256Hash,
/// Additional Optional Spending [`Conditions`]
conditions: Option<Conditions>,
},
}
@@ -291,6 +305,7 @@ impl SpendingConditions {
}
}
/// Number if signatures required to unlock
pub fn num_sigs(&self) -> Option<u64> {
match self {
Self::P2PKConditions { conditions, .. } => conditions.as_ref().and_then(|c| c.num_sigs),
@@ -298,6 +313,7 @@ impl SpendingConditions {
}
}
/// Public keys of locked [`Proof`]
pub fn pubkeys(&self) -> Option<Vec<PublicKey>> {
match self {
Self::P2PKConditions { data, conditions } => {
@@ -312,6 +328,7 @@ impl SpendingConditions {
}
}
/// Locktime of Spending Conditions
pub fn locktime(&self) -> Option<u64> {
match self {
Self::P2PKConditions { conditions, .. } => conditions.as_ref().and_then(|c| c.locktime),
@@ -319,6 +336,7 @@ impl SpendingConditions {
}
}
/// Refund keys
pub fn refund_keys(&self) -> Option<Vec<PublicKey>> {
match self {
Self::P2PKConditions { conditions, .. } => {
@@ -373,18 +391,28 @@ impl From<SpendingConditions> for super::nut10::Secret {
/// P2PK and HTLC spending conditions
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
pub struct Conditions {
/// Unix locktime after which refund keys can be used
#[serde(skip_serializing_if = "Option::is_none")]
pub locktime: Option<u64>,
/// Additional Public keys
#[serde(skip_serializing_if = "Option::is_none")]
pub pubkeys: Option<Vec<PublicKey>>,
/// Refund keys
#[serde(skip_serializing_if = "Option::is_none")]
pub refund_keys: Option<Vec<PublicKey>>,
/// Numbedr of signatures required
///
/// Default is 1
#[serde(skip_serializing_if = "Option::is_none")]
pub num_sigs: Option<u64>,
/// Signature flag
///
/// Default [`SigFlag::SigInputs`]
pub sig_flag: SigFlag,
}
impl Conditions {
/// Create new Spending [`Conditions`]
pub fn new(
locktime: Option<u64>,
pubkeys: Option<Vec<PublicKey>>,
@@ -499,7 +527,7 @@ impl TryFrom<Vec<Vec<String>>> for Conditions {
}
}
// P2PK and HTLC Spending condition tags
/// P2PK and HTLC Spending condition tags
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
#[serde(rename_all = "lowercase")]
pub enum TagKind {
@@ -547,10 +575,16 @@ where
}
}
/// Signature flag
///
/// Defined in [NUT11](https://github.com/cashubtc/nuts/blob/main/11.md)
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord, Hash)]
pub enum SigFlag {
#[default]
/// Requires valid signatures on all inputs.
/// It is the default signature flag and will be applied even if the `sigflag` tag is absent.
SigInputs,
/// Requires valid signatures on all inputs and on all outputs.
SigAll,
}
@@ -574,6 +608,7 @@ impl FromStr for SigFlag {
}
}
/// Get the signature flag that should be enforced for a set of proofs and the public keys that signatures are valid for
pub fn enforce_sig_flag(proofs: Proofs) -> (SigFlag, HashSet<PublicKey>) {
let mut sig_flag = SigFlag::SigInputs;
let mut pubkeys = HashSet::new();
@@ -602,16 +637,23 @@ pub fn enforce_sig_flag(proofs: Proofs) -> (SigFlag, HashSet<PublicKey>) {
(sig_flag, pubkeys)
}
/// Tag
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub enum Tag {
/// Sigflag [`Tag`]
SigFlag(SigFlag),
/// Number of Sigs [`Tag`]
NSigs(u64),
/// Locktime [`Tag`]
LockTime(u64),
/// Refund [`Tag`]
Refund(Vec<PublicKey>),
/// Pubkeys [`Tag`]
PubKeys(Vec<PublicKey>),
}
impl Tag {
/// Get [`Tag`] Kind
pub fn kind(&self) -> TagKind {
match self {
Self::SigFlag(_) => TagKind::SigFlag,

View File

@@ -14,36 +14,55 @@ use super::nut02::Id;
use crate::dhke::{hash_e, hash_to_curve};
use crate::{Amount, SECP256K1};
/// NUT12 Error
#[derive(Debug, Error)]
pub enum Error {
/// Missing Dleq Proof
#[error("No Dleq Proof provided")]
MissingDleqProof,
/// Incomplete Dleq Proof
#[error("Incomplete DLEQ Proof")]
IncompleteDleqProof,
/// Invalid Dleq Proof
#[error("Invalid Dleq Prood")]
InvalidDleqProof,
/// Cashu Error
#[error(transparent)]
Cashu(#[from] crate::error::Error),
/// NUT01 Error
#[error(transparent)]
NUT01(#[from] crate::nuts::nut01::Error),
/// SECP256k1 Error
#[error(transparent)]
Secp256k1(#[from] secp256k1::Error),
}
/// Blinded Signature on Dleq
///
/// Defined in [NUT12](https://github.com/cashubtc/nuts/blob/main/12.md)
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct BlindSignatureDleq {
/// e
pub e: SecretKey,
/// s
pub s: SecretKey,
}
/// Proof Dleq
///
/// Defined in [NUT12](https://github.com/cashubtc/nuts/blob/main/12.md)
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ProofDleq {
/// e
pub e: SecretKey,
/// s
pub s: SecretKey,
/// Blinding factor
pub r: SecretKey,
}
impl ProofDleq {
/// Create new [`ProofDleq`]
pub fn new(e: SecretKey, s: SecretKey, r: SecretKey) -> Self {
Self { e, s, r }
}
@@ -119,6 +138,7 @@ fn calculate_dleq(
}
impl Proof {
/// Verify proof Dleq
pub fn verify_dleq(&self, mint_pubkey: PublicKey) -> Result<(), Error> {
match &self.dleq {
Some(dleq) => {
@@ -165,6 +185,7 @@ impl BlindSignature {
})
}
/// Verify dleq on proof
#[inline]
pub fn verify_dleq(
&self,
@@ -177,6 +198,7 @@ impl BlindSignature {
}
}
/// Add Dleq to proof
/*
r = random nonce
R1 = r*G

View File

@@ -15,6 +15,7 @@ use crate::util::hex;
use crate::{Amount, SECP256K1};
impl Secret {
/// Create new [`Secret`] from xpriv
pub fn from_xpriv(xpriv: ExtendedPrivKey, keyset_id: Id, counter: u32) -> Result<Self, Error> {
let path = derive_path_from_keyset_id(keyset_id)?
.child(ChildNumber::from_hardened_idx(counter)?)
@@ -28,6 +29,7 @@ impl Secret {
}
impl SecretKey {
/// Create new [`SecretKey`] from xpriv
pub fn from_xpriv(xpriv: ExtendedPrivKey, keyset_id: Id, counter: u32) -> Result<Self, Error> {
let path = derive_path_from_keyset_id(keyset_id)?
.child(ChildNumber::from_hardened_idx(counter)?)
@@ -74,6 +76,7 @@ impl PreMintSecrets {
Ok(pre_mint_secrets)
}
/// New [`PreMintSecrets`] from xpriv with a zero amount used for change
pub fn from_xpriv_blank(
keyset_id: Id,
counter: u32,

View File

@@ -18,6 +18,7 @@ use crate::util::unix_time;
pub mod serde_htlc_witness;
/// NUT14 Errors
#[derive(Debug, Error)]
pub enum Error {
/// Incorrect secret kind
@@ -42,12 +43,16 @@ pub enum Error {
#[error(transparent)]
NUT11(#[from] super::nut11::Error),
#[error(transparent)]
/// Serde Error
Serde(#[from] serde_json::Error),
}
/// HTLC Witness
#[derive(Default, Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct HTLCWitness {
/// Primage
pub preimage: String,
/// Signatures
#[serde(skip_serializing_if = "Option::is_none")]
pub signatures: Option<Vec<String>>,
}
@@ -125,6 +130,7 @@ impl Proof {
Ok(())
}
/// Add Preimage
#[inline]
pub fn add_preimage(&mut self, preimage: String) {
self.witness = Some(Witness::HTLCWitness(HTLCWitness {

View File

@@ -1,3 +1,5 @@
//! Serde helpers for HTLC Witness
use serde::{de, ser, Deserialize, Deserializer, Serializer};
use super::HTLCWitness;

View File

@@ -7,9 +7,11 @@ use serde::{Deserialize, Serialize};
use super::{CurrencyUnit, PaymentMethod};
use crate::Amount;
/// Multi-part payment
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename = "lowercase")]
pub struct Mpp {
/// Amount
pub amount: Amount,
}
@@ -27,5 +29,6 @@ pub struct MppMethodSettings {
/// Mpp Settings
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Settings {
/// Method settings
pub methods: Vec<MppMethodSettings>,
}

View File

@@ -1,6 +1,6 @@
//! Secret
use core::fmt;
use std::fmt;
use std::str::FromStr;
use bitcoin::secp256k1::rand::{self, RngCore};
@@ -14,10 +14,13 @@ use crate::util::hex;
#[serde(transparent)]
pub struct Secret(String);
/// Secret Errors
#[derive(Debug, Error)]
pub enum Error {
/// Invalid Length
#[error("Invalid secret length: `{0}`")]
InvalidLength(u64),
/// Hex Error
#[error(transparent)]
Hex(#[from] hex::Error),
}
@@ -29,6 +32,7 @@ impl Default for Secret {
}
impl Secret {
/// Create new [`Secret`]
#[inline]
pub fn new<S>(secret: S) -> Self
where
@@ -51,16 +55,19 @@ impl Secret {
Self(secret)
}
/// [`Secret`] as bytes
#[inline]
pub fn as_bytes(&self) -> &[u8] {
self.0.as_bytes()
}
/// [`Secret`] to bytes
#[inline]
pub fn to_bytes(&self) -> Vec<u8> {
self.as_bytes().to_vec()
}
/// Check if secret is P2PK secret
pub fn is_p2pk(&self) -> bool {
use crate::nuts::Kind;

View File

@@ -14,24 +14,35 @@ use crate::Amount;
/// Melt response with proofs
#[derive(Debug, Clone, Hash, PartialEq, Eq, Default, Serialize, Deserialize)]
pub struct Melted {
/// State of quote
pub state: MeltQuoteState,
/// Preimage of melt payment
pub preimage: Option<String>,
/// Melt change
pub change: Option<Proofs>,
}
/// Mint Quote Info
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub struct MintQuote {
/// Quote id
pub id: String,
/// Mint Url
pub mint_url: UncheckedUrl,
/// Amount of quote
pub amount: Amount,
/// Unit of quote
pub unit: CurrencyUnit,
/// Quote payment request e.g. bolt11
pub request: String,
/// Quote state
pub state: MintQuoteState,
/// Expiration time of quote
pub expiry: u64,
}
impl MintQuote {
/// Create new [`MintQuote`]
pub fn new(
mint_url: UncheckedUrl,
request: String,
@@ -56,18 +67,27 @@ impl MintQuote {
/// Melt Quote Info
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub struct MeltQuote {
/// Quote id
pub id: String,
/// Quote unit
pub unit: CurrencyUnit,
/// Quote amount
pub amount: Amount,
/// Quote Payment request e.g. bolt11
pub request: String,
/// Quote fee reserve
pub fee_reserve: Amount,
/// Quote state
pub state: MeltQuoteState,
/// Expiration time of quote
pub expiry: u64,
/// Payment preimage
pub payment_preimage: Option<String>,
}
#[cfg(feature = "mint")]
impl MeltQuote {
/// Create new [`MeltQuote`]
pub fn new(
request: String,
unit: CurrencyUnit,
@@ -90,17 +110,25 @@ impl MeltQuote {
}
}
/// Prooinfo
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub struct ProofInfo {
/// Proof
pub proof: Proof,
/// y
pub y: PublicKey,
/// Mint Url
pub mint_url: UncheckedUrl,
/// Proof State
pub state: State,
/// Proof Spending Conditions
pub spending_condition: Option<SpendingConditions>,
/// Unit
pub unit: CurrencyUnit,
}
impl ProofInfo {
/// Create new [`ProofInfo`]
pub fn new(
proof: Proof,
mint_url: UncheckedUrl,
@@ -123,6 +151,7 @@ impl ProofInfo {
})
}
/// Check if [`Proof`] matches conditions
pub fn matches_conditions(
&self,
mint_url: &Option<UncheckedUrl>,

View File

@@ -36,6 +36,7 @@ impl UncheckedUrl {
Self(String::new())
}
/// Join onto url
pub fn join(&self, path: &str) -> Result<Url, Error> {
let url: Url = self.try_into()?;
Ok(url.join(path)?)

View File

@@ -36,6 +36,7 @@ fn join_url(url: Url, paths: &[&str]) -> Result<Url, Error> {
Ok(url)
}
/// Http Client
#[derive(Debug, Clone)]
pub struct HttpClient {
inner: Client,
@@ -48,6 +49,7 @@ impl Default for HttpClient {
}
impl HttpClient {
/// Create new [`HttpClient`]
pub fn new() -> Self {
Self {
inner: Client::new(),
@@ -321,6 +323,7 @@ impl HttpClient {
}
}
/// Restore request [NUT-13]
#[instrument(skip(self, request), fields(mint_url = %mint_url))]
pub async fn post_restore(
&self,

View File

@@ -1,3 +1,5 @@
//! CDK Wallet Error
use std::num::ParseIntError;
use thiserror::Error;
@@ -5,6 +7,7 @@ use thiserror::Error;
use crate::cdk_database;
use crate::error::{ErrorCode, ErrorResponse};
/// Wallet Error
#[derive(Debug, Error)]
pub enum Error {
/// Insufficient Funds
@@ -31,6 +34,7 @@ pub enum Error {
/// Preimage not provided
#[error("Preimage not provided")]
PreimageNotProvided,
/// Unknown Key
#[error("Unknown Key")]
UnknownKey,
/// Spending Locktime not provided
@@ -102,14 +106,6 @@ pub enum Error {
/// Serde Error
#[error(transparent)]
Serde(#[from] serde_json::Error),
/// Nostr Client Error
#[cfg(feature = "nostr")]
#[error(transparent)]
NostrClient(#[from] nostr_sdk::client::Error),
/// Nostr Key Error
#[cfg(feature = "nostr")]
#[error(transparent)]
NostrKey(#[from] nostr_sdk::key::Error),
/// Custom Error
#[error("`{0}`")]
Custom(String),

View File

@@ -34,16 +34,21 @@ pub mod error;
pub mod multi_mint_wallet;
pub mod util;
/// CDK Wallet
#[derive(Debug, Clone)]
pub struct Wallet {
/// Mint Url
pub mint_url: UncheckedUrl,
/// Unit
pub unit: CurrencyUnit,
pub client: HttpClient,
client: HttpClient,
/// Storage backend
pub localstore: Arc<dyn WalletDatabase<Err = cdk_database::Error> + Send + Sync>,
xpriv: ExtendedPrivKey,
}
impl Wallet {
/// Create new [`Wallet`]
pub fn new(
mint_url: &str,
unit: CurrencyUnit,
@@ -278,6 +283,7 @@ impl Wallet {
Ok(())
}
/// Get Active mint keyset id
#[instrument(skip(self))]
pub async fn active_mint_keyset(&self) -> Result<Id, Error> {
let mint_url = &self.mint_url;
@@ -307,6 +313,7 @@ impl Wallet {
Err(Error::NoActiveKeyset)
}
/// Get active mint keys
#[instrument(skip(self))]
pub async fn active_keys(&self) -> Result<Option<Keys>, Error> {
let active_keyset_id = self.active_mint_keyset().await?;
@@ -1044,7 +1051,7 @@ impl Wallet {
Ok(melted)
}
// Select proofs
/// Select proofs
#[instrument(skip(self))]
pub async fn select_proofs(
&self,
@@ -1313,6 +1320,7 @@ impl Wallet {
Ok(total_amount)
}
/// Restore
#[instrument(skip(self))]
pub async fn restore(&self) -> Result<Amount, Error> {
// Check that mint is in store of mints

View File

@@ -17,11 +17,14 @@ use crate::nuts::{CurrencyUnit, SecretKey, SpendingConditions, Token};
use crate::types::{Melted, MintQuote};
use crate::{Amount, UncheckedUrl, Wallet};
/// Multi Mint Wallet
#[derive(Debug, Clone)]
pub struct MultiMintWallet {
/// Wallets
pub wallets: Arc<Mutex<HashMap<WalletKey, Wallet>>>,
}
/// Wallet Key
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub struct WalletKey {
mint_url: UncheckedUrl,
@@ -35,6 +38,7 @@ impl fmt::Display for WalletKey {
}
impl WalletKey {
/// Create new [`WalletKey`]
pub fn new(mint_url: UncheckedUrl, unit: CurrencyUnit) -> Self {
Self { mint_url, unit }
}
@@ -255,7 +259,7 @@ impl MultiMintWallet {
wallet.melt(&quote.id, SplitTarget::default()).await
}
// Restore
/// Restore
#[instrument(skip(self))]
pub async fn restore(&self, wallet_key: &WalletKey) -> Result<Amount, Error> {
let wallet = self

View File

@@ -27,7 +27,6 @@ pub fn proof_to_token(
Ok(Token::new(mint_url, proofs, memo, unit)?)
}
#[cfg(feature = "nostr")]
#[cfg(test)]
mod tests {