mirror of
https://github.com/aljazceru/cdk.git
synced 2025-12-20 06:05:09 +01:00
chore: readmes
chore: doc comments on public
This commit is contained in:
14
README.md
14
README.md
@@ -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/):
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
23
crates/cdk-redb/README.md
Normal 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
|
||||
@@ -1,7 +1,10 @@
|
||||
//! Redb Error
|
||||
|
||||
use std::num::ParseIntError;
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
/// Redb Database Error
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
/// Redb Error
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))]
|
||||
|
||||
22
crates/cdk-rexie/README.md
Normal file
22
crates/cdk-rexie/README.md
Normal 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.
|
||||
@@ -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;
|
||||
|
||||
@@ -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(
|
||||
|
||||
23
crates/cdk-sqlite/README.md
Normal file
23
crates/cdk-sqlite/README.md
Normal 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.
|
||||
@@ -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;
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
//! SQLite Database Error
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
/// SQLite Database Error
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
/// SQLX Error
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
//! SQLite Wallet Error
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
/// SQLite Wallet Error
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
/// SQLX Error
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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::*;
|
||||
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
//! Nuts
|
||||
//!
|
||||
//! See all at <https://github.com/cashubtc/nuts>
|
||||
|
||||
pub mod nut00;
|
||||
pub mod nut01;
|
||||
pub mod nut02;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>,
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! Serde helpers for HTLC Witness
|
||||
|
||||
use serde::{de, ser, Deserialize, Deserializer, Serializer};
|
||||
|
||||
use super::HTLCWitness;
|
||||
|
||||
@@ -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>,
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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)?)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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("e.id, SplitTarget::default()).await
|
||||
}
|
||||
|
||||
// Restore
|
||||
/// Restore
|
||||
#[instrument(skip(self))]
|
||||
pub async fn restore(&self, wallet_key: &WalletKey) -> Result<Amount, Error> {
|
||||
let wallet = self
|
||||
|
||||
@@ -27,7 +27,6 @@ pub fn proof_to_token(
|
||||
Ok(Token::new(mint_url, proofs, memo, unit)?)
|
||||
}
|
||||
|
||||
#[cfg(feature = "nostr")]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user