mirror of
https://github.com/aljazceru/cdk.git
synced 2025-12-20 14:14:49 +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.
|
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/):
|
## Implemented [NUTs](https://github.com/cashubtc/nuts/):
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use cdk::amount::SplitTarget;
|
|||||||
use cdk::nuts::{Proofs, SecretKey};
|
use cdk::nuts::{Proofs, SecretKey};
|
||||||
use cdk::wallet::Wallet;
|
use cdk::wallet::Wallet;
|
||||||
use cdk::Amount;
|
use cdk::Amount;
|
||||||
use cdk_rexie::RexieWalletDatabase;
|
use cdk_rexie::WalletRexieDatabase;
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
use crate::error::{into_err, Result};
|
use crate::error::{into_err, Result};
|
||||||
@@ -42,7 +42,7 @@ impl From<Wallet> for JsWallet {
|
|||||||
impl JsWallet {
|
impl JsWallet {
|
||||||
#[wasm_bindgen(constructor)]
|
#[wasm_bindgen(constructor)]
|
||||||
pub async fn new(mints_url: String, unit: JsCurrencyUnit, seed: Vec<u8>) -> Self {
|
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()
|
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::cdk_database::WalletDatabase;
|
||||||
use cdk::wallet::Wallet;
|
use cdk::wallet::Wallet;
|
||||||
use cdk::{cdk_database, UncheckedUrl};
|
use cdk::{cdk_database, UncheckedUrl};
|
||||||
use cdk_redb::RedbWalletDatabase;
|
use cdk_redb::WalletRedbDatabase;
|
||||||
use cdk_sqlite::WalletSQLiteDatabase;
|
use cdk_sqlite::WalletSqliteDatabase;
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@ async fn main() -> Result<()> {
|
|||||||
match args.engine.as_str() {
|
match args.engine.as_str() {
|
||||||
"sqlite" => {
|
"sqlite" => {
|
||||||
let sql_path = work_dir.join("cdk-cli.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;
|
sql.migrate().await;
|
||||||
|
|
||||||
@@ -95,7 +95,7 @@ async fn main() -> Result<()> {
|
|||||||
"redb" => {
|
"redb" => {
|
||||||
let redb_path = work_dir.join("cdk-cli.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"),
|
_ => 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 std::num::ParseIntError;
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
/// Redb Database Error
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Redb Error
|
/// Redb Error
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
//! SQLite Storage backend for CDK
|
||||||
|
|
||||||
|
#![warn(missing_docs)]
|
||||||
|
#![warn(rustdoc::bare_urls)]
|
||||||
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
mod migrations;
|
mod migrations;
|
||||||
|
|
||||||
@@ -9,4 +14,4 @@ pub mod wallet;
|
|||||||
#[cfg(feature = "mint")]
|
#[cfg(feature = "mint")]
|
||||||
pub use mint::MintRedbDatabase;
|
pub use mint::MintRedbDatabase;
|
||||||
#[cfg(feature = "wallet")]
|
#[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::cmp::Ordering;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
@@ -34,12 +36,14 @@ const BLINDED_SIGNATURES: TableDefinition<[u8; 33], &str> =
|
|||||||
|
|
||||||
const DATABASE_VERSION: u32 = 0;
|
const DATABASE_VERSION: u32 = 0;
|
||||||
|
|
||||||
|
/// Mint Redbdatabase
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct MintRedbDatabase {
|
pub struct MintRedbDatabase {
|
||||||
db: Arc<Mutex<Database>>,
|
db: Arc<Mutex<Database>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MintRedbDatabase {
|
impl MintRedbDatabase {
|
||||||
|
/// Create new [`MintRedbDatabase`]
|
||||||
pub fn new(path: &Path) -> Result<Self, Error> {
|
pub fn new(path: &Path) -> Result<Self, Error> {
|
||||||
{
|
{
|
||||||
// Check database version
|
// Check database version
|
||||||
|
|||||||
@@ -41,12 +41,14 @@ const NOSTR_LAST_CHECKED: TableDefinition<&str, u32> = TableDefinition::new("key
|
|||||||
|
|
||||||
const DATABASE_VERSION: u32 = 1;
|
const DATABASE_VERSION: u32 = 1;
|
||||||
|
|
||||||
|
/// Wallet Redb Database
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct RedbWalletDatabase {
|
pub struct WalletRedbDatabase {
|
||||||
db: Arc<Mutex<Database>>,
|
db: Arc<Mutex<Database>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RedbWalletDatabase {
|
impl WalletRedbDatabase {
|
||||||
|
/// Create new [`WalletRedbDatabase`]
|
||||||
pub fn new(path: &Path) -> Result<Self, Error> {
|
pub fn new(path: &Path) -> Result<Self, Error> {
|
||||||
{
|
{
|
||||||
let db = Arc::new(Database::create(path)?);
|
let db = Arc::new(Database::create(path)?);
|
||||||
@@ -132,7 +134,7 @@ impl RedbWalletDatabase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl WalletDatabase for RedbWalletDatabase {
|
impl WalletDatabase for WalletRedbDatabase {
|
||||||
type Err = cdk_database::Error;
|
type Err = cdk_database::Error;
|
||||||
|
|
||||||
#[instrument(skip(self))]
|
#[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"))]
|
#[cfg(all(feature = "wallet", target_arch = "wasm32"))]
|
||||||
pub mod wallet;
|
pub mod wallet;
|
||||||
|
|
||||||
#[cfg(all(feature = "wallet", target_arch = "wasm32"))]
|
#[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::collections::{HashMap, HashSet};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::result::Result;
|
use std::result::Result;
|
||||||
@@ -28,6 +30,7 @@ const NOSTR_LAST_CHECKED: &str = "nostr_last_check";
|
|||||||
|
|
||||||
const DATABASE_VERSION: u32 = 3;
|
const DATABASE_VERSION: u32 = 3;
|
||||||
|
|
||||||
|
/// Rexie Database Error
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// CDK Database Error
|
/// CDK Database Error
|
||||||
@@ -39,6 +42,7 @@ pub enum Error {
|
|||||||
/// Serde Wasm Error
|
/// Serde Wasm Error
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
SerdeBindgen(#[from] serde_wasm_bindgen::Error),
|
SerdeBindgen(#[from] serde_wasm_bindgen::Error),
|
||||||
|
/// NUT00 Error
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
NUT00(cdk::nuts::nut00::Error),
|
NUT00(cdk::nuts::nut00::Error),
|
||||||
}
|
}
|
||||||
@@ -52,16 +56,18 @@ impl From<Error> for cdk::cdk_database::Error {
|
|||||||
unsafe impl Send for Error {}
|
unsafe impl Send for Error {}
|
||||||
unsafe impl Sync for Error {}
|
unsafe impl Sync for Error {}
|
||||||
|
|
||||||
|
/// Wallet Rexie Database
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct RexieWalletDatabase {
|
pub struct WalletRexieDatabase {
|
||||||
db: Rc<Mutex<Rexie>>,
|
db: Rc<Mutex<Rexie>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// These are okay because we never actually send across threads in the browser
|
// These are okay because we never actually send across threads in the browser
|
||||||
unsafe impl Send for RexieWalletDatabase {}
|
unsafe impl Send for WalletRexieDatabase {}
|
||||||
unsafe impl Sync for RexieWalletDatabase {}
|
unsafe impl Sync for WalletRexieDatabase {}
|
||||||
|
|
||||||
impl RexieWalletDatabase {
|
impl WalletRexieDatabase {
|
||||||
|
/// Create new [`WalletRexieDatabase`]
|
||||||
pub async fn new() -> Result<Self, Error> {
|
pub async fn new() -> Result<Self, Error> {
|
||||||
let rexie = Rexie::builder("cdk")
|
let rexie = Rexie::builder("cdk")
|
||||||
.version(DATABASE_VERSION)
|
.version(DATABASE_VERSION)
|
||||||
@@ -102,7 +108,7 @@ impl RexieWalletDatabase {
|
|||||||
|
|
||||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||||
impl WalletDatabase for RexieWalletDatabase {
|
impl WalletDatabase for WalletRexieDatabase {
|
||||||
type Err = cdk::cdk_database::Error;
|
type Err = cdk::cdk_database::Error;
|
||||||
|
|
||||||
async fn add_mint(
|
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")]
|
#[cfg(feature = "mint")]
|
||||||
pub mod mint;
|
pub mod mint;
|
||||||
#[cfg(feature = "wallet")]
|
#[cfg(feature = "wallet")]
|
||||||
@@ -6,4 +11,4 @@ pub mod wallet;
|
|||||||
#[cfg(feature = "mint")]
|
#[cfg(feature = "mint")]
|
||||||
pub use mint::MintSqliteDatabase;
|
pub use mint::MintSqliteDatabase;
|
||||||
#[cfg(feature = "wallet")]
|
#[cfg(feature = "wallet")]
|
||||||
pub use wallet::WalletSQLiteDatabase;
|
pub use wallet::WalletSqliteDatabase;
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
|
//! SQLite Database Error
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
/// SQLite Database Error
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// SQLX Error
|
/// SQLX Error
|
||||||
|
|||||||
@@ -21,12 +21,14 @@ use sqlx::{ConnectOptions, Row};
|
|||||||
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
|
||||||
|
/// Mint SQLite Database
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct MintSqliteDatabase {
|
pub struct MintSqliteDatabase {
|
||||||
pool: SqlitePool,
|
pool: SqlitePool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MintSqliteDatabase {
|
impl MintSqliteDatabase {
|
||||||
|
/// Create new [`MintSqliteDatabase`]
|
||||||
pub async fn new(path: &Path) -> Result<Self, Error> {
|
pub async fn new(path: &Path) -> Result<Self, Error> {
|
||||||
let path = path.to_str().ok_or(Error::InvalidDbPath)?;
|
let path = path.to_str().ok_or(Error::InvalidDbPath)?;
|
||||||
let _conn = SqliteConnectOptions::from_str(path)?
|
let _conn = SqliteConnectOptions::from_str(path)?
|
||||||
@@ -42,6 +44,7 @@ impl MintSqliteDatabase {
|
|||||||
Ok(Self { pool })
|
Ok(Self { pool })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Migrate [`MintSqliteDatabase`]
|
||||||
pub async fn migrate(&self) {
|
pub async fn migrate(&self) {
|
||||||
sqlx::migrate!("./src/mint/migrations")
|
sqlx::migrate!("./src/mint/migrations")
|
||||||
.run(&self.pool)
|
.run(&self.pool)
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
|
//! SQLite Wallet Error
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
/// SQLite Wallet Error
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// SQLX Error
|
/// SQLX Error
|
||||||
|
|||||||
@@ -20,12 +20,14 @@ use sqlx::{ConnectOptions, Row};
|
|||||||
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
|
||||||
|
/// Wallet SQLite Database
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct WalletSQLiteDatabase {
|
pub struct WalletSqliteDatabase {
|
||||||
pool: SqlitePool,
|
pool: SqlitePool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WalletSQLiteDatabase {
|
impl WalletSqliteDatabase {
|
||||||
|
/// Create new [`WalletSqliteDatabase`]
|
||||||
pub async fn new(path: &Path) -> Result<Self, Error> {
|
pub async fn new(path: &Path) -> Result<Self, Error> {
|
||||||
let path = path.to_str().ok_or(Error::InvalidDbPath)?;
|
let path = path.to_str().ok_or(Error::InvalidDbPath)?;
|
||||||
let _conn = SqliteConnectOptions::from_str(path)?
|
let _conn = SqliteConnectOptions::from_str(path)?
|
||||||
@@ -41,6 +43,7 @@ impl WalletSQLiteDatabase {
|
|||||||
Ok(Self { pool })
|
Ok(Self { pool })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Migrate [`WalletSqliteDatabase`]
|
||||||
pub async fn migrate(&self) {
|
pub async fn migrate(&self) {
|
||||||
sqlx::migrate!("./src/wallet/migrations")
|
sqlx::migrate!("./src/wallet/migrations")
|
||||||
.run(&self.pool)
|
.run(&self.pool)
|
||||||
@@ -50,7 +53,7 @@ impl WalletSQLiteDatabase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl WalletDatabase for WalletSQLiteDatabase {
|
impl WalletDatabase for WalletSqliteDatabase {
|
||||||
type Err = cdk_database::Error;
|
type Err = cdk_database::Error;
|
||||||
|
|
||||||
async fn add_mint(
|
async fn add_mint(
|
||||||
|
|||||||
@@ -1,19 +1,23 @@
|
|||||||
|
|
||||||
# Cashu Development Kit
|
# 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/):
|
## Implemented [NUTs](https://github.com/cashubtc/nuts/):
|
||||||
|
|
||||||
- :heavy_check_mark: [NUT-00](https://github.com/cashubtc/nuts/blob/main/00.md)
|
See <https://github.com/cashubtc/cdk/blob/main/README.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)
|
## License
|
||||||
- :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)
|
This project is distributed under the MIT software license - see the [LICENSE](../../LICENSE) file for details
|
||||||
- :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)
|
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
//! CDK Amount
|
||||||
|
//!
|
||||||
|
//! Is any and will be treated as the unit of the wallet
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@@ -8,6 +12,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
pub struct Amount(u64);
|
pub struct Amount(u64);
|
||||||
|
|
||||||
impl Amount {
|
impl Amount {
|
||||||
|
/// Amount zero
|
||||||
pub const ZERO: Amount = Amount(0);
|
pub const ZERO: Amount = Amount(0);
|
||||||
|
|
||||||
/// Split into parts that are powers of two
|
/// Split into parts that are powers of two
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//! Mint in memory database
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@@ -13,6 +15,7 @@ use crate::nuts::{
|
|||||||
use crate::secret::Secret;
|
use crate::secret::Secret;
|
||||||
use crate::types::{MeltQuote, MintQuote};
|
use crate::types::{MeltQuote, MintQuote};
|
||||||
|
|
||||||
|
/// Mint Memory Database
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct MintMemoryDatabase {
|
pub struct MintMemoryDatabase {
|
||||||
active_keysets: Arc<RwLock<HashMap<CurrencyUnit, Id>>>,
|
active_keysets: Arc<RwLock<HashMap<CurrencyUnit, Id>>>,
|
||||||
@@ -25,6 +28,7 @@ pub struct MintMemoryDatabase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl MintMemoryDatabase {
|
impl MintMemoryDatabase {
|
||||||
|
/// Create new [`MintMemoryDatabase`]
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
active_keysets: HashMap<CurrencyUnit, Id>,
|
active_keysets: HashMap<CurrencyUnit, Id>,
|
||||||
|
|||||||
@@ -35,63 +35,90 @@ pub mod wallet_memory;
|
|||||||
#[cfg(feature = "wallet")]
|
#[cfg(feature = "wallet")]
|
||||||
pub use wallet_memory::WalletMemoryDatabase;
|
pub use wallet_memory::WalletMemoryDatabase;
|
||||||
|
|
||||||
|
/// CDK_database error
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
/// Database Error
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Database(Box<dyn std::error::Error + Send + Sync>),
|
Database(Box<dyn std::error::Error + Send + Sync>),
|
||||||
|
/// CDK Error
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Cdk(#[from] crate::error::Error),
|
Cdk(#[from] crate::error::Error),
|
||||||
|
/// NUT01 Error
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
NUT01(#[from] crate::nuts::nut00::Error),
|
NUT01(#[from] crate::nuts::nut00::Error),
|
||||||
|
/// Unknown Quote
|
||||||
#[error("Unknown Quote")]
|
#[error("Unknown Quote")]
|
||||||
UnknownQuote,
|
UnknownQuote,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Wallet Database trait
|
||||||
#[cfg(feature = "wallet")]
|
#[cfg(feature = "wallet")]
|
||||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||||
pub trait WalletDatabase: Debug {
|
pub trait WalletDatabase: Debug {
|
||||||
|
/// Wallet Database Error
|
||||||
type Err: Into<Error> + From<Error>;
|
type Err: Into<Error> + From<Error>;
|
||||||
|
|
||||||
|
/// Add Mint to storage
|
||||||
async fn add_mint(
|
async fn add_mint(
|
||||||
&self,
|
&self,
|
||||||
mint_url: UncheckedUrl,
|
mint_url: UncheckedUrl,
|
||||||
mint_info: Option<MintInfo>,
|
mint_info: Option<MintInfo>,
|
||||||
) -> Result<(), Self::Err>;
|
) -> Result<(), Self::Err>;
|
||||||
|
/// Remove Mint from storage
|
||||||
async fn remove_mint(&self, mint_url: UncheckedUrl) -> Result<(), Self::Err>;
|
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>;
|
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>;
|
async fn get_mints(&self) -> Result<HashMap<UncheckedUrl, Option<MintInfo>>, Self::Err>;
|
||||||
|
/// Update mint url
|
||||||
async fn update_mint_url(
|
async fn update_mint_url(
|
||||||
&self,
|
&self,
|
||||||
old_mint_url: UncheckedUrl,
|
old_mint_url: UncheckedUrl,
|
||||||
new_mint_url: UncheckedUrl,
|
new_mint_url: UncheckedUrl,
|
||||||
) -> Result<(), Self::Err>;
|
) -> Result<(), Self::Err>;
|
||||||
|
|
||||||
|
/// Add mint keyset to storage
|
||||||
async fn add_mint_keysets(
|
async fn add_mint_keysets(
|
||||||
&self,
|
&self,
|
||||||
mint_url: UncheckedUrl,
|
mint_url: UncheckedUrl,
|
||||||
keysets: Vec<KeySetInfo>,
|
keysets: Vec<KeySetInfo>,
|
||||||
) -> Result<(), Self::Err>;
|
) -> Result<(), Self::Err>;
|
||||||
|
/// Get mint keysets for mint url
|
||||||
async fn get_mint_keysets(
|
async fn get_mint_keysets(
|
||||||
&self,
|
&self,
|
||||||
mint_url: UncheckedUrl,
|
mint_url: UncheckedUrl,
|
||||||
) -> Result<Option<Vec<KeySetInfo>>, Self::Err>;
|
) -> 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>;
|
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>;
|
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>;
|
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>;
|
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>;
|
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>;
|
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>;
|
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>;
|
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>;
|
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>;
|
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>;
|
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>;
|
async fn add_proofs(&self, proof_info: Vec<ProofInfo>) -> Result<(), Self::Err>;
|
||||||
|
/// Get proofs from storage
|
||||||
async fn get_proofs(
|
async fn get_proofs(
|
||||||
&self,
|
&self,
|
||||||
mint_url: Option<UncheckedUrl>,
|
mint_url: Option<UncheckedUrl>,
|
||||||
@@ -99,17 +126,23 @@ pub trait WalletDatabase: Debug {
|
|||||||
state: Option<Vec<State>>,
|
state: Option<Vec<State>>,
|
||||||
spending_conditions: Option<Vec<SpendingConditions>>,
|
spending_conditions: Option<Vec<SpendingConditions>>,
|
||||||
) -> Result<Option<Vec<ProofInfo>>, Self::Err>;
|
) -> Result<Option<Vec<ProofInfo>>, Self::Err>;
|
||||||
|
/// Remove proofs from storage
|
||||||
async fn remove_proofs(&self, proofs: &Proofs) -> Result<(), Self::Err>;
|
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>;
|
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>;
|
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>;
|
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(
|
async fn get_nostr_last_checked(
|
||||||
&self,
|
&self,
|
||||||
verifying_key: &PublicKey,
|
verifying_key: &PublicKey,
|
||||||
) -> Result<Option<u32>, Self::Err>;
|
) -> Result<Option<u32>, Self::Err>;
|
||||||
|
/// Update last checked time
|
||||||
async fn add_nostr_last_checked(
|
async fn add_nostr_last_checked(
|
||||||
&self,
|
&self,
|
||||||
verifying_key: PublicKey,
|
verifying_key: PublicKey,
|
||||||
@@ -117,60 +150,88 @@ pub trait WalletDatabase: Debug {
|
|||||||
) -> Result<(), Self::Err>;
|
) -> Result<(), Self::Err>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Mint Database trait
|
||||||
#[cfg(feature = "mint")]
|
#[cfg(feature = "mint")]
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait MintDatabase {
|
pub trait MintDatabase {
|
||||||
|
/// Mint Database Error
|
||||||
type Err: Into<Error> + From<Error>;
|
type Err: Into<Error> + From<Error>;
|
||||||
|
|
||||||
|
/// Add Active Keyset
|
||||||
async fn add_active_keyset(&self, unit: CurrencyUnit, id: Id) -> Result<(), Self::Err>;
|
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>;
|
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>;
|
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>;
|
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>;
|
async fn get_mint_quote(&self, quote_id: &str) -> Result<Option<MintQuote>, Self::Err>;
|
||||||
|
/// Update state of [`MintQuote`]
|
||||||
async fn update_mint_quote_state(
|
async fn update_mint_quote_state(
|
||||||
&self,
|
&self,
|
||||||
quote_id: &str,
|
quote_id: &str,
|
||||||
state: MintQuoteState,
|
state: MintQuoteState,
|
||||||
) -> Result<MintQuoteState, Self::Err>;
|
) -> Result<MintQuoteState, Self::Err>;
|
||||||
|
/// Get all [`MintQuote`]s
|
||||||
async fn get_mint_quotes(&self) -> Result<Vec<MintQuote>, Self::Err>;
|
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>;
|
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>;
|
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>;
|
async fn get_melt_quote(&self, quote_id: &str) -> Result<Option<MeltQuote>, Self::Err>;
|
||||||
|
/// Update [`MeltQuote`] state
|
||||||
async fn update_melt_quote_state(
|
async fn update_melt_quote_state(
|
||||||
&self,
|
&self,
|
||||||
quote_id: &str,
|
quote_id: &str,
|
||||||
state: MeltQuoteState,
|
state: MeltQuoteState,
|
||||||
) -> Result<MeltQuoteState, Self::Err>;
|
) -> Result<MeltQuoteState, Self::Err>;
|
||||||
|
/// Get all [`MeltQuote`]s
|
||||||
async fn get_melt_quotes(&self) -> Result<Vec<MeltQuote>, Self::Err>;
|
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>;
|
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>;
|
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>;
|
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>;
|
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>;
|
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>;
|
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>;
|
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>;
|
async fn add_pending_proofs(&self, proof: Proofs) -> Result<(), Self::Err>;
|
||||||
|
/// Get pending [`Proof`] by secret
|
||||||
async fn get_pending_proof_by_secret(
|
async fn get_pending_proof_by_secret(
|
||||||
&self,
|
&self,
|
||||||
secret: &Secret,
|
secret: &Secret,
|
||||||
) -> Result<Option<Proof>, Self::Err>;
|
) -> 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>;
|
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>;
|
async fn remove_pending_proofs(&self, secret: Vec<&Secret>) -> Result<(), Self::Err>;
|
||||||
|
|
||||||
|
/// Add [`BlindSignature`]
|
||||||
async fn add_blinded_signature(
|
async fn add_blinded_signature(
|
||||||
&self,
|
&self,
|
||||||
blinded_message: PublicKey,
|
blinded_message: PublicKey,
|
||||||
blinded_signature: BlindSignature,
|
blinded_signature: BlindSignature,
|
||||||
) -> Result<(), Self::Err>;
|
) -> Result<(), Self::Err>;
|
||||||
|
/// Get [`BlindSignature`]
|
||||||
async fn get_blinded_signature(
|
async fn get_blinded_signature(
|
||||||
&self,
|
&self,
|
||||||
blinded_message: &PublicKey,
|
blinded_message: &PublicKey,
|
||||||
) -> Result<Option<BlindSignature>, Self::Err>;
|
) -> Result<Option<BlindSignature>, Self::Err>;
|
||||||
|
/// Get [`BlindSignature`]s
|
||||||
async fn get_blinded_signatures(
|
async fn get_blinded_signatures(
|
||||||
&self,
|
&self,
|
||||||
blinded_messages: Vec<PublicKey>,
|
blinded_messages: Vec<PublicKey>,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! Memory Database
|
//! Wallet in memory database
|
||||||
|
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -15,6 +15,7 @@ use crate::types::{MeltQuote, MintQuote, ProofInfo};
|
|||||||
use crate::url::UncheckedUrl;
|
use crate::url::UncheckedUrl;
|
||||||
use crate::util::unix_time;
|
use crate::util::unix_time;
|
||||||
|
|
||||||
|
/// Wallet in Memory Database
|
||||||
#[derive(Default, Debug, Clone)]
|
#[derive(Default, Debug, Clone)]
|
||||||
pub struct WalletMemoryDatabase {
|
pub struct WalletMemoryDatabase {
|
||||||
mints: Arc<RwLock<HashMap<UncheckedUrl, Option<MintInfo>>>>,
|
mints: Arc<RwLock<HashMap<UncheckedUrl, Option<MintInfo>>>>,
|
||||||
@@ -29,6 +30,7 @@ pub struct WalletMemoryDatabase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl WalletMemoryDatabase {
|
impl WalletMemoryDatabase {
|
||||||
|
/// Create new [`WalletMemoryDatabase`]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
mint_quotes: Vec<MintQuote>,
|
mint_quotes: Vec<MintQuote>,
|
||||||
melt_quotes: Vec<MeltQuote>,
|
melt_quotes: Vec<MeltQuote>,
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ use crate::SECP256K1;
|
|||||||
|
|
||||||
const DOMAIN_SEPARATOR: &[u8; 28] = b"Secp256k1_HashToCurve_Cashu_";
|
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> {
|
pub fn hash_to_curve(message: &[u8]) -> Result<PublicKey, Error> {
|
||||||
let msg_to_hash: Vec<u8> = [DOMAIN_SEPARATOR, message].concat();
|
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)
|
Err(Error::NoValidPoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert iterator of [`PublicKey`] to byte array
|
||||||
pub fn hash_e<I>(public_keys: I) -> [u8; 32]
|
pub fn hash_e<I>(public_keys: I) -> [u8; 32]
|
||||||
where
|
where
|
||||||
I: IntoIterator<Item = PublicKey>,
|
I: IntoIterator<Item = PublicKey>,
|
||||||
@@ -158,7 +162,7 @@ pub fn verify_message(
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use core::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ use thiserror::Error;
|
|||||||
|
|
||||||
use crate::util::hex;
|
use crate::util::hex;
|
||||||
|
|
||||||
|
/// CDK Error
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Mint does not have a key for amount
|
/// Mint does not have a key for amount
|
||||||
@@ -77,10 +78,16 @@ pub enum Error {
|
|||||||
CustomError(String),
|
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)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct ErrorResponse {
|
pub struct ErrorResponse {
|
||||||
|
/// Error Code
|
||||||
pub code: ErrorCode,
|
pub code: ErrorCode,
|
||||||
|
/// Human readable Text
|
||||||
pub error: Option<String>,
|
pub error: Option<String>,
|
||||||
|
/// Longer human readable description
|
||||||
pub detail: Option<String>,
|
pub detail: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,12 +104,14 @@ impl fmt::Display for ErrorResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ErrorResponse {
|
impl ErrorResponse {
|
||||||
|
/// Error response from json
|
||||||
pub fn from_json(json: &str) -> Result<Self, serde_json::Error> {
|
pub fn from_json(json: &str) -> Result<Self, serde_json::Error> {
|
||||||
let value: Value = serde_json::from_str(json)?;
|
let value: Value = serde_json::from_str(json)?;
|
||||||
|
|
||||||
Self::from_value(value)
|
Self::from_value(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Error response from json Value
|
||||||
pub fn from_value(value: Value) -> Result<Self, serde_json::Error> {
|
pub fn from_value(value: Value) -> Result<Self, serde_json::Error> {
|
||||||
match serde_json::from_value::<ErrorResponse>(value.clone()) {
|
match serde_json::from_value::<ErrorResponse>(value.clone()) {
|
||||||
Ok(res) => Ok(res),
|
Ok(res) => Ok(res),
|
||||||
@@ -115,11 +124,16 @@ impl ErrorResponse {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Possible Error Codes
|
||||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
|
||||||
pub enum ErrorCode {
|
pub enum ErrorCode {
|
||||||
|
/// Token is already spent
|
||||||
TokenAlreadySpent,
|
TokenAlreadySpent,
|
||||||
|
/// Quote is not paid
|
||||||
QuoteNotPaid,
|
QuoteNotPaid,
|
||||||
|
/// Keyset is not found
|
||||||
KeysetNotFound,
|
KeysetNotFound,
|
||||||
|
/// Unknown error code
|
||||||
Unknown(u16),
|
Unknown(u16),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
extern crate core;
|
//! Rust implementation of the Cashu Protocol
|
||||||
|
|
||||||
pub use bitcoin::hashes::sha256::Hash as Sha256;
|
#![warn(missing_docs)]
|
||||||
pub use bitcoin::secp256k1;
|
#![warn(rustdoc::bare_urls)]
|
||||||
pub use lightning_invoice::{self, Bolt11Invoice};
|
|
||||||
|
|
||||||
pub mod amount;
|
pub mod amount;
|
||||||
pub mod cdk_database;
|
pub mod cdk_database;
|
||||||
@@ -18,15 +17,27 @@ pub mod util;
|
|||||||
#[cfg(feature = "wallet")]
|
#[cfg(feature = "wallet")]
|
||||||
pub mod wallet;
|
pub mod wallet;
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub use bitcoin::secp256k1;
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub use lightning_invoice::{self, Bolt11Invoice};
|
||||||
#[cfg(feature = "mint")]
|
#[cfg(feature = "mint")]
|
||||||
|
#[doc(hidden)]
|
||||||
pub use mint::Mint;
|
pub use mint::Mint;
|
||||||
#[cfg(feature = "wallet")]
|
#[cfg(feature = "wallet")]
|
||||||
|
#[doc(hidden)]
|
||||||
pub use wallet::Wallet;
|
pub use wallet::Wallet;
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
pub use self::amount::Amount;
|
pub use self::amount::Amount;
|
||||||
|
#[doc(hidden)]
|
||||||
pub use self::url::UncheckedUrl;
|
pub use self::url::UncheckedUrl;
|
||||||
|
#[doc(hidden)]
|
||||||
pub use self::util::SECP256K1;
|
pub use self::util::SECP256K1;
|
||||||
#[cfg(feature = "wallet")]
|
#[cfg(feature = "wallet")]
|
||||||
|
#[doc(hidden)]
|
||||||
pub use self::wallet::client::HttpClient;
|
pub use self::wallet::client::HttpClient;
|
||||||
|
|
||||||
|
/// Result
|
||||||
|
#[doc(hidden)]
|
||||||
pub type Result<T, E = Box<dyn std::error::Error>> = std::result::Result<T, E>;
|
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::cdk_database;
|
||||||
use crate::error::{ErrorCode, ErrorResponse};
|
use crate::error::{ErrorCode, ErrorResponse};
|
||||||
|
|
||||||
|
/// CDK Mint Error
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Unknown Keyset
|
/// Unknown Keyset
|
||||||
@@ -14,47 +15,67 @@ pub enum Error {
|
|||||||
/// Inactive Keyset
|
/// Inactive Keyset
|
||||||
#[error("Inactive Keyset")]
|
#[error("Inactive Keyset")]
|
||||||
InactiveKeyset,
|
InactiveKeyset,
|
||||||
|
/// There is not key for amount given
|
||||||
#[error("No key for amount")]
|
#[error("No key for amount")]
|
||||||
AmountKey,
|
AmountKey,
|
||||||
|
/// Amount is not what is expected
|
||||||
#[error("Amount")]
|
#[error("Amount")]
|
||||||
Amount,
|
Amount,
|
||||||
|
/// Duplicate proofs provided
|
||||||
#[error("Duplicate proofs")]
|
#[error("Duplicate proofs")]
|
||||||
DuplicateProofs,
|
DuplicateProofs,
|
||||||
|
/// Token is already spent
|
||||||
#[error("Token Already Spent")]
|
#[error("Token Already Spent")]
|
||||||
TokenAlreadySpent,
|
TokenAlreadySpent,
|
||||||
|
/// Token is already pending
|
||||||
#[error("Token Pending")]
|
#[error("Token Pending")]
|
||||||
TokenPending,
|
TokenPending,
|
||||||
|
/// Quote is not paiud
|
||||||
#[error("Quote not paid")]
|
#[error("Quote not paid")]
|
||||||
UnpaidQuote,
|
UnpaidQuote,
|
||||||
|
/// Quote has already been paid
|
||||||
#[error("Quote is already paid")]
|
#[error("Quote is already paid")]
|
||||||
PaidQuote,
|
PaidQuote,
|
||||||
|
/// Quote is not known
|
||||||
#[error("Unknown quote")]
|
#[error("Unknown quote")]
|
||||||
UnknownQuote,
|
UnknownQuote,
|
||||||
|
/// Quote is pending
|
||||||
#[error("Quote pending")]
|
#[error("Quote pending")]
|
||||||
PendingQuote,
|
PendingQuote,
|
||||||
|
/// ecash already issued for quote
|
||||||
#[error("Quote already issued")]
|
#[error("Quote already issued")]
|
||||||
IssuedQuote,
|
IssuedQuote,
|
||||||
|
/// Unknown secret kind
|
||||||
#[error("Unknown secret kind")]
|
#[error("Unknown secret kind")]
|
||||||
UnknownSecretKind,
|
UnknownSecretKind,
|
||||||
|
/// Multiple units provided
|
||||||
#[error("Cannot have multiple units")]
|
#[error("Cannot have multiple units")]
|
||||||
MultipleUnits,
|
MultipleUnits,
|
||||||
|
/// BlindMessage is already signed
|
||||||
#[error("Blinded Message is already signed")]
|
#[error("Blinded Message is already signed")]
|
||||||
BlindedMessageAlreadySigned,
|
BlindedMessageAlreadySigned,
|
||||||
|
/// Cashu Error
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Cashu(#[from] crate::error::Error),
|
Cashu(#[from] crate::error::Error),
|
||||||
|
/// Secret Error
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Secret(#[from] crate::secret::Error),
|
Secret(#[from] crate::secret::Error),
|
||||||
|
/// NUT00 Error
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
NUT00(#[from] crate::nuts::nut00::Error),
|
NUT00(#[from] crate::nuts::nut00::Error),
|
||||||
|
/// NUT11 Error
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
NUT11(#[from] crate::nuts::nut11::Error),
|
NUT11(#[from] crate::nuts::nut11::Error),
|
||||||
|
/// NUT12 Error
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Nut12(#[from] crate::nuts::nut12::Error),
|
Nut12(#[from] crate::nuts::nut12::Error),
|
||||||
|
/// NUT14 Error
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Nut14(#[from] crate::nuts::nut14::Error),
|
Nut14(#[from] crate::nuts::nut14::Error),
|
||||||
/// Database Error
|
/// Database Error
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Database(#[from] cdk_database::Error),
|
Database(#[from] cdk_database::Error),
|
||||||
|
/// Custom Error
|
||||||
#[error("`{0}`")]
|
#[error("`{0}`")]
|
||||||
Custom(String),
|
Custom(String),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,18 +21,23 @@ use crate::Amount;
|
|||||||
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
|
||||||
|
/// Cashu Mint
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Mint {
|
pub struct Mint {
|
||||||
|
/// Mint Url
|
||||||
pub mint_url: UncheckedUrl,
|
pub mint_url: UncheckedUrl,
|
||||||
mint_info: MintInfo,
|
mint_info: MintInfo,
|
||||||
keysets: Arc<RwLock<HashMap<Id, MintKeySet>>>,
|
keysets: Arc<RwLock<HashMap<Id, MintKeySet>>>,
|
||||||
secp_ctx: Secp256k1<secp256k1::All>,
|
secp_ctx: Secp256k1<secp256k1::All>,
|
||||||
xpriv: ExtendedPrivKey,
|
xpriv: ExtendedPrivKey,
|
||||||
|
/// Mint Expected [`FeeReserve`]
|
||||||
pub fee_reserve: FeeReserve,
|
pub fee_reserve: FeeReserve,
|
||||||
|
/// Mint Storage backend
|
||||||
pub localstore: Arc<dyn MintDatabase<Err = cdk_database::Error> + Send + Sync>,
|
pub localstore: Arc<dyn MintDatabase<Err = cdk_database::Error> + Send + Sync>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mint {
|
impl Mint {
|
||||||
|
/// Create new [`Mint`]
|
||||||
pub async fn new(
|
pub async fn new(
|
||||||
mint_url: &str,
|
mint_url: &str,
|
||||||
seed: &[u8],
|
seed: &[u8],
|
||||||
@@ -844,19 +849,30 @@ impl Mint {
|
|||||||
/// Mint Fee Reserve
|
/// Mint Fee Reserve
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct FeeReserve {
|
pub struct FeeReserve {
|
||||||
|
/// Absolute expected min fee
|
||||||
pub min_fee_reserve: Amount,
|
pub min_fee_reserve: Amount,
|
||||||
|
/// Percentage expected fee
|
||||||
pub percent_fee_reserve: f32,
|
pub percent_fee_reserve: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mint Keyset Info
|
/// Mint Keyset Info
|
||||||
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct MintKeySetInfo {
|
pub struct MintKeySetInfo {
|
||||||
|
/// Keyset [`Id`]
|
||||||
pub id: Id,
|
pub id: Id,
|
||||||
|
/// Keyset [`CurrencyUnit`]
|
||||||
pub unit: CurrencyUnit,
|
pub unit: CurrencyUnit,
|
||||||
|
/// Keyset active or inactive
|
||||||
|
/// Mint will only issue new [`BlindSignature`] on active keysets
|
||||||
pub active: bool,
|
pub active: bool,
|
||||||
|
/// Starting unix time Keyset is valid from
|
||||||
pub valid_from: u64,
|
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>,
|
pub valid_to: Option<u64>,
|
||||||
|
/// [`DerivationPath`] of Keyset
|
||||||
pub derivation_path: DerivationPath,
|
pub derivation_path: DerivationPath,
|
||||||
|
/// Max order of keyset
|
||||||
pub max_order: u8,
|
pub max_order: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
//! Nuts
|
||||||
|
//!
|
||||||
|
//! See all at <https://github.com/cashubtc/nuts>
|
||||||
|
|
||||||
pub mod nut00;
|
pub mod nut00;
|
||||||
pub mod nut01;
|
pub mod nut01;
|
||||||
pub mod nut02;
|
pub mod nut02;
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ use crate::Amount;
|
|||||||
/// List of [Proof]
|
/// List of [Proof]
|
||||||
pub type Proofs = Vec<Proof>;
|
pub type Proofs = Vec<Proof>;
|
||||||
|
|
||||||
|
/// NUT00 Error
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Proofs required
|
/// Proofs required
|
||||||
@@ -141,6 +142,7 @@ pub enum Witness {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Witness {
|
impl Witness {
|
||||||
|
/// Add signatures to [`Witness`]
|
||||||
pub fn add_signatures(&mut self, signatues: Vec<String>) {
|
pub fn add_signatures(&mut self, signatues: Vec<String>) {
|
||||||
match self {
|
match self {
|
||||||
Self::P2PKWitness(p2pk_witness) => p2pk_witness.signatures.extend(signatues),
|
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>> {
|
pub fn signatures(&self) -> Option<Vec<String>> {
|
||||||
match self {
|
match self {
|
||||||
Self::P2PKWitness(witness) => Some(witness.signatures.clone()),
|
Self::P2PKWitness(witness) => Some(witness.signatures.clone()),
|
||||||
@@ -161,6 +164,7 @@ impl Witness {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get preimage from [`Witness`]
|
||||||
pub fn preimage(&self) -> Option<String> {
|
pub fn preimage(&self) -> Option<String> {
|
||||||
match self {
|
match self {
|
||||||
Self::P2PKWitness(_witness) => None,
|
Self::P2PKWitness(_witness) => None,
|
||||||
@@ -191,6 +195,7 @@ pub struct Proof {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Proof {
|
impl Proof {
|
||||||
|
/// Create new [`Proof`]
|
||||||
pub fn new(amount: Amount, keyset_id: Id, secret: Secret, c: PublicKey) -> Self {
|
pub fn new(amount: Amount, keyset_id: Id, secret: Secret, c: PublicKey) -> Self {
|
||||||
Proof {
|
Proof {
|
||||||
amount,
|
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> {
|
pub fn y(&self) -> Result<PublicKey, Error> {
|
||||||
Ok(hash_to_curve(self.secret.as_bytes())?)
|
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)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
|
||||||
pub enum CurrencyUnit {
|
pub enum CurrencyUnit {
|
||||||
|
/// Sat
|
||||||
#[default]
|
#[default]
|
||||||
Sat,
|
Sat,
|
||||||
|
/// Msat
|
||||||
Msat,
|
Msat,
|
||||||
|
/// Usd
|
||||||
Usd,
|
Usd,
|
||||||
|
/// Custom unit
|
||||||
Custom(String),
|
Custom(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,10 +291,13 @@ impl<'de> Deserialize<'de> for CurrencyUnit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Payment Method
|
||||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
|
||||||
pub enum PaymentMethod {
|
pub enum PaymentMethod {
|
||||||
|
/// Bolt11 payment type
|
||||||
#[default]
|
#[default]
|
||||||
Bolt11,
|
Bolt11,
|
||||||
|
/// Custom payment type:
|
||||||
Custom(String),
|
Custom(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,6 +341,7 @@ impl<'de> Deserialize<'de> for PaymentMethod {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// PreMint
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
|
||||||
pub struct PreMint {
|
pub struct PreMint {
|
||||||
/// Blinded message
|
/// Blinded message
|
||||||
@@ -349,8 +366,10 @@ impl PartialOrd for PreMint {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Premint Secrets
|
||||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize)]
|
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize)]
|
||||||
pub struct PreMintSecrets {
|
pub struct PreMintSecrets {
|
||||||
|
/// Secrets
|
||||||
pub secrets: Vec<PreMint>,
|
pub secrets: Vec<PreMint>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -382,6 +401,7 @@ impl PreMintSecrets {
|
|||||||
Ok(PreMintSecrets { secrets: output })
|
Ok(PreMintSecrets { secrets: output })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Outputs from pre defined secrets
|
||||||
pub fn from_secrets(
|
pub fn from_secrets(
|
||||||
keyset_id: Id,
|
keyset_id: Id,
|
||||||
amounts: Vec<Amount>,
|
amounts: Vec<Amount>,
|
||||||
@@ -428,6 +448,7 @@ impl PreMintSecrets {
|
|||||||
Ok(PreMintSecrets { secrets: output })
|
Ok(PreMintSecrets { secrets: output })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Outputs with specific spending conditions
|
||||||
pub fn with_conditions(
|
pub fn with_conditions(
|
||||||
keyset_id: Id,
|
keyset_id: Id,
|
||||||
amount: Amount,
|
amount: Amount,
|
||||||
@@ -457,18 +478,25 @@ impl PreMintSecrets {
|
|||||||
Ok(PreMintSecrets { secrets: output })
|
Ok(PreMintSecrets { secrets: output })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Iterate over secrets
|
||||||
|
#[inline]
|
||||||
pub fn iter(&self) -> impl Iterator<Item = &PreMint> {
|
pub fn iter(&self) -> impl Iterator<Item = &PreMint> {
|
||||||
self.secrets.iter()
|
self.secrets.iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Length of secrets
|
||||||
|
#[inline]
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.secrets.len()
|
self.secrets.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If secrets is empty
|
||||||
|
#[inline]
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.secrets.is_empty()
|
self.secrets.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Totoal amount of secrets
|
||||||
pub fn total_amount(&self) -> Amount {
|
pub fn total_amount(&self) -> Amount {
|
||||||
self.secrets
|
self.secrets
|
||||||
.iter()
|
.iter()
|
||||||
@@ -476,26 +504,38 @@ impl PreMintSecrets {
|
|||||||
.sum()
|
.sum()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// [`BlindedMessage`]s from [`PreMintSecrets`]
|
||||||
|
#[inline]
|
||||||
pub fn blinded_messages(&self) -> Vec<BlindedMessage> {
|
pub fn blinded_messages(&self) -> Vec<BlindedMessage> {
|
||||||
self.iter().map(|pm| pm.blinded_message.clone()).collect()
|
self.iter().map(|pm| pm.blinded_message.clone()).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// [`Secret`]s from [`PreMintSecrets`]
|
||||||
|
#[inline]
|
||||||
pub fn secrets(&self) -> Vec<Secret> {
|
pub fn secrets(&self) -> Vec<Secret> {
|
||||||
self.iter().map(|pm| pm.secret.clone()).collect()
|
self.iter().map(|pm| pm.secret.clone()).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Blinding factor from [`PreMintSecrets`]
|
||||||
|
#[inline]
|
||||||
pub fn rs(&self) -> Vec<SecretKey> {
|
pub fn rs(&self) -> Vec<SecretKey> {
|
||||||
self.iter().map(|pm| pm.r.clone()).collect()
|
self.iter().map(|pm| pm.r.clone()).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Amounts from [`PreMintSecrets`]
|
||||||
|
#[inline]
|
||||||
pub fn amounts(&self) -> Vec<Amount> {
|
pub fn amounts(&self) -> Vec<Amount> {
|
||||||
self.iter().map(|pm| pm.amount).collect()
|
self.iter().map(|pm| pm.amount).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Combine [`PreMintSecrets`]
|
||||||
|
#[inline]
|
||||||
pub fn combine(&mut self, mut other: Self) {
|
pub fn combine(&mut self, mut other: Self) {
|
||||||
self.secrets.append(&mut other.secrets)
|
self.secrets.append(&mut other.secrets)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sort [`PreMintSecrets`] by [`Amount`]
|
||||||
|
#[inline]
|
||||||
pub fn sort_secrets(&mut self) {
|
pub fn sort_secrets(&mut self) {
|
||||||
self.secrets.sort();
|
self.secrets.sort();
|
||||||
}
|
}
|
||||||
@@ -523,8 +563,10 @@ impl PartialOrd for PreMintSecrets {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Token
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct Token {
|
pub struct Token {
|
||||||
|
/// Proofs in [`Token`] by mint
|
||||||
pub token: Vec<MintProofs>,
|
pub token: Vec<MintProofs>,
|
||||||
/// Memo for token
|
/// Memo for token
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
@@ -535,6 +577,7 @@ pub struct Token {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Token {
|
impl Token {
|
||||||
|
/// Create new [`Token`]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
mint_url: UncheckedUrl,
|
mint_url: UncheckedUrl,
|
||||||
proofs: Proofs,
|
proofs: Proofs,
|
||||||
@@ -555,6 +598,8 @@ impl Token {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Token Info
|
||||||
|
/// Assumes only one mint in [`Token`]
|
||||||
pub fn token_info(&self) -> (Amount, String) {
|
pub fn token_info(&self) -> (Amount, String) {
|
||||||
let mut amount = Amount::ZERO;
|
let mut amount = Amount::ZERO;
|
||||||
|
|
||||||
@@ -595,13 +640,17 @@ impl fmt::Display for Token {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Mint Proofs
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct MintProofs {
|
pub struct MintProofs {
|
||||||
|
/// Url of mint
|
||||||
pub mint: UncheckedUrl,
|
pub mint: UncheckedUrl,
|
||||||
|
/// [`Proofs`]
|
||||||
pub proofs: Proofs,
|
pub proofs: Proofs,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MintProofs {
|
impl MintProofs {
|
||||||
|
/// Create new [`MintProofs`]
|
||||||
pub fn new(mint_url: UncheckedUrl, proofs: Proofs) -> Self {
|
pub fn new(mint_url: UncheckedUrl, proofs: Proofs) -> Self {
|
||||||
Self {
|
Self {
|
||||||
mint: mint_url,
|
mint: mint_url,
|
||||||
|
|||||||
@@ -18,17 +18,23 @@ pub use self::secret_key::SecretKey;
|
|||||||
use super::nut02::KeySet;
|
use super::nut02::KeySet;
|
||||||
use crate::amount::Amount;
|
use crate::amount::Amount;
|
||||||
|
|
||||||
|
/// Nut01 Error
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
/// Secp256k1 Error
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Secp256k1(#[from] secp256k1::Error),
|
Secp256k1(#[from] secp256k1::Error),
|
||||||
|
/// Json Error
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Json(#[from] serde_json::Error),
|
Json(#[from] serde_json::Error),
|
||||||
#[cfg(feature = "nostr")]
|
/// Invalid Pubkey size
|
||||||
#[error(transparent)]
|
|
||||||
NostrKey(#[from] nostr_sdk::key::Error),
|
|
||||||
#[error("Invalid public key size: expected={expected}, found={found}")]
|
#[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]
|
/// Mint Keys [NUT-01]
|
||||||
@@ -47,16 +53,19 @@ impl From<MintKeys> for Keys {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Keys {
|
impl Keys {
|
||||||
|
/// Create new [`Keys`]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(keys: BTreeMap<String, PublicKey>) -> Self {
|
pub fn new(keys: BTreeMap<String, PublicKey>) -> Self {
|
||||||
Self(keys)
|
Self(keys)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get [`Keys`]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn keys(&self) -> &BTreeMap<String, PublicKey> {
|
pub fn keys(&self) -> &BTreeMap<String, PublicKey> {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get [`PublicKey`] for [`Amount`]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn amount_key(&self, amount: Amount) -> Option<PublicKey> {
|
pub fn amount_key(&self, amount: Amount) -> Option<PublicKey> {
|
||||||
self.0.get(&amount.to_string()).copied()
|
self.0.get(&amount.to_string()).copied()
|
||||||
@@ -72,6 +81,7 @@ impl Keys {
|
|||||||
/// Mint Public Keys [NUT-01]
|
/// Mint Public Keys [NUT-01]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
|
||||||
pub struct KeysResponse {
|
pub struct KeysResponse {
|
||||||
|
/// Keysets
|
||||||
pub keysets: Vec<KeySet>,
|
pub keysets: Vec<KeySet>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,19 +126,24 @@ impl DerefMut for MintKeys {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl MintKeys {
|
impl MintKeys {
|
||||||
|
/// Create new [`MintKeys`]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(map: BTreeMap<Amount, MintKeyPair>) -> Self {
|
pub fn new(map: BTreeMap<Amount, MintKeyPair>) -> Self {
|
||||||
Self(map)
|
Self(map)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Mint Public Private key pair
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct MintKeyPair {
|
pub struct MintKeyPair {
|
||||||
|
/// Publickey
|
||||||
pub public_key: PublicKey,
|
pub public_key: PublicKey,
|
||||||
|
/// Secretkey
|
||||||
pub secret_key: SecretKey,
|
pub secret_key: SecretKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MintKeyPair {
|
impl MintKeyPair {
|
||||||
|
/// [`MintKeyPair`] from secret key
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_secret_key(secret_key: SecretKey) -> Self {
|
pub fn from_secret_key(secret_key: SecretKey) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use serde::{Deserialize, Deserializer, Serialize};
|
|||||||
use super::Error;
|
use super::Error;
|
||||||
use crate::SECP256K1;
|
use crate::SECP256K1;
|
||||||
|
|
||||||
|
/// PublicKey
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct PublicKey {
|
pub struct PublicKey {
|
||||||
inner: secp256k1::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 {
|
impl PublicKey {
|
||||||
/// Parse from `bytes`
|
/// Parse from `bytes`
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -92,16 +61,19 @@ impl PublicKey {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// [`PublicKey`] to bytes
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn to_bytes(&self) -> [u8; 33] {
|
pub fn to_bytes(&self) -> [u8; 33] {
|
||||||
self.inner.serialize()
|
self.inner.serialize()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// To uncompressed bytes
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn to_uncompressed_bytes(&self) -> [u8; 65] {
|
pub fn to_uncompressed_bytes(&self) -> [u8; 65] {
|
||||||
self.inner.serialize_uncompressed()
|
self.inner.serialize_uncompressed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// To [`XOnlyPublicKey`]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn x_only_public_key(&self) -> XOnlyPublicKey {
|
pub fn x_only_public_key(&self) -> XOnlyPublicKey {
|
||||||
self.inner.x_only_public_key().0
|
self.inner.x_only_public_key().0
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ use serde::{Deserialize, Deserializer, Serialize};
|
|||||||
use super::{Error, PublicKey};
|
use super::{Error, PublicKey};
|
||||||
use crate::SECP256K1;
|
use crate::SECP256K1;
|
||||||
|
|
||||||
|
/// SecretKey
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct SecretKey {
|
pub struct SecretKey {
|
||||||
inner: secp256k1::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 {
|
impl fmt::Display for SecretKey {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "{}", self.to_secret_hex())
|
write!(f, "{}", self.to_secret_hex())
|
||||||
@@ -105,11 +90,13 @@ impl SecretKey {
|
|||||||
self.inner.public_key(&SECP256K1).into()
|
self.inner.public_key(&SECP256K1).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// [`SecretKey`] to [`Scalar`]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn to_scalar(self) -> Scalar {
|
pub fn to_scalar(self) -> Scalar {
|
||||||
Scalar::from(self.inner)
|
Scalar::from(self.inner)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// [`SecretKey`] as [`Scalar`]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn as_scalar(&self) -> Scalar {
|
pub fn as_scalar(&self) -> Scalar {
|
||||||
Scalar::from(self.inner)
|
Scalar::from(self.inner)
|
||||||
|
|||||||
@@ -30,30 +30,39 @@ use crate::util::hex;
|
|||||||
#[cfg(feature = "mint")]
|
#[cfg(feature = "mint")]
|
||||||
use crate::Amount;
|
use crate::Amount;
|
||||||
|
|
||||||
|
/// NUT02 Error
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
/// Hex Error
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
HexError(#[from] hex::Error),
|
HexError(#[from] hex::Error),
|
||||||
|
/// Keyset length error
|
||||||
#[error("NUT02: ID length invalid")]
|
#[error("NUT02: ID length invalid")]
|
||||||
Length,
|
Length,
|
||||||
|
/// Unknown version
|
||||||
#[error("NUT02: Unknown Version")]
|
#[error("NUT02: Unknown Version")]
|
||||||
UnknownVersion,
|
UnknownVersion,
|
||||||
|
/// Slice Error
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Slice(#[from] TryFromSliceError),
|
Slice(#[from] TryFromSliceError),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Keyset version
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub enum KeySetVersion {
|
pub enum KeySetVersion {
|
||||||
|
/// Current Version 00
|
||||||
Version00,
|
Version00,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KeySetVersion {
|
impl KeySetVersion {
|
||||||
|
/// [`KeySetVersion`] to byte
|
||||||
pub fn to_byte(&self) -> u8 {
|
pub fn to_byte(&self) -> u8 {
|
||||||
match self {
|
match self {
|
||||||
Self::Version00 => 0,
|
Self::Version00 => 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// [`KeySetVersion`] from byte
|
||||||
pub fn from_byte(byte: &u8) -> Result<Self, Error> {
|
pub fn from_byte(byte: &u8) -> Result<Self, Error> {
|
||||||
match byte {
|
match byte {
|
||||||
0 => Ok(Self::Version00),
|
0 => Ok(Self::Version00),
|
||||||
@@ -84,10 +93,12 @@ impl Id {
|
|||||||
const STRLEN: usize = 14;
|
const STRLEN: usize = 14;
|
||||||
const BYTELEN: usize = 7;
|
const BYTELEN: usize = 7;
|
||||||
|
|
||||||
|
/// [`Id`] to bytes
|
||||||
pub fn to_bytes(&self) -> Vec<u8> {
|
pub fn to_bytes(&self) -> Vec<u8> {
|
||||||
[vec![self.version.to_byte()], self.id.to_vec()].concat()
|
[vec![self.version.to_byte()], self.id.to_vec()].concat()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// [`Id`] from bytes
|
||||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
|
pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
version: KeySetVersion::from_byte(&bytes[0])?,
|
version: KeySetVersion::from_byte(&bytes[0])?,
|
||||||
@@ -220,6 +231,7 @@ pub struct KeysetResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl KeysetResponse {
|
impl KeysetResponse {
|
||||||
|
/// Create new [`KeysetResponse`]
|
||||||
pub fn new(keysets: Vec<KeySet>) -> Self {
|
pub fn new(keysets: Vec<KeySet>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
keysets: keysets.into_iter().map(|keyset| keyset.into()).collect(),
|
keysets: keysets.into_iter().map(|keyset| keyset.into()).collect(),
|
||||||
@@ -227,10 +239,14 @@ impl KeysetResponse {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Keyset
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
|
||||||
pub struct KeySet {
|
pub struct KeySet {
|
||||||
|
/// Keyset [`Id`]
|
||||||
pub id: Id,
|
pub id: Id,
|
||||||
|
/// Keyset [`CurrencyUnit`]
|
||||||
pub unit: CurrencyUnit,
|
pub unit: CurrencyUnit,
|
||||||
|
/// Keyset [`Keys`]
|
||||||
pub keys: Keys,
|
pub keys: Keys,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,10 +261,15 @@ impl From<MintKeySet> for KeySet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// KeySetInfo
|
||||||
#[derive(Debug, Clone, Hash, PartialEq, Eq, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Hash, PartialEq, Eq, Deserialize, Serialize)]
|
||||||
pub struct KeySetInfo {
|
pub struct KeySetInfo {
|
||||||
|
/// Keyset [`Id`]
|
||||||
pub id: Id,
|
pub id: Id,
|
||||||
|
/// Keyset [`CurrencyUnit`]
|
||||||
pub unit: CurrencyUnit,
|
pub unit: CurrencyUnit,
|
||||||
|
/// Keyset state
|
||||||
|
/// Mint will only sign from an active keyset
|
||||||
pub active: bool,
|
pub active: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,16 +283,21 @@ impl From<KeySet> for KeySetInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// MintKeyset
|
||||||
#[cfg(feature = "mint")]
|
#[cfg(feature = "mint")]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct MintKeySet {
|
pub struct MintKeySet {
|
||||||
|
/// Keyset [`Id`]
|
||||||
pub id: Id,
|
pub id: Id,
|
||||||
|
/// Keyset [`CurrencyUnit`]
|
||||||
pub unit: CurrencyUnit,
|
pub unit: CurrencyUnit,
|
||||||
|
/// Keyset [`MintKeys`]
|
||||||
pub keys: MintKeys,
|
pub keys: MintKeys,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "mint")]
|
#[cfg(feature = "mint")]
|
||||||
impl MintKeySet {
|
impl MintKeySet {
|
||||||
|
/// Generate new [`MintKeySet`]
|
||||||
pub fn generate<C: secp256k1::Signing>(
|
pub fn generate<C: secp256k1::Signing>(
|
||||||
secp: &Secp256k1<C>,
|
secp: &Secp256k1<C>,
|
||||||
xpriv: ExtendedPrivKey,
|
xpriv: ExtendedPrivKey,
|
||||||
@@ -306,6 +332,7 @@ impl MintKeySet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generate new [`MintKeySet`] from seed
|
||||||
pub fn generate_from_seed<C: secp256k1::Signing>(
|
pub fn generate_from_seed<C: secp256k1::Signing>(
|
||||||
secp: &Secp256k1<C>,
|
secp: &Secp256k1<C>,
|
||||||
seed: &[u8],
|
seed: &[u8],
|
||||||
@@ -325,6 +352,7 @@ impl MintKeySet {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generate new [`MintKeySet`] from xpriv
|
||||||
pub fn generate_from_xpriv<C: secp256k1::Signing>(
|
pub fn generate_from_xpriv<C: secp256k1::Signing>(
|
||||||
secp: &Secp256k1<C>,
|
secp: &Secp256k1<C>,
|
||||||
xpriv: ExtendedPrivKey,
|
xpriv: ExtendedPrivKey,
|
||||||
|
|||||||
@@ -7,9 +7,12 @@ use serde::{Deserialize, Serialize};
|
|||||||
use super::nut00::{BlindSignature, BlindedMessage, PreMintSecrets, Proofs};
|
use super::nut00::{BlindSignature, BlindedMessage, PreMintSecrets, Proofs};
|
||||||
use crate::Amount;
|
use crate::Amount;
|
||||||
|
|
||||||
|
/// Preswap information
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
|
||||||
pub struct PreSwap {
|
pub struct PreSwap {
|
||||||
|
/// Preswap mint secrets
|
||||||
pub pre_mint_secrets: PreMintSecrets,
|
pub pre_mint_secrets: PreMintSecrets,
|
||||||
|
/// Swap request
|
||||||
pub swap_request: SwapRequest,
|
pub swap_request: SwapRequest,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,16 +26,17 @@ pub struct SwapRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SwapRequest {
|
impl SwapRequest {
|
||||||
|
/// Create new [`SwapRequest`]
|
||||||
pub fn new(inputs: Proofs, outputs: Vec<BlindedMessage>) -> Self {
|
pub fn new(inputs: Proofs, outputs: Vec<BlindedMessage>) -> Self {
|
||||||
Self { inputs, outputs }
|
Self { inputs, outputs }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Total value of proofs in `SplitRequest`
|
/// Total value of proofs in [`SwapRequest`]
|
||||||
pub fn input_amount(&self) -> Amount {
|
pub fn input_amount(&self) -> Amount {
|
||||||
self.inputs.iter().map(|proof| proof.amount).sum()
|
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 {
|
pub fn output_amount(&self) -> Amount {
|
||||||
self.outputs.iter().map(|proof| proof.amount).sum()
|
self.outputs.iter().map(|proof| proof.amount).sum()
|
||||||
}
|
}
|
||||||
@@ -46,12 +50,14 @@ pub struct SwapResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SwapResponse {
|
impl SwapResponse {
|
||||||
|
/// Create new [`SwapRequest`]
|
||||||
pub fn new(promises: Vec<BlindSignature>) -> SwapResponse {
|
pub fn new(promises: Vec<BlindSignature>) -> SwapResponse {
|
||||||
SwapResponse {
|
SwapResponse {
|
||||||
signatures: promises,
|
signatures: promises,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Total [`Amount`] of promises
|
||||||
pub fn promises_amount(&self) -> Amount {
|
pub fn promises_amount(&self) -> Amount {
|
||||||
self.signatures
|
self.signatures
|
||||||
.iter()
|
.iter()
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ use super::MintQuoteState;
|
|||||||
use crate::types::MintQuote;
|
use crate::types::MintQuote;
|
||||||
use crate::Amount;
|
use crate::Amount;
|
||||||
|
|
||||||
|
/// NUT04 Error
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Unknown Quote State
|
/// Unknown Quote State
|
||||||
@@ -34,10 +35,14 @@ pub struct MintQuoteBolt11Request {
|
|||||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Default, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Default, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "UPPERCASE")]
|
#[serde(rename_all = "UPPERCASE")]
|
||||||
pub enum QuoteState {
|
pub enum QuoteState {
|
||||||
|
/// Quote has not been paid
|
||||||
#[default]
|
#[default]
|
||||||
Unpaid,
|
Unpaid,
|
||||||
|
/// Quote has been paid and wallet can mint
|
||||||
Paid,
|
Paid,
|
||||||
|
/// Minting is in progress
|
||||||
Pending,
|
Pending,
|
||||||
|
/// ecash issued for quote
|
||||||
Issued,
|
Issued,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,6 +176,7 @@ pub struct MintBolt11Request {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl MintBolt11Request {
|
impl MintBolt11Request {
|
||||||
|
/// Total [`Amount`] of outputs
|
||||||
pub fn total_amount(&self) -> Amount {
|
pub fn total_amount(&self) -> Amount {
|
||||||
self.outputs
|
self.outputs
|
||||||
.iter()
|
.iter()
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ use super::nut15::Mpp;
|
|||||||
use crate::types::MeltQuote;
|
use crate::types::MeltQuote;
|
||||||
use crate::{Amount, Bolt11Invoice};
|
use crate::{Amount, Bolt11Invoice};
|
||||||
|
|
||||||
|
/// NUT05 Error
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Unknown Quote State
|
/// Unknown Quote State
|
||||||
@@ -36,9 +37,12 @@ pub struct MeltQuoteBolt11Request {
|
|||||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Default, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Default, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "UPPERCASE")]
|
#[serde(rename_all = "UPPERCASE")]
|
||||||
pub enum QuoteState {
|
pub enum QuoteState {
|
||||||
|
/// Quote has not been paid
|
||||||
#[default]
|
#[default]
|
||||||
Unpaid,
|
Unpaid,
|
||||||
|
/// Quote has been paid
|
||||||
Paid,
|
Paid,
|
||||||
|
/// Paying quote is in progress
|
||||||
Pending,
|
Pending,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,6 +207,7 @@ pub struct MeltBolt11Request {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl MeltBolt11Request {
|
impl MeltBolt11Request {
|
||||||
|
/// Total [`Amount`] of [`Proofs`]
|
||||||
pub fn proofs_amount(&self) -> Amount {
|
pub fn proofs_amount(&self) -> Amount {
|
||||||
self.inputs.iter().map(|proof| proof.amount).sum()
|
self.inputs.iter().map(|proof| proof.amount).sum()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ use super::{nut04, nut05, nut15};
|
|||||||
/// Mint Version
|
/// Mint Version
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct MintVersion {
|
pub struct MintVersion {
|
||||||
|
/// Mint Software name
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
/// Mint Version
|
||||||
pub version: String,
|
pub version: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,36 +78,47 @@ pub struct MintInfo {
|
|||||||
/// Supported nuts and settings
|
/// Supported nuts and settings
|
||||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
pub struct Nuts {
|
pub struct Nuts {
|
||||||
|
/// NUT04 Settings
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
#[serde(rename = "4")]
|
#[serde(rename = "4")]
|
||||||
pub nut04: nut04::Settings,
|
pub nut04: nut04::Settings,
|
||||||
|
/// NUT05 Settings
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
#[serde(rename = "5")]
|
#[serde(rename = "5")]
|
||||||
pub nut05: nut05::Settings,
|
pub nut05: nut05::Settings,
|
||||||
|
/// NUT07 Settings
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
#[serde(rename = "7")]
|
#[serde(rename = "7")]
|
||||||
pub nut07: SupportedSettings,
|
pub nut07: SupportedSettings,
|
||||||
|
/// NUT08 Settings
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
#[serde(rename = "8")]
|
#[serde(rename = "8")]
|
||||||
pub nut08: SupportedSettings,
|
pub nut08: SupportedSettings,
|
||||||
|
/// NUT09 Settings
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
#[serde(rename = "9")]
|
#[serde(rename = "9")]
|
||||||
pub nut09: SupportedSettings,
|
pub nut09: SupportedSettings,
|
||||||
|
/// NUT10 Settings
|
||||||
#[serde(rename = "10")]
|
#[serde(rename = "10")]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub nut10: SupportedSettings,
|
pub nut10: SupportedSettings,
|
||||||
|
/// NUT11 Settings
|
||||||
#[serde(rename = "11")]
|
#[serde(rename = "11")]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub nut11: SupportedSettings,
|
pub nut11: SupportedSettings,
|
||||||
|
/// NUT12 Settings
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
#[serde(rename = "12")]
|
#[serde(rename = "12")]
|
||||||
pub nut12: SupportedSettings,
|
pub nut12: SupportedSettings,
|
||||||
|
/// NUT13 Settings
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
#[serde(rename = "13")]
|
#[serde(rename = "13")]
|
||||||
pub nut13: SupportedSettings,
|
pub nut13: SupportedSettings,
|
||||||
|
/// NUT14 Settings
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
#[serde(rename = "14")]
|
#[serde(rename = "14")]
|
||||||
pub nut14: SupportedSettings,
|
pub nut14: SupportedSettings,
|
||||||
|
/// NUT15 Settings
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
#[serde(rename = "15")]
|
#[serde(rename = "15")]
|
||||||
pub nut15: nut15::MppMethodSettings,
|
pub nut15: nut15::MppMethodSettings,
|
||||||
|
|||||||
@@ -18,12 +18,21 @@ pub enum Error {
|
|||||||
UnknownState,
|
UnknownState,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// State of Proof
|
||||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "UPPERCASE")]
|
#[serde(rename_all = "UPPERCASE")]
|
||||||
pub enum State {
|
pub enum State {
|
||||||
|
/// Spent
|
||||||
Spent,
|
Spent,
|
||||||
|
/// Unspent
|
||||||
Unspent,
|
Unspent,
|
||||||
|
/// Pending
|
||||||
|
///
|
||||||
|
/// Currently being used in a transaction i.e. melt in progress
|
||||||
Pending,
|
Pending,
|
||||||
|
/// Proof is reserved
|
||||||
|
///
|
||||||
|
/// i.e. used to create a token
|
||||||
Reserved,
|
Reserved,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,5 +86,6 @@ pub struct ProofState {
|
|||||||
/// Check Spendable Response [NUT-07]
|
/// Check Spendable Response [NUT-07]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct CheckStateResponse {
|
pub struct CheckStateResponse {
|
||||||
|
/// Proof states
|
||||||
pub states: Vec<ProofState>,
|
pub states: Vec<ProofState>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ use super::nut05::{MeltBolt11Request, MeltQuoteBolt11Response};
|
|||||||
use crate::Amount;
|
use crate::Amount;
|
||||||
|
|
||||||
impl MeltBolt11Request {
|
impl MeltBolt11Request {
|
||||||
|
/// Total output [`Amount`]
|
||||||
pub fn output_amount(&self) -> Option<Amount> {
|
pub fn output_amount(&self) -> Option<Amount> {
|
||||||
self.outputs
|
self.outputs
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@@ -14,6 +15,7 @@ impl MeltBolt11Request {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl MeltQuoteBolt11Response {
|
impl MeltQuoteBolt11Response {
|
||||||
|
/// Total change [`Amount`]
|
||||||
pub fn change_amount(&self) -> Option<Amount> {
|
pub fn change_amount(&self) -> Option<Amount> {
|
||||||
self.change
|
self.change
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ pub enum Kind {
|
|||||||
HTLC,
|
HTLC,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Secert Date
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
pub struct SecretData {
|
pub struct SecretData {
|
||||||
/// Unique random string
|
/// Unique random string
|
||||||
@@ -29,6 +30,7 @@ pub struct SecretData {
|
|||||||
pub tags: Option<Vec<Vec<String>>>,
|
pub tags: Option<Vec<Vec<String>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// NUT10 Secret
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize)]
|
||||||
pub struct Secret {
|
pub struct Secret {
|
||||||
/// Kind of the spending condition
|
/// Kind of the spending condition
|
||||||
@@ -38,6 +40,7 @@ pub struct Secret {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Secret {
|
impl Secret {
|
||||||
|
/// Create new [`Secret`]
|
||||||
pub fn new<S, V>(kind: Kind, data: S, tags: Option<V>) -> Self
|
pub fn new<S, V>(kind: Kind, data: S, tags: Option<V>) -> Self
|
||||||
where
|
where
|
||||||
S: Into<String>,
|
S: Into<String>,
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ use crate::util::{hex, unix_time};
|
|||||||
|
|
||||||
pub mod serde_p2pk_witness;
|
pub mod serde_p2pk_witness;
|
||||||
|
|
||||||
|
/// Nut11 Error
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Incorrect secret kind
|
/// Incorrect secret kind
|
||||||
@@ -84,11 +85,13 @@ pub enum Error {
|
|||||||
/// P2Pk Witness
|
/// P2Pk Witness
|
||||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Default, Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
pub struct P2PKWitness {
|
pub struct P2PKWitness {
|
||||||
|
/// Signatures
|
||||||
pub signatures: Vec<String>,
|
pub signatures: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl P2PKWitness {
|
impl P2PKWitness {
|
||||||
#[inline]
|
#[inline]
|
||||||
|
/// Check id Witness is empty
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.signatures.is_empty()
|
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)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
pub enum SpendingConditions {
|
pub enum SpendingConditions {
|
||||||
/// NUT11 Spending conditions
|
/// NUT11 Spending conditions
|
||||||
|
///
|
||||||
|
/// Defined in [NUT11](https://github.com/cashubtc/nuts/blob/main/11.md)
|
||||||
P2PKConditions {
|
P2PKConditions {
|
||||||
|
/// The public key of the recipient of the locked ecash
|
||||||
data: PublicKey,
|
data: PublicKey,
|
||||||
|
/// Additional Optional Spending [`Conditions`]
|
||||||
conditions: Option<Conditions>,
|
conditions: Option<Conditions>,
|
||||||
},
|
},
|
||||||
/// NUT14 Spending conditions
|
/// NUT14 Spending conditions
|
||||||
|
///
|
||||||
|
/// Dedined in [NUT14](https://github.com/cashubtc/nuts/blob/main/14.md)
|
||||||
HTLCConditions {
|
HTLCConditions {
|
||||||
|
/// Hash Lock of ecash
|
||||||
data: Sha256Hash,
|
data: Sha256Hash,
|
||||||
|
/// Additional Optional Spending [`Conditions`]
|
||||||
conditions: Option<Conditions>,
|
conditions: Option<Conditions>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -291,6 +305,7 @@ impl SpendingConditions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Number if signatures required to unlock
|
||||||
pub fn num_sigs(&self) -> Option<u64> {
|
pub fn num_sigs(&self) -> Option<u64> {
|
||||||
match self {
|
match self {
|
||||||
Self::P2PKConditions { conditions, .. } => conditions.as_ref().and_then(|c| c.num_sigs),
|
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>> {
|
pub fn pubkeys(&self) -> Option<Vec<PublicKey>> {
|
||||||
match self {
|
match self {
|
||||||
Self::P2PKConditions { data, conditions } => {
|
Self::P2PKConditions { data, conditions } => {
|
||||||
@@ -312,6 +328,7 @@ impl SpendingConditions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Locktime of Spending Conditions
|
||||||
pub fn locktime(&self) -> Option<u64> {
|
pub fn locktime(&self) -> Option<u64> {
|
||||||
match self {
|
match self {
|
||||||
Self::P2PKConditions { conditions, .. } => conditions.as_ref().and_then(|c| c.locktime),
|
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>> {
|
pub fn refund_keys(&self) -> Option<Vec<PublicKey>> {
|
||||||
match self {
|
match self {
|
||||||
Self::P2PKConditions { conditions, .. } => {
|
Self::P2PKConditions { conditions, .. } => {
|
||||||
@@ -373,18 +391,28 @@ impl From<SpendingConditions> for super::nut10::Secret {
|
|||||||
/// P2PK and HTLC spending conditions
|
/// P2PK and HTLC spending conditions
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
|
||||||
pub struct Conditions {
|
pub struct Conditions {
|
||||||
|
/// Unix locktime after which refund keys can be used
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub locktime: Option<u64>,
|
pub locktime: Option<u64>,
|
||||||
|
/// Additional Public keys
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub pubkeys: Option<Vec<PublicKey>>,
|
pub pubkeys: Option<Vec<PublicKey>>,
|
||||||
|
/// Refund keys
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub refund_keys: Option<Vec<PublicKey>>,
|
pub refund_keys: Option<Vec<PublicKey>>,
|
||||||
|
/// Numbedr of signatures required
|
||||||
|
///
|
||||||
|
/// Default is 1
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub num_sigs: Option<u64>,
|
pub num_sigs: Option<u64>,
|
||||||
|
/// Signature flag
|
||||||
|
///
|
||||||
|
/// Default [`SigFlag::SigInputs`]
|
||||||
pub sig_flag: SigFlag,
|
pub sig_flag: SigFlag,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Conditions {
|
impl Conditions {
|
||||||
|
/// Create new Spending [`Conditions`]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
locktime: Option<u64>,
|
locktime: Option<u64>,
|
||||||
pubkeys: Option<Vec<PublicKey>>,
|
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)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum TagKind {
|
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)]
|
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord, Hash)]
|
||||||
pub enum SigFlag {
|
pub enum SigFlag {
|
||||||
#[default]
|
#[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,
|
SigInputs,
|
||||||
|
/// Requires valid signatures on all inputs and on all outputs.
|
||||||
SigAll,
|
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>) {
|
pub fn enforce_sig_flag(proofs: Proofs) -> (SigFlag, HashSet<PublicKey>) {
|
||||||
let mut sig_flag = SigFlag::SigInputs;
|
let mut sig_flag = SigFlag::SigInputs;
|
||||||
let mut pubkeys = HashSet::new();
|
let mut pubkeys = HashSet::new();
|
||||||
@@ -602,16 +637,23 @@ pub fn enforce_sig_flag(proofs: Proofs) -> (SigFlag, HashSet<PublicKey>) {
|
|||||||
(sig_flag, pubkeys)
|
(sig_flag, pubkeys)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tag
|
||||||
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||||||
pub enum Tag {
|
pub enum Tag {
|
||||||
|
/// Sigflag [`Tag`]
|
||||||
SigFlag(SigFlag),
|
SigFlag(SigFlag),
|
||||||
|
/// Number of Sigs [`Tag`]
|
||||||
NSigs(u64),
|
NSigs(u64),
|
||||||
|
/// Locktime [`Tag`]
|
||||||
LockTime(u64),
|
LockTime(u64),
|
||||||
|
/// Refund [`Tag`]
|
||||||
Refund(Vec<PublicKey>),
|
Refund(Vec<PublicKey>),
|
||||||
|
/// Pubkeys [`Tag`]
|
||||||
PubKeys(Vec<PublicKey>),
|
PubKeys(Vec<PublicKey>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tag {
|
impl Tag {
|
||||||
|
/// Get [`Tag`] Kind
|
||||||
pub fn kind(&self) -> TagKind {
|
pub fn kind(&self) -> TagKind {
|
||||||
match self {
|
match self {
|
||||||
Self::SigFlag(_) => TagKind::SigFlag,
|
Self::SigFlag(_) => TagKind::SigFlag,
|
||||||
|
|||||||
@@ -14,36 +14,55 @@ use super::nut02::Id;
|
|||||||
use crate::dhke::{hash_e, hash_to_curve};
|
use crate::dhke::{hash_e, hash_to_curve};
|
||||||
use crate::{Amount, SECP256K1};
|
use crate::{Amount, SECP256K1};
|
||||||
|
|
||||||
|
/// NUT12 Error
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
/// Missing Dleq Proof
|
||||||
#[error("No Dleq Proof provided")]
|
#[error("No Dleq Proof provided")]
|
||||||
MissingDleqProof,
|
MissingDleqProof,
|
||||||
|
/// Incomplete Dleq Proof
|
||||||
#[error("Incomplete DLEQ Proof")]
|
#[error("Incomplete DLEQ Proof")]
|
||||||
IncompleteDleqProof,
|
IncompleteDleqProof,
|
||||||
|
/// Invalid Dleq Proof
|
||||||
#[error("Invalid Dleq Prood")]
|
#[error("Invalid Dleq Prood")]
|
||||||
InvalidDleqProof,
|
InvalidDleqProof,
|
||||||
|
/// Cashu Error
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Cashu(#[from] crate::error::Error),
|
Cashu(#[from] crate::error::Error),
|
||||||
|
/// NUT01 Error
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
NUT01(#[from] crate::nuts::nut01::Error),
|
NUT01(#[from] crate::nuts::nut01::Error),
|
||||||
|
/// SECP256k1 Error
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Secp256k1(#[from] secp256k1::Error),
|
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)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct BlindSignatureDleq {
|
pub struct BlindSignatureDleq {
|
||||||
|
/// e
|
||||||
pub e: SecretKey,
|
pub e: SecretKey,
|
||||||
|
/// s
|
||||||
pub s: SecretKey,
|
pub s: SecretKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Proof Dleq
|
||||||
|
///
|
||||||
|
/// Defined in [NUT12](https://github.com/cashubtc/nuts/blob/main/12.md)
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct ProofDleq {
|
pub struct ProofDleq {
|
||||||
|
/// e
|
||||||
pub e: SecretKey,
|
pub e: SecretKey,
|
||||||
|
/// s
|
||||||
pub s: SecretKey,
|
pub s: SecretKey,
|
||||||
|
/// Blinding factor
|
||||||
pub r: SecretKey,
|
pub r: SecretKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProofDleq {
|
impl ProofDleq {
|
||||||
|
/// Create new [`ProofDleq`]
|
||||||
pub fn new(e: SecretKey, s: SecretKey, r: SecretKey) -> Self {
|
pub fn new(e: SecretKey, s: SecretKey, r: SecretKey) -> Self {
|
||||||
Self { e, s, r }
|
Self { e, s, r }
|
||||||
}
|
}
|
||||||
@@ -119,6 +138,7 @@ fn calculate_dleq(
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Proof {
|
impl Proof {
|
||||||
|
/// Verify proof Dleq
|
||||||
pub fn verify_dleq(&self, mint_pubkey: PublicKey) -> Result<(), Error> {
|
pub fn verify_dleq(&self, mint_pubkey: PublicKey) -> Result<(), Error> {
|
||||||
match &self.dleq {
|
match &self.dleq {
|
||||||
Some(dleq) => {
|
Some(dleq) => {
|
||||||
@@ -165,6 +185,7 @@ impl BlindSignature {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Verify dleq on proof
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn verify_dleq(
|
pub fn verify_dleq(
|
||||||
&self,
|
&self,
|
||||||
@@ -177,6 +198,7 @@ impl BlindSignature {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add Dleq to proof
|
||||||
/*
|
/*
|
||||||
r = random nonce
|
r = random nonce
|
||||||
R1 = r*G
|
R1 = r*G
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ use crate::util::hex;
|
|||||||
use crate::{Amount, SECP256K1};
|
use crate::{Amount, SECP256K1};
|
||||||
|
|
||||||
impl Secret {
|
impl Secret {
|
||||||
|
/// Create new [`Secret`] from xpriv
|
||||||
pub fn from_xpriv(xpriv: ExtendedPrivKey, keyset_id: Id, counter: u32) -> Result<Self, Error> {
|
pub fn from_xpriv(xpriv: ExtendedPrivKey, keyset_id: Id, counter: u32) -> Result<Self, Error> {
|
||||||
let path = derive_path_from_keyset_id(keyset_id)?
|
let path = derive_path_from_keyset_id(keyset_id)?
|
||||||
.child(ChildNumber::from_hardened_idx(counter)?)
|
.child(ChildNumber::from_hardened_idx(counter)?)
|
||||||
@@ -28,6 +29,7 @@ impl Secret {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SecretKey {
|
impl SecretKey {
|
||||||
|
/// Create new [`SecretKey`] from xpriv
|
||||||
pub fn from_xpriv(xpriv: ExtendedPrivKey, keyset_id: Id, counter: u32) -> Result<Self, Error> {
|
pub fn from_xpriv(xpriv: ExtendedPrivKey, keyset_id: Id, counter: u32) -> Result<Self, Error> {
|
||||||
let path = derive_path_from_keyset_id(keyset_id)?
|
let path = derive_path_from_keyset_id(keyset_id)?
|
||||||
.child(ChildNumber::from_hardened_idx(counter)?)
|
.child(ChildNumber::from_hardened_idx(counter)?)
|
||||||
@@ -74,6 +76,7 @@ impl PreMintSecrets {
|
|||||||
Ok(pre_mint_secrets)
|
Ok(pre_mint_secrets)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// New [`PreMintSecrets`] from xpriv with a zero amount used for change
|
||||||
pub fn from_xpriv_blank(
|
pub fn from_xpriv_blank(
|
||||||
keyset_id: Id,
|
keyset_id: Id,
|
||||||
counter: u32,
|
counter: u32,
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ use crate::util::unix_time;
|
|||||||
|
|
||||||
pub mod serde_htlc_witness;
|
pub mod serde_htlc_witness;
|
||||||
|
|
||||||
|
/// NUT14 Errors
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Incorrect secret kind
|
/// Incorrect secret kind
|
||||||
@@ -42,12 +43,16 @@ pub enum Error {
|
|||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
NUT11(#[from] super::nut11::Error),
|
NUT11(#[from] super::nut11::Error),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
|
/// Serde Error
|
||||||
Serde(#[from] serde_json::Error),
|
Serde(#[from] serde_json::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// HTLC Witness
|
||||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Default, Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
pub struct HTLCWitness {
|
pub struct HTLCWitness {
|
||||||
|
/// Primage
|
||||||
pub preimage: String,
|
pub preimage: String,
|
||||||
|
/// Signatures
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub signatures: Option<Vec<String>>,
|
pub signatures: Option<Vec<String>>,
|
||||||
}
|
}
|
||||||
@@ -125,6 +130,7 @@ impl Proof {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add Preimage
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn add_preimage(&mut self, preimage: String) {
|
pub fn add_preimage(&mut self, preimage: String) {
|
||||||
self.witness = Some(Witness::HTLCWitness(HTLCWitness {
|
self.witness = Some(Witness::HTLCWitness(HTLCWitness {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//! Serde helpers for HTLC Witness
|
||||||
|
|
||||||
use serde::{de, ser, Deserialize, Deserializer, Serializer};
|
use serde::{de, ser, Deserialize, Deserializer, Serializer};
|
||||||
|
|
||||||
use super::HTLCWitness;
|
use super::HTLCWitness;
|
||||||
|
|||||||
@@ -7,9 +7,11 @@ use serde::{Deserialize, Serialize};
|
|||||||
use super::{CurrencyUnit, PaymentMethod};
|
use super::{CurrencyUnit, PaymentMethod};
|
||||||
use crate::Amount;
|
use crate::Amount;
|
||||||
|
|
||||||
|
/// Multi-part payment
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
#[serde(rename = "lowercase")]
|
#[serde(rename = "lowercase")]
|
||||||
pub struct Mpp {
|
pub struct Mpp {
|
||||||
|
/// Amount
|
||||||
pub amount: Amount,
|
pub amount: Amount,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,5 +29,6 @@ pub struct MppMethodSettings {
|
|||||||
/// Mpp Settings
|
/// Mpp Settings
|
||||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
|
/// Method settings
|
||||||
pub methods: Vec<MppMethodSettings>,
|
pub methods: Vec<MppMethodSettings>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//! Secret
|
//! Secret
|
||||||
|
|
||||||
use core::fmt;
|
use std::fmt;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use bitcoin::secp256k1::rand::{self, RngCore};
|
use bitcoin::secp256k1::rand::{self, RngCore};
|
||||||
@@ -14,10 +14,13 @@ use crate::util::hex;
|
|||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
pub struct Secret(String);
|
pub struct Secret(String);
|
||||||
|
|
||||||
|
/// Secret Errors
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
/// Invalid Length
|
||||||
#[error("Invalid secret length: `{0}`")]
|
#[error("Invalid secret length: `{0}`")]
|
||||||
InvalidLength(u64),
|
InvalidLength(u64),
|
||||||
|
/// Hex Error
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Hex(#[from] hex::Error),
|
Hex(#[from] hex::Error),
|
||||||
}
|
}
|
||||||
@@ -29,6 +32,7 @@ impl Default for Secret {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Secret {
|
impl Secret {
|
||||||
|
/// Create new [`Secret`]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new<S>(secret: S) -> Self
|
pub fn new<S>(secret: S) -> Self
|
||||||
where
|
where
|
||||||
@@ -51,16 +55,19 @@ impl Secret {
|
|||||||
Self(secret)
|
Self(secret)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// [`Secret`] as bytes
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn as_bytes(&self) -> &[u8] {
|
pub fn as_bytes(&self) -> &[u8] {
|
||||||
self.0.as_bytes()
|
self.0.as_bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// [`Secret`] to bytes
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn to_bytes(&self) -> Vec<u8> {
|
pub fn to_bytes(&self) -> Vec<u8> {
|
||||||
self.as_bytes().to_vec()
|
self.as_bytes().to_vec()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if secret is P2PK secret
|
||||||
pub fn is_p2pk(&self) -> bool {
|
pub fn is_p2pk(&self) -> bool {
|
||||||
use crate::nuts::Kind;
|
use crate::nuts::Kind;
|
||||||
|
|
||||||
|
|||||||
@@ -14,24 +14,35 @@ use crate::Amount;
|
|||||||
/// Melt response with proofs
|
/// Melt response with proofs
|
||||||
#[derive(Debug, Clone, Hash, PartialEq, Eq, Default, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Hash, PartialEq, Eq, Default, Serialize, Deserialize)]
|
||||||
pub struct Melted {
|
pub struct Melted {
|
||||||
|
/// State of quote
|
||||||
pub state: MeltQuoteState,
|
pub state: MeltQuoteState,
|
||||||
|
/// Preimage of melt payment
|
||||||
pub preimage: Option<String>,
|
pub preimage: Option<String>,
|
||||||
|
/// Melt change
|
||||||
pub change: Option<Proofs>,
|
pub change: Option<Proofs>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mint Quote Info
|
/// Mint Quote Info
|
||||||
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct MintQuote {
|
pub struct MintQuote {
|
||||||
|
/// Quote id
|
||||||
pub id: String,
|
pub id: String,
|
||||||
|
/// Mint Url
|
||||||
pub mint_url: UncheckedUrl,
|
pub mint_url: UncheckedUrl,
|
||||||
|
/// Amount of quote
|
||||||
pub amount: Amount,
|
pub amount: Amount,
|
||||||
|
/// Unit of quote
|
||||||
pub unit: CurrencyUnit,
|
pub unit: CurrencyUnit,
|
||||||
|
/// Quote payment request e.g. bolt11
|
||||||
pub request: String,
|
pub request: String,
|
||||||
|
/// Quote state
|
||||||
pub state: MintQuoteState,
|
pub state: MintQuoteState,
|
||||||
|
/// Expiration time of quote
|
||||||
pub expiry: u64,
|
pub expiry: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MintQuote {
|
impl MintQuote {
|
||||||
|
/// Create new [`MintQuote`]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
mint_url: UncheckedUrl,
|
mint_url: UncheckedUrl,
|
||||||
request: String,
|
request: String,
|
||||||
@@ -56,18 +67,27 @@ impl MintQuote {
|
|||||||
/// Melt Quote Info
|
/// Melt Quote Info
|
||||||
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct MeltQuote {
|
pub struct MeltQuote {
|
||||||
|
/// Quote id
|
||||||
pub id: String,
|
pub id: String,
|
||||||
|
/// Quote unit
|
||||||
pub unit: CurrencyUnit,
|
pub unit: CurrencyUnit,
|
||||||
|
/// Quote amount
|
||||||
pub amount: Amount,
|
pub amount: Amount,
|
||||||
|
/// Quote Payment request e.g. bolt11
|
||||||
pub request: String,
|
pub request: String,
|
||||||
|
/// Quote fee reserve
|
||||||
pub fee_reserve: Amount,
|
pub fee_reserve: Amount,
|
||||||
|
/// Quote state
|
||||||
pub state: MeltQuoteState,
|
pub state: MeltQuoteState,
|
||||||
|
/// Expiration time of quote
|
||||||
pub expiry: u64,
|
pub expiry: u64,
|
||||||
|
/// Payment preimage
|
||||||
pub payment_preimage: Option<String>,
|
pub payment_preimage: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "mint")]
|
#[cfg(feature = "mint")]
|
||||||
impl MeltQuote {
|
impl MeltQuote {
|
||||||
|
/// Create new [`MeltQuote`]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
request: String,
|
request: String,
|
||||||
unit: CurrencyUnit,
|
unit: CurrencyUnit,
|
||||||
@@ -90,17 +110,25 @@ impl MeltQuote {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Prooinfo
|
||||||
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct ProofInfo {
|
pub struct ProofInfo {
|
||||||
|
/// Proof
|
||||||
pub proof: Proof,
|
pub proof: Proof,
|
||||||
|
/// y
|
||||||
pub y: PublicKey,
|
pub y: PublicKey,
|
||||||
|
/// Mint Url
|
||||||
pub mint_url: UncheckedUrl,
|
pub mint_url: UncheckedUrl,
|
||||||
|
/// Proof State
|
||||||
pub state: State,
|
pub state: State,
|
||||||
|
/// Proof Spending Conditions
|
||||||
pub spending_condition: Option<SpendingConditions>,
|
pub spending_condition: Option<SpendingConditions>,
|
||||||
|
/// Unit
|
||||||
pub unit: CurrencyUnit,
|
pub unit: CurrencyUnit,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProofInfo {
|
impl ProofInfo {
|
||||||
|
/// Create new [`ProofInfo`]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
proof: Proof,
|
proof: Proof,
|
||||||
mint_url: UncheckedUrl,
|
mint_url: UncheckedUrl,
|
||||||
@@ -123,6 +151,7 @@ impl ProofInfo {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if [`Proof`] matches conditions
|
||||||
pub fn matches_conditions(
|
pub fn matches_conditions(
|
||||||
&self,
|
&self,
|
||||||
mint_url: &Option<UncheckedUrl>,
|
mint_url: &Option<UncheckedUrl>,
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ impl UncheckedUrl {
|
|||||||
Self(String::new())
|
Self(String::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Join onto url
|
||||||
pub fn join(&self, path: &str) -> Result<Url, Error> {
|
pub fn join(&self, path: &str) -> Result<Url, Error> {
|
||||||
let url: Url = self.try_into()?;
|
let url: Url = self.try_into()?;
|
||||||
Ok(url.join(path)?)
|
Ok(url.join(path)?)
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ fn join_url(url: Url, paths: &[&str]) -> Result<Url, Error> {
|
|||||||
Ok(url)
|
Ok(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Http Client
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct HttpClient {
|
pub struct HttpClient {
|
||||||
inner: Client,
|
inner: Client,
|
||||||
@@ -48,6 +49,7 @@ impl Default for HttpClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl HttpClient {
|
impl HttpClient {
|
||||||
|
/// Create new [`HttpClient`]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
inner: Client::new(),
|
inner: Client::new(),
|
||||||
@@ -321,6 +323,7 @@ impl HttpClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Restore request [NUT-13]
|
||||||
#[instrument(skip(self, request), fields(mint_url = %mint_url))]
|
#[instrument(skip(self, request), fields(mint_url = %mint_url))]
|
||||||
pub async fn post_restore(
|
pub async fn post_restore(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//! CDK Wallet Error
|
||||||
|
|
||||||
use std::num::ParseIntError;
|
use std::num::ParseIntError;
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
@@ -5,6 +7,7 @@ use thiserror::Error;
|
|||||||
use crate::cdk_database;
|
use crate::cdk_database;
|
||||||
use crate::error::{ErrorCode, ErrorResponse};
|
use crate::error::{ErrorCode, ErrorResponse};
|
||||||
|
|
||||||
|
/// Wallet Error
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Insufficient Funds
|
/// Insufficient Funds
|
||||||
@@ -31,6 +34,7 @@ pub enum Error {
|
|||||||
/// Preimage not provided
|
/// Preimage not provided
|
||||||
#[error("Preimage not provided")]
|
#[error("Preimage not provided")]
|
||||||
PreimageNotProvided,
|
PreimageNotProvided,
|
||||||
|
/// Unknown Key
|
||||||
#[error("Unknown Key")]
|
#[error("Unknown Key")]
|
||||||
UnknownKey,
|
UnknownKey,
|
||||||
/// Spending Locktime not provided
|
/// Spending Locktime not provided
|
||||||
@@ -102,14 +106,6 @@ pub enum Error {
|
|||||||
/// Serde Error
|
/// Serde Error
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Serde(#[from] serde_json::Error),
|
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
|
/// Custom Error
|
||||||
#[error("`{0}`")]
|
#[error("`{0}`")]
|
||||||
Custom(String),
|
Custom(String),
|
||||||
|
|||||||
@@ -34,16 +34,21 @@ pub mod error;
|
|||||||
pub mod multi_mint_wallet;
|
pub mod multi_mint_wallet;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
|
||||||
|
/// CDK Wallet
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Wallet {
|
pub struct Wallet {
|
||||||
|
/// Mint Url
|
||||||
pub mint_url: UncheckedUrl,
|
pub mint_url: UncheckedUrl,
|
||||||
|
/// Unit
|
||||||
pub unit: CurrencyUnit,
|
pub unit: CurrencyUnit,
|
||||||
pub client: HttpClient,
|
client: HttpClient,
|
||||||
|
/// Storage backend
|
||||||
pub localstore: Arc<dyn WalletDatabase<Err = cdk_database::Error> + Send + Sync>,
|
pub localstore: Arc<dyn WalletDatabase<Err = cdk_database::Error> + Send + Sync>,
|
||||||
xpriv: ExtendedPrivKey,
|
xpriv: ExtendedPrivKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Wallet {
|
impl Wallet {
|
||||||
|
/// Create new [`Wallet`]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
mint_url: &str,
|
mint_url: &str,
|
||||||
unit: CurrencyUnit,
|
unit: CurrencyUnit,
|
||||||
@@ -278,6 +283,7 @@ impl Wallet {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get Active mint keyset id
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip(self))]
|
||||||
pub async fn active_mint_keyset(&self) -> Result<Id, Error> {
|
pub async fn active_mint_keyset(&self) -> Result<Id, Error> {
|
||||||
let mint_url = &self.mint_url;
|
let mint_url = &self.mint_url;
|
||||||
@@ -307,6 +313,7 @@ impl Wallet {
|
|||||||
Err(Error::NoActiveKeyset)
|
Err(Error::NoActiveKeyset)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get active mint keys
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip(self))]
|
||||||
pub async fn active_keys(&self) -> Result<Option<Keys>, Error> {
|
pub async fn active_keys(&self) -> Result<Option<Keys>, Error> {
|
||||||
let active_keyset_id = self.active_mint_keyset().await?;
|
let active_keyset_id = self.active_mint_keyset().await?;
|
||||||
@@ -1044,7 +1051,7 @@ impl Wallet {
|
|||||||
Ok(melted)
|
Ok(melted)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Select proofs
|
/// Select proofs
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip(self))]
|
||||||
pub async fn select_proofs(
|
pub async fn select_proofs(
|
||||||
&self,
|
&self,
|
||||||
@@ -1313,6 +1320,7 @@ impl Wallet {
|
|||||||
Ok(total_amount)
|
Ok(total_amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Restore
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip(self))]
|
||||||
pub async fn restore(&self) -> Result<Amount, Error> {
|
pub async fn restore(&self) -> Result<Amount, Error> {
|
||||||
// Check that mint is in store of mints
|
// 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::types::{Melted, MintQuote};
|
||||||
use crate::{Amount, UncheckedUrl, Wallet};
|
use crate::{Amount, UncheckedUrl, Wallet};
|
||||||
|
|
||||||
|
/// Multi Mint Wallet
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct MultiMintWallet {
|
pub struct MultiMintWallet {
|
||||||
|
/// Wallets
|
||||||
pub wallets: Arc<Mutex<HashMap<WalletKey, Wallet>>>,
|
pub wallets: Arc<Mutex<HashMap<WalletKey, Wallet>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Wallet Key
|
||||||
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct WalletKey {
|
pub struct WalletKey {
|
||||||
mint_url: UncheckedUrl,
|
mint_url: UncheckedUrl,
|
||||||
@@ -35,6 +38,7 @@ impl fmt::Display for WalletKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl WalletKey {
|
impl WalletKey {
|
||||||
|
/// Create new [`WalletKey`]
|
||||||
pub fn new(mint_url: UncheckedUrl, unit: CurrencyUnit) -> Self {
|
pub fn new(mint_url: UncheckedUrl, unit: CurrencyUnit) -> Self {
|
||||||
Self { mint_url, unit }
|
Self { mint_url, unit }
|
||||||
}
|
}
|
||||||
@@ -255,7 +259,7 @@ impl MultiMintWallet {
|
|||||||
wallet.melt("e.id, SplitTarget::default()).await
|
wallet.melt("e.id, SplitTarget::default()).await
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restore
|
/// Restore
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip(self))]
|
||||||
pub async fn restore(&self, wallet_key: &WalletKey) -> Result<Amount, Error> {
|
pub async fn restore(&self, wallet_key: &WalletKey) -> Result<Amount, Error> {
|
||||||
let wallet = self
|
let wallet = self
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ pub fn proof_to_token(
|
|||||||
Ok(Token::new(mint_url, proofs, memo, unit)?)
|
Ok(Token::new(mint_url, proofs, memo, unit)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "nostr")]
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user