feat: use Uuid as mint and melt quote ids (#469)

This commit is contained in:
Timothée Delabrouille
2024-12-05 10:58:53 +01:00
committed by GitHub
parent 5ad4328a4e
commit 7d87c4806c
25 changed files with 509 additions and 307 deletions

View File

@@ -29,6 +29,7 @@ moka = { version = "0.11.1", features = ["future"] }
serde_json = "1" serde_json = "1"
paste = "1.0.15" paste = "1.0.15"
serde = { version = "1.0.210", features = ["derive"] } serde = { version = "1.0.210", features = ["derive"] }
uuid = { version = "1", features = ["v4", "serde"] }
[features] [features]
swagger = ["cdk/swagger", "dep:utoipa"] swagger = ["cdk/swagger", "dep:utoipa"]

View File

@@ -45,6 +45,8 @@ mod swagger_imports {
#[cfg(feature = "swagger")] #[cfg(feature = "swagger")]
use swagger_imports::*; use swagger_imports::*;
#[cfg(feature = "swagger")]
use uuid::Uuid;
/// CDK Mint State /// CDK Mint State
#[derive(Clone)] #[derive(Clone)]
@@ -75,16 +77,16 @@ pub struct MintState {
KeySet, KeySet,
KeySetInfo, KeySetInfo,
KeySetVersion, KeySetVersion,
MeltBolt11Request, MeltBolt11Request<Uuid>,
MeltQuoteBolt11Request, MeltQuoteBolt11Request,
MeltQuoteBolt11Response, MeltQuoteBolt11Response<Uuid>,
MeltQuoteState, MeltQuoteState,
MeltMethodSettings, MeltMethodSettings,
MintBolt11Request, MintBolt11Request<Uuid>,
MintBolt11Response, MintBolt11Response,
MintInfo, MintInfo,
MintQuoteBolt11Request, MintQuoteBolt11Request,
MintQuoteBolt11Response, MintQuoteBolt11Response<Uuid>,
MintQuoteState, MintQuoteState,
MintMethodSettings, MintMethodSettings,
MintVersion, MintVersion,

View File

@@ -13,6 +13,7 @@ use cdk::nuts::{
use cdk::util::unix_time; use cdk::util::unix_time;
use cdk::Error; use cdk::Error;
use paste::paste; use paste::paste;
use uuid::Uuid;
use crate::ws::main_websocket; use crate::ws::main_websocket;
use crate::MintState; use crate::MintState;
@@ -50,8 +51,16 @@ macro_rules! post_cache_wrapper {
} }
post_cache_wrapper!(post_swap, SwapRequest, SwapResponse); post_cache_wrapper!(post_swap, SwapRequest, SwapResponse);
post_cache_wrapper!(post_mint_bolt11, MintBolt11Request, MintBolt11Response); post_cache_wrapper!(
post_cache_wrapper!(post_melt_bolt11, MeltBolt11Request, MeltQuoteBolt11Response); post_mint_bolt11,
MintBolt11Request<Uuid>,
MintBolt11Response
);
post_cache_wrapper!(
post_melt_bolt11,
MeltBolt11Request<Uuid>,
MeltQuoteBolt11Response<Uuid>
);
#[cfg_attr(feature = "swagger", utoipa::path( #[cfg_attr(feature = "swagger", utoipa::path(
get, get,
@@ -137,7 +146,7 @@ pub async fn get_keysets(State(state): State<MintState>) -> Result<Json<KeysetRe
pub async fn post_mint_bolt11_quote( pub async fn post_mint_bolt11_quote(
State(state): State<MintState>, State(state): State<MintState>,
Json(payload): Json<MintQuoteBolt11Request>, Json(payload): Json<MintQuoteBolt11Request>,
) -> Result<Json<MintQuoteBolt11Response>, Response> { ) -> Result<Json<MintQuoteBolt11Response<Uuid>>, Response> {
let quote = state let quote = state
.mint .mint
.get_mint_bolt11_quote(payload) .get_mint_bolt11_quote(payload)
@@ -164,8 +173,8 @@ pub async fn post_mint_bolt11_quote(
/// Get mint quote state. /// Get mint quote state.
pub async fn get_check_mint_bolt11_quote( pub async fn get_check_mint_bolt11_quote(
State(state): State<MintState>, State(state): State<MintState>,
Path(quote_id): Path<String>, Path(quote_id): Path<Uuid>,
) -> Result<Json<MintQuoteBolt11Response>, Response> { ) -> Result<Json<MintQuoteBolt11Response<Uuid>>, Response> {
let quote = state let quote = state
.mint .mint
.check_mint_quote(&quote_id) .check_mint_quote(&quote_id)
@@ -199,7 +208,7 @@ pub async fn ws_handler(State(state): State<MintState>, ws: WebSocketUpgrade) ->
))] ))]
pub async fn post_mint_bolt11( pub async fn post_mint_bolt11(
State(state): State<MintState>, State(state): State<MintState>,
Json(payload): Json<MintBolt11Request>, Json(payload): Json<MintBolt11Request<Uuid>>,
) -> Result<Json<MintBolt11Response>, Response> { ) -> Result<Json<MintBolt11Response>, Response> {
let res = state let res = state
.mint .mint
@@ -227,7 +236,7 @@ pub async fn post_mint_bolt11(
pub async fn post_melt_bolt11_quote( pub async fn post_melt_bolt11_quote(
State(state): State<MintState>, State(state): State<MintState>,
Json(payload): Json<MeltQuoteBolt11Request>, Json(payload): Json<MeltQuoteBolt11Request>,
) -> Result<Json<MeltQuoteBolt11Response>, Response> { ) -> Result<Json<MeltQuoteBolt11Response<Uuid>>, Response> {
let quote = state let quote = state
.mint .mint
.get_melt_bolt11_quote(&payload) .get_melt_bolt11_quote(&payload)
@@ -254,8 +263,8 @@ pub async fn post_melt_bolt11_quote(
/// Get melt quote state. /// Get melt quote state.
pub async fn get_check_melt_bolt11_quote( pub async fn get_check_melt_bolt11_quote(
State(state): State<MintState>, State(state): State<MintState>,
Path(quote_id): Path<String>, Path(quote_id): Path<Uuid>,
) -> Result<Json<MeltQuoteBolt11Response>, Response> { ) -> Result<Json<MeltQuoteBolt11Response<Uuid>>, Response> {
let quote = state let quote = state
.mint .mint
.check_melt_quote(&quote_id) .check_melt_quote(&quote_id)
@@ -283,8 +292,8 @@ pub async fn get_check_melt_bolt11_quote(
/// Requests tokens to be destroyed and sent out via Lightning. /// Requests tokens to be destroyed and sent out via Lightning.
pub async fn post_melt_bolt11( pub async fn post_melt_bolt11(
State(state): State<MintState>, State(state): State<MintState>,
Json(payload): Json<MeltBolt11Request>, Json(payload): Json<MeltBolt11Request<Uuid>>,
) -> Result<Json<MeltQuoteBolt11Response>, Response> { ) -> Result<Json<MeltQuoteBolt11Response<Uuid>>, Response> {
let res = state let res = state
.mint .mint
.melt_bolt11(&payload) .melt_bolt11(&payload)

View File

@@ -20,7 +20,6 @@ tracing = { version = "0.1", default-features = false, features = ["attributes",
thiserror = "1" thiserror = "1"
serde = "1" serde = "1"
serde_json = "1" serde_json = "1"
uuid = { version = "1", features = ["v4"] }
lightning-invoice = { version = "0.32.0", features = ["serde", "std"] } lightning-invoice = { version = "0.32.0", features = ["serde", "std"] }
tokio-stream = "0.1.15" tokio-stream = "0.1.15"
rand = "0.8.5" rand = "0.8.5"

View File

@@ -120,7 +120,7 @@ async fn test_regtest_mint_melt_round_trip() -> Result<()> {
_ => panic!("Wrong payload"), _ => panic!("Wrong payload"),
}; };
assert_eq!(payload.amount + payload.fee_reserve, 100.into()); assert_eq!(payload.amount + payload.fee_reserve, 100.into());
assert_eq!(payload.quote, melt.id); assert_eq!(payload.quote.to_string(), melt.id);
assert_eq!(payload.state, MeltQuoteState::Unpaid); assert_eq!(payload.state, MeltQuoteState::Unpaid);
// get current state // get current state
@@ -131,7 +131,7 @@ async fn test_regtest_mint_melt_round_trip() -> Result<()> {
_ => panic!("Wrong payload"), _ => panic!("Wrong payload"),
}; };
assert_eq!(payload.amount + payload.fee_reserve, 100.into()); assert_eq!(payload.amount + payload.fee_reserve, 100.into());
assert_eq!(payload.quote, melt.id); assert_eq!(payload.quote.to_string(), melt.id);
assert_eq!(payload.state, MeltQuoteState::Paid); assert_eq!(payload.state, MeltQuoteState::Paid);
Ok(()) Ok(())

View File

@@ -24,3 +24,4 @@ tracing = { version = "0.1", default-features = false, features = ["attributes",
serde = { version = "1", default-features = false, features = ["derive"] } serde = { version = "1", default-features = false, features = ["derive"] }
serde_json = "1" serde_json = "1"
lightning-invoice = { version = "0.32.0", features = ["serde", "std"] } lightning-invoice = { version = "0.32.0", features = ["serde", "std"] }
uuid = { version = "1", features = ["v4", "serde"] }

View File

@@ -8,12 +8,15 @@ use cdk::mint_url::MintUrl;
use cdk::nuts::{CurrencyUnit, MintQuoteState, Proof, State}; use cdk::nuts::{CurrencyUnit, MintQuoteState, Proof, State};
use cdk::Amount; use cdk::Amount;
use lightning_invoice::Bolt11Invoice; use lightning_invoice::Bolt11Invoice;
use redb::{Database, MultimapTableDefinition, ReadableTable, TableDefinition}; use redb::{
Database, MultimapTableDefinition, ReadableMultimapTable, ReadableTable, TableDefinition,
};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use uuid::Uuid;
use super::{Error, PROOFS_STATE_TABLE, PROOFS_TABLE, QUOTE_SIGNATURES_TABLE}; use super::{Error, PROOFS_STATE_TABLE, PROOFS_TABLE, QUOTE_SIGNATURES_TABLE};
const MINT_QUOTES_TABLE: TableDefinition<&str, &str> = TableDefinition::new("mint_quotes"); const ID_STR_MINT_QUOTES_TABLE: TableDefinition<&str, &str> = TableDefinition::new("mint_quotes");
const PENDING_PROOFS_TABLE: TableDefinition<[u8; 33], &str> = const PENDING_PROOFS_TABLE: TableDefinition<[u8; 33], &str> =
TableDefinition::new("pending_proofs"); TableDefinition::new("pending_proofs");
const SPENT_PROOFS_TABLE: TableDefinition<[u8; 33], &str> = TableDefinition::new("spent_proofs"); const SPENT_PROOFS_TABLE: TableDefinition<[u8; 33], &str> = TableDefinition::new("spent_proofs");
@@ -36,10 +39,158 @@ pub fn migrate_03_to_04(db: Arc<Database>) -> Result<u32, Error> {
Ok(4) Ok(4)
} }
pub fn migrate_04_to_05(db: Arc<Database>) -> Result<u32, Error> {
let write_txn = db.begin_write()?;
// Mint quotes
{
const MINT_QUOTE_TABLE_NAME: &str = "mint_quotes";
const OLD_TABLE: TableDefinition<&str, &str> = TableDefinition::new(MINT_QUOTE_TABLE_NAME);
const NEW_TABLE: TableDefinition<[u8; 16], &str> =
TableDefinition::new(MINT_QUOTE_TABLE_NAME);
let old_table = write_txn.open_table(OLD_TABLE)?;
let mut tmp_hashmap = HashMap::new();
for (k, v) in old_table.iter().map_err(Error::from)?.flatten() {
let quote_id = Uuid::try_parse(k.value()).unwrap();
tmp_hashmap.insert(quote_id, v.value().to_string());
}
write_txn.delete_table(old_table).map_err(Error::from)?;
let mut new_table = write_txn.open_table(NEW_TABLE)?;
for (k, v) in tmp_hashmap.into_iter() {
new_table
.insert(k.as_bytes(), v.as_str())
.map_err(Error::from)?;
}
}
// Melt quotes
{
const MELT_QUOTE_TABLE_NAME: &str = "melt_quotes";
const OLD_TABLE: TableDefinition<&str, &str> = TableDefinition::new(MELT_QUOTE_TABLE_NAME);
const NEW_TABLE: TableDefinition<[u8; 16], &str> =
TableDefinition::new(MELT_QUOTE_TABLE_NAME);
let old_table = write_txn.open_table(OLD_TABLE)?;
let mut tmp_hashmap = HashMap::new();
for (k, v) in old_table.iter().map_err(Error::from)?.flatten() {
let quote_id = Uuid::try_parse(k.value()).unwrap();
tmp_hashmap.insert(quote_id, v.value().to_string());
}
write_txn.delete_table(old_table).map_err(Error::from)?;
let mut new_table = write_txn.open_table(NEW_TABLE)?;
for (k, v) in tmp_hashmap.into_iter() {
new_table
.insert(k.as_bytes(), v.as_str())
.map_err(Error::from)?;
}
}
// Quote proofs
{
const QUOTE_PROOFS_TABLE_NAME: &str = "quote_proofs";
const OLD_TABLE: MultimapTableDefinition<&str, [u8; 33]> =
MultimapTableDefinition::new(QUOTE_PROOFS_TABLE_NAME);
const NEW_TABLE: MultimapTableDefinition<[u8; 16], [u8; 33]> =
MultimapTableDefinition::new(QUOTE_PROOFS_TABLE_NAME);
let old_table = write_txn.open_multimap_table(OLD_TABLE)?;
let mut tmp_hashmap = HashMap::new();
for (k, v) in old_table.iter().map_err(Error::from)?.flatten() {
let quote_id = Uuid::try_parse(k.value()).unwrap();
let ys: Vec<[u8; 33]> = v.into_iter().flatten().map(|v| v.value()).collect();
tmp_hashmap.insert(quote_id, ys);
}
write_txn
.delete_multimap_table(old_table)
.map_err(Error::from)?;
let mut new_table = write_txn.open_multimap_table(NEW_TABLE)?;
for (quote_id, blind_messages) in tmp_hashmap.into_iter() {
for blind_message in blind_messages {
new_table
.insert(quote_id.as_bytes(), blind_message)
.map_err(Error::from)?;
}
}
}
// Quote signatures
{
const QUOTE_SIGNATURES_TABLE_NAME: &str = "quote_signatures";
const OLD_TABLE: MultimapTableDefinition<&str, [u8; 33]> =
MultimapTableDefinition::new(QUOTE_SIGNATURES_TABLE_NAME);
const NEW_TABLE: MultimapTableDefinition<[u8; 16], [u8; 33]> =
MultimapTableDefinition::new(QUOTE_SIGNATURES_TABLE_NAME);
let old_table = write_txn.open_multimap_table(OLD_TABLE)?;
let mut tmp_hashmap = HashMap::new();
for (k, v) in old_table.iter().map_err(Error::from)?.flatten() {
let quote_id = Uuid::try_parse(k.value()).unwrap();
let ys: Vec<[u8; 33]> = v.into_iter().flatten().map(|v| v.value()).collect();
tmp_hashmap.insert(quote_id, ys);
}
write_txn
.delete_multimap_table(old_table)
.map_err(Error::from)?;
let mut new_table = write_txn.open_multimap_table(NEW_TABLE)?;
for (quote_id, signatures) in tmp_hashmap.into_iter() {
for signature in signatures {
new_table
.insert(quote_id.as_bytes(), signature)
.map_err(Error::from)?;
}
}
}
// Melt requests
{
const MELT_REQUESTS_TABLE_NAME: &str = "melt_requests";
const OLD_TABLE: TableDefinition<&str, (&str, &str)> =
TableDefinition::new(MELT_REQUESTS_TABLE_NAME);
const NEW_TABLE: TableDefinition<[u8; 16], (&str, &str)> =
TableDefinition::new(MELT_REQUESTS_TABLE_NAME);
let old_table = write_txn.open_table(OLD_TABLE)?;
let mut tmp_hashmap = HashMap::new();
for (k, v) in old_table.iter().map_err(Error::from)?.flatten() {
let quote_id = Uuid::try_parse(k.value()).unwrap();
let value = v.value();
tmp_hashmap.insert(quote_id, (value.0.to_string(), value.1.to_string()));
}
write_txn.delete_table(old_table).map_err(Error::from)?;
let mut new_table = write_txn.open_table(NEW_TABLE)?;
for (k, v) in tmp_hashmap.into_iter() {
new_table
.insert(k.as_bytes(), (v.0.as_str(), v.1.as_str()))
.map_err(Error::from)?;
}
}
write_txn.commit().map_err(Error::from)?;
Ok(5)
}
/// Mint Quote Info /// Mint Quote Info
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
struct V1MintQuote { struct V1MintQuote {
pub id: String, pub id: Uuid,
pub mint_url: MintUrl, pub mint_url: MintUrl,
pub amount: Amount, pub amount: Amount,
pub unit: CurrencyUnit, pub unit: CurrencyUnit,
@@ -66,7 +217,7 @@ impl From<V1MintQuote> for MintQuote {
fn migrate_mint_quotes_01_to_02(db: Arc<Database>) -> Result<(), Error> { fn migrate_mint_quotes_01_to_02(db: Arc<Database>) -> Result<(), Error> {
let read_txn = db.begin_read().map_err(Error::from)?; let read_txn = db.begin_read().map_err(Error::from)?;
let table = read_txn let table = read_txn
.open_table(MINT_QUOTES_TABLE) .open_table(ID_STR_MINT_QUOTES_TABLE)
.map_err(Error::from)?; .map_err(Error::from)?;
let mint_quotes: HashMap<String, Option<V1MintQuote>>; let mint_quotes: HashMap<String, Option<V1MintQuote>>;
@@ -94,7 +245,7 @@ fn migrate_mint_quotes_01_to_02(db: Arc<Database>) -> Result<(), Error> {
{ {
let mut table = write_txn let mut table = write_txn
.open_table(MINT_QUOTES_TABLE) .open_table(ID_STR_MINT_QUOTES_TABLE)
.map_err(Error::from)?; .map_err(Error::from)?;
for (quote_id, quote) in migrated_mint_quotes { for (quote_id, quote) in migrated_mint_quotes {
match quote { match quote {

View File

@@ -17,8 +17,9 @@ use cdk::nuts::{
}; };
use cdk::types::LnKey; use cdk::types::LnKey;
use cdk::{cdk_database, mint}; use cdk::{cdk_database, mint};
use migrations::migrate_01_to_02; use migrations::{migrate_01_to_02, migrate_04_to_05};
use redb::{Database, MultimapTableDefinition, ReadableTable, TableDefinition}; use redb::{Database, MultimapTableDefinition, ReadableTable, TableDefinition};
use uuid::Uuid;
use super::error::Error; use super::error::Error;
use crate::migrations::migrate_00_to_01; use crate::migrations::migrate_00_to_01;
@@ -28,22 +29,23 @@ mod migrations;
const ACTIVE_KEYSETS_TABLE: TableDefinition<&str, &str> = TableDefinition::new("active_keysets"); const ACTIVE_KEYSETS_TABLE: TableDefinition<&str, &str> = TableDefinition::new("active_keysets");
const KEYSETS_TABLE: TableDefinition<&str, &str> = TableDefinition::new("keysets"); const KEYSETS_TABLE: TableDefinition<&str, &str> = TableDefinition::new("keysets");
const MINT_QUOTES_TABLE: TableDefinition<&str, &str> = TableDefinition::new("mint_quotes"); const MINT_QUOTES_TABLE: TableDefinition<[u8; 16], &str> = TableDefinition::new("mint_quotes");
const MELT_QUOTES_TABLE: TableDefinition<&str, &str> = TableDefinition::new("melt_quotes"); const MELT_QUOTES_TABLE: TableDefinition<[u8; 16], &str> = TableDefinition::new("melt_quotes");
const PROOFS_TABLE: TableDefinition<[u8; 33], &str> = TableDefinition::new("proofs"); const PROOFS_TABLE: TableDefinition<[u8; 33], &str> = TableDefinition::new("proofs");
const PROOFS_STATE_TABLE: TableDefinition<[u8; 33], &str> = TableDefinition::new("proofs_state"); const PROOFS_STATE_TABLE: TableDefinition<[u8; 33], &str> = TableDefinition::new("proofs_state");
const CONFIG_TABLE: TableDefinition<&str, &str> = TableDefinition::new("config"); const CONFIG_TABLE: TableDefinition<&str, &str> = TableDefinition::new("config");
// Key is hex blinded_message B_ value is blinded_signature // Key is hex blinded_message B_ value is blinded_signature
const BLINDED_SIGNATURES: TableDefinition<[u8; 33], &str> = const BLINDED_SIGNATURES: TableDefinition<[u8; 33], &str> =
TableDefinition::new("blinded_signatures"); TableDefinition::new("blinded_signatures");
const QUOTE_PROOFS_TABLE: MultimapTableDefinition<&str, [u8; 33]> = const QUOTE_PROOFS_TABLE: MultimapTableDefinition<[u8; 16], [u8; 33]> =
MultimapTableDefinition::new("quote_proofs"); MultimapTableDefinition::new("quote_proofs");
const QUOTE_SIGNATURES_TABLE: MultimapTableDefinition<&str, [u8; 33]> = const QUOTE_SIGNATURES_TABLE: MultimapTableDefinition<[u8; 16], [u8; 33]> =
MultimapTableDefinition::new("quote_signatures"); MultimapTableDefinition::new("quote_signatures");
const MELT_REQUESTS: TableDefinition<&str, (&str, &str)> = TableDefinition::new("melt_requests"); const MELT_REQUESTS: TableDefinition<[u8; 16], (&str, &str)> =
TableDefinition::new("melt_requests");
const DATABASE_VERSION: u32 = 4; const DATABASE_VERSION: u32 = 5;
/// Mint Redbdatabase /// Mint Redbdatabase
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -93,6 +95,10 @@ impl MintRedbDatabase {
current_file_version = migrate_03_to_04(Arc::clone(&db))?; current_file_version = migrate_03_to_04(Arc::clone(&db))?;
} }
if current_file_version == 4 {
current_file_version = migrate_04_to_05(Arc::clone(&db))?;
}
if current_file_version != DATABASE_VERSION { if current_file_version != DATABASE_VERSION {
tracing::warn!( tracing::warn!(
"Database upgrade did not complete at {} current is {}", "Database upgrade did not complete at {} current is {}",
@@ -261,7 +267,7 @@ impl MintDatabase for MintRedbDatabase {
.map_err(Error::from)?; .map_err(Error::from)?;
table table
.insert( .insert(
quote.id.as_str(), quote.id.as_bytes(),
serde_json::to_string(&quote).map_err(Error::from)?.as_str(), serde_json::to_string(&quote).map_err(Error::from)?.as_str(),
) )
.map_err(Error::from)?; .map_err(Error::from)?;
@@ -271,13 +277,13 @@ impl MintDatabase for MintRedbDatabase {
Ok(()) Ok(())
} }
async fn get_mint_quote(&self, quote_id: &str) -> Result<Option<MintQuote>, Self::Err> { async fn get_mint_quote(&self, quote_id: &Uuid) -> Result<Option<MintQuote>, Self::Err> {
let read_txn = self.db.begin_read().map_err(Error::from)?; let read_txn = self.db.begin_read().map_err(Error::from)?;
let table = read_txn let table = read_txn
.open_table(MINT_QUOTES_TABLE) .open_table(MINT_QUOTES_TABLE)
.map_err(Error::from)?; .map_err(Error::from)?;
match table.get(quote_id).map_err(Error::from)? { match table.get(quote_id.as_bytes()).map_err(Error::from)? {
Some(quote) => Ok(serde_json::from_str(quote.value()).map_err(Error::from)?), Some(quote) => Ok(serde_json::from_str(quote.value()).map_err(Error::from)?),
None => Ok(None), None => Ok(None),
} }
@@ -285,7 +291,7 @@ impl MintDatabase for MintRedbDatabase {
async fn update_mint_quote_state( async fn update_mint_quote_state(
&self, &self,
quote_id: &str, quote_id: &Uuid,
state: MintQuoteState, state: MintQuoteState,
) -> Result<MintQuoteState, Self::Err> { ) -> Result<MintQuoteState, Self::Err> {
let write_txn = self.db.begin_write().map_err(Error::from)?; let write_txn = self.db.begin_write().map_err(Error::from)?;
@@ -297,7 +303,7 @@ impl MintDatabase for MintRedbDatabase {
.map_err(Error::from)?; .map_err(Error::from)?;
let quote_guard = table let quote_guard = table
.get(quote_id) .get(quote_id.as_bytes())
.map_err(Error::from)? .map_err(Error::from)?
.ok_or(Error::UnknownMintInfo)?; .ok_or(Error::UnknownMintInfo)?;
@@ -316,7 +322,7 @@ impl MintDatabase for MintRedbDatabase {
table table
.insert( .insert(
quote_id, quote_id.as_bytes(),
serde_json::to_string(&mint_quote) serde_json::to_string(&mint_quote)
.map_err(Error::from)? .map_err(Error::from)?
.as_str(), .as_str(),
@@ -376,14 +382,14 @@ impl MintDatabase for MintRedbDatabase {
Ok(quotes) Ok(quotes)
} }
async fn remove_mint_quote(&self, quote_id: &str) -> Result<(), Self::Err> { async fn remove_mint_quote(&self, quote_id: &Uuid) -> Result<(), Self::Err> {
let write_txn = self.db.begin_write().map_err(Error::from)?; let write_txn = self.db.begin_write().map_err(Error::from)?;
{ {
let mut table = write_txn let mut table = write_txn
.open_table(MINT_QUOTES_TABLE) .open_table(MINT_QUOTES_TABLE)
.map_err(Error::from)?; .map_err(Error::from)?;
table.remove(quote_id).map_err(Error::from)?; table.remove(quote_id.as_bytes()).map_err(Error::from)?;
} }
write_txn.commit().map_err(Error::from)?; write_txn.commit().map_err(Error::from)?;
@@ -399,7 +405,7 @@ impl MintDatabase for MintRedbDatabase {
.map_err(Error::from)?; .map_err(Error::from)?;
table table
.insert( .insert(
quote.id.as_str(), quote.id.as_bytes(),
serde_json::to_string(&quote).map_err(Error::from)?.as_str(), serde_json::to_string(&quote).map_err(Error::from)?.as_str(),
) )
.map_err(Error::from)?; .map_err(Error::from)?;
@@ -409,20 +415,20 @@ impl MintDatabase for MintRedbDatabase {
Ok(()) Ok(())
} }
async fn get_melt_quote(&self, quote_id: &str) -> Result<Option<mint::MeltQuote>, Self::Err> { async fn get_melt_quote(&self, quote_id: &Uuid) -> Result<Option<mint::MeltQuote>, Self::Err> {
let read_txn = self.db.begin_read().map_err(Error::from)?; let read_txn = self.db.begin_read().map_err(Error::from)?;
let table = read_txn let table = read_txn
.open_table(MELT_QUOTES_TABLE) .open_table(MELT_QUOTES_TABLE)
.map_err(Error::from)?; .map_err(Error::from)?;
let quote = table.get(quote_id).map_err(Error::from)?; let quote = table.get(quote_id.as_bytes()).map_err(Error::from)?;
Ok(quote.map(|q| serde_json::from_str(q.value()).unwrap())) Ok(quote.map(|q| serde_json::from_str(q.value()).unwrap()))
} }
async fn update_melt_quote_state( async fn update_melt_quote_state(
&self, &self,
quote_id: &str, quote_id: &Uuid,
state: MeltQuoteState, state: MeltQuoteState,
) -> Result<MeltQuoteState, Self::Err> { ) -> Result<MeltQuoteState, Self::Err> {
let write_txn = self.db.begin_write().map_err(Error::from)?; let write_txn = self.db.begin_write().map_err(Error::from)?;
@@ -435,7 +441,7 @@ impl MintDatabase for MintRedbDatabase {
.map_err(Error::from)?; .map_err(Error::from)?;
let quote_guard = table let quote_guard = table
.get(quote_id) .get(quote_id.as_bytes())
.map_err(Error::from)? .map_err(Error::from)?
.ok_or(Error::UnknownMintInfo)?; .ok_or(Error::UnknownMintInfo)?;
@@ -454,7 +460,7 @@ impl MintDatabase for MintRedbDatabase {
table table
.insert( .insert(
quote_id, quote_id.as_bytes(),
serde_json::to_string(&melt_quote) serde_json::to_string(&melt_quote)
.map_err(Error::from)? .map_err(Error::from)?
.as_str(), .as_str(),
@@ -483,21 +489,21 @@ impl MintDatabase for MintRedbDatabase {
Ok(quotes) Ok(quotes)
} }
async fn remove_melt_quote(&self, quote_id: &str) -> Result<(), Self::Err> { async fn remove_melt_quote(&self, quote_id: &Uuid) -> Result<(), Self::Err> {
let write_txn = self.db.begin_write().map_err(Error::from)?; let write_txn = self.db.begin_write().map_err(Error::from)?;
{ {
let mut table = write_txn let mut table = write_txn
.open_table(MELT_QUOTES_TABLE) .open_table(MELT_QUOTES_TABLE)
.map_err(Error::from)?; .map_err(Error::from)?;
table.remove(quote_id).map_err(Error::from)?; table.remove(quote_id.as_bytes()).map_err(Error::from)?;
} }
write_txn.commit().map_err(Error::from)?; write_txn.commit().map_err(Error::from)?;
Ok(()) Ok(())
} }
async fn add_proofs(&self, proofs: Proofs, quote_id: Option<String>) -> Result<(), Self::Err> { async fn add_proofs(&self, proofs: Proofs, quote_id: Option<Uuid>) -> Result<(), Self::Err> {
let write_txn = self.db.begin_write().map_err(Error::from)?; let write_txn = self.db.begin_write().map_err(Error::from)?;
{ {
@@ -519,7 +525,7 @@ impl MintDatabase for MintRedbDatabase {
if let Some(quote_id) = &quote_id { if let Some(quote_id) = &quote_id {
quote_proofs_table quote_proofs_table
.insert(quote_id.as_str(), y) .insert(quote_id.as_bytes(), y)
.map_err(Error::from)?; .map_err(Error::from)?;
} }
} }
@@ -547,13 +553,13 @@ impl MintDatabase for MintRedbDatabase {
Ok(proofs) Ok(proofs)
} }
async fn get_proof_ys_by_quote_id(&self, quote_id: &str) -> Result<Vec<PublicKey>, Self::Err> { async fn get_proof_ys_by_quote_id(&self, quote_id: &Uuid) -> Result<Vec<PublicKey>, Self::Err> {
let read_txn = self.db.begin_read().map_err(Error::from)?; let read_txn = self.db.begin_read().map_err(Error::from)?;
let table = read_txn let table = read_txn
.open_multimap_table(QUOTE_PROOFS_TABLE) .open_multimap_table(QUOTE_PROOFS_TABLE)
.map_err(Error::from)?; .map_err(Error::from)?;
let ys = table.get(quote_id).map_err(Error::from)?; let ys = table.get(quote_id.as_bytes()).map_err(Error::from)?;
let proof_ys = ys.fold(Vec::new(), |mut acc, y| { let proof_ys = ys.fold(Vec::new(), |mut acc, y| {
if let Ok(y) = y { if let Ok(y) = y {
@@ -660,7 +666,7 @@ impl MintDatabase for MintRedbDatabase {
&self, &self,
blinded_messages: &[PublicKey], blinded_messages: &[PublicKey],
blind_signatures: &[BlindSignature], blind_signatures: &[BlindSignature],
quote_id: Option<String>, quote_id: Option<Uuid>,
) -> Result<(), Self::Err> { ) -> Result<(), Self::Err> {
let write_txn = self.db.begin_write().map_err(Error::from)?; let write_txn = self.db.begin_write().map_err(Error::from)?;
@@ -681,7 +687,7 @@ impl MintDatabase for MintRedbDatabase {
if let Some(quote_id) = &quote_id { if let Some(quote_id) = &quote_id {
quote_sigs_table quote_sigs_table
.insert(quote_id.as_str(), blinded_message.to_bytes()) .insert(quote_id.as_bytes(), blinded_message.to_bytes())
.map_err(Error::from)?; .map_err(Error::from)?;
} }
} }
@@ -740,7 +746,7 @@ impl MintDatabase for MintRedbDatabase {
/// Add melt request /// Add melt request
async fn add_melt_request( async fn add_melt_request(
&self, &self,
melt_request: MeltBolt11Request, melt_request: MeltBolt11Request<Uuid>,
ln_key: LnKey, ln_key: LnKey,
) -> Result<(), Self::Err> { ) -> Result<(), Self::Err> {
let write_txn = self.db.begin_write().map_err(Error::from)?; let write_txn = self.db.begin_write().map_err(Error::from)?;
@@ -748,7 +754,7 @@ impl MintDatabase for MintRedbDatabase {
table table
.insert( .insert(
melt_request.quote.as_str(), melt_request.quote.as_bytes(),
( (
serde_json::to_string(&melt_request)?.as_str(), serde_json::to_string(&melt_request)?.as_str(),
serde_json::to_string(&ln_key)?.as_str(), serde_json::to_string(&ln_key)?.as_str(),
@@ -761,12 +767,12 @@ impl MintDatabase for MintRedbDatabase {
/// Get melt request /// Get melt request
async fn get_melt_request( async fn get_melt_request(
&self, &self,
quote_id: &str, quote_id: &Uuid,
) -> Result<Option<(MeltBolt11Request, LnKey)>, Self::Err> { ) -> Result<Option<(MeltBolt11Request<Uuid>, LnKey)>, Self::Err> {
let read_txn = self.db.begin_read().map_err(Error::from)?; let read_txn = self.db.begin_read().map_err(Error::from)?;
let table = read_txn.open_table(MELT_REQUESTS).map_err(Error::from)?; let table = read_txn.open_table(MELT_REQUESTS).map_err(Error::from)?;
match table.get(quote_id).map_err(Error::from)? { match table.get(quote_id.as_bytes()).map_err(Error::from)? {
Some(melt_request) => { Some(melt_request) => {
let (melt_request_str, ln_key_str) = melt_request.value(); let (melt_request_str, ln_key_str) = melt_request.value();
let melt_request = serde_json::from_str(melt_request_str)?; let melt_request = serde_json::from_str(melt_request_str)?;
@@ -781,14 +787,14 @@ impl MintDatabase for MintRedbDatabase {
/// Get [`BlindSignature`]s for quote /// Get [`BlindSignature`]s for quote
async fn get_blind_signatures_for_quote( async fn get_blind_signatures_for_quote(
&self, &self,
quote_id: &str, quote_id: &Uuid,
) -> Result<Vec<BlindSignature>, Self::Err> { ) -> Result<Vec<BlindSignature>, Self::Err> {
let read_txn = self.db.begin_read().map_err(Error::from)?; let read_txn = self.db.begin_read().map_err(Error::from)?;
let quote_proofs_table = read_txn let quote_proofs_table = read_txn
.open_multimap_table(QUOTE_SIGNATURES_TABLE) .open_multimap_table(QUOTE_SIGNATURES_TABLE)
.map_err(Error::from)?; .map_err(Error::from)?;
let ys = quote_proofs_table.get(quote_id).unwrap(); let ys = quote_proofs_table.get(quote_id.as_bytes()).unwrap();
let ys: Vec<[u8; 33]> = ys.into_iter().flatten().map(|v| v.value()).collect(); let ys: Vec<[u8; 33]> = ys.into_iter().flatten().map(|v| v.value()).collect();

View File

@@ -19,7 +19,7 @@ wallet = ["cdk/wallet"]
async-trait = "0.1" async-trait = "0.1"
cdk = { path = "../cdk", version = "0.5.0", default-features = false } cdk = { path = "../cdk", version = "0.5.0", default-features = false }
bitcoin = { version = "0.32.2", default-features = false } bitcoin = { version = "0.32.2", default-features = false }
sqlx = { version = "0.6.3", default-features = false, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] } sqlx = { version = "0.6.3", default-features = false, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate", "uuid"] }
thiserror = "1" thiserror = "1"
tokio = { version = "1", features = [ tokio = { version = "1", features = [
"time", "time",
@@ -29,3 +29,4 @@ tokio = { version = "1", features = [
tracing = { version = "0.1", default-features = false, features = ["attributes", "log"] } tracing = { version = "0.1", default-features = false, features = ["attributes", "log"] }
serde_json = "1" serde_json = "1"
lightning-invoice = { version = "0.32.0", features = ["serde", "std"] } lightning-invoice = { version = "0.32.0", features = ["serde", "std"] }
uuid = { version = "1", features = ["v4", "serde"] }

View File

@@ -23,6 +23,8 @@ use error::Error;
use lightning_invoice::Bolt11Invoice; use lightning_invoice::Bolt11Invoice;
use sqlx::sqlite::{SqliteConnectOptions, SqlitePool, SqlitePoolOptions, SqliteRow}; use sqlx::sqlite::{SqliteConnectOptions, SqlitePool, SqlitePoolOptions, SqliteRow};
use sqlx::Row; use sqlx::Row;
use uuid::fmt::Hyphenated;
use uuid::Uuid;
pub mod error; pub mod error;
@@ -236,7 +238,7 @@ VALUES (?, ?, ?, ?, ?, ?, ?, ?);
} }
} }
async fn get_mint_quote(&self, quote_id: &str) -> Result<Option<MintQuote>, Self::Err> { async fn get_mint_quote(&self, quote_id: &Uuid) -> Result<Option<MintQuote>, Self::Err> {
let mut transaction = self.pool.begin().await.map_err(Error::from)?; let mut transaction = self.pool.begin().await.map_err(Error::from)?;
let rec = sqlx::query( let rec = sqlx::query(
r#" r#"
@@ -245,7 +247,7 @@ FROM mint_quote
WHERE id=?; WHERE id=?;
"#, "#,
) )
.bind(quote_id) .bind(quote_id.as_hyphenated())
.fetch_one(&mut transaction) .fetch_one(&mut transaction)
.await; .await;
@@ -345,7 +347,7 @@ WHERE request_lookup_id=?;
async fn update_mint_quote_state( async fn update_mint_quote_state(
&self, &self,
quote_id: &str, quote_id: &Uuid,
state: MintQuoteState, state: MintQuoteState,
) -> Result<MintQuoteState, Self::Err> { ) -> Result<MintQuoteState, Self::Err> {
let mut transaction = self.pool.begin().await.map_err(Error::from)?; let mut transaction = self.pool.begin().await.map_err(Error::from)?;
@@ -357,7 +359,7 @@ FROM mint_quote
WHERE id=?; WHERE id=?;
"#, "#,
) )
.bind(quote_id) .bind(quote_id.as_hyphenated())
.fetch_one(&mut transaction) .fetch_one(&mut transaction)
.await; .await;
let quote = match rec { let quote = match rec {
@@ -378,7 +380,7 @@ WHERE id=?;
"#, "#,
) )
.bind(state.to_string()) .bind(state.to_string())
.bind(quote_id) .bind(quote_id.as_hyphenated())
.execute(&mut transaction) .execute(&mut transaction)
.await; .await;
@@ -430,7 +432,7 @@ FROM mint_quote
} }
} }
async fn remove_mint_quote(&self, quote_id: &str) -> Result<(), Self::Err> { async fn remove_mint_quote(&self, quote_id: &Uuid) -> Result<(), Self::Err> {
let mut transaction = self.pool.begin().await.map_err(Error::from)?; let mut transaction = self.pool.begin().await.map_err(Error::from)?;
let res = sqlx::query( let res = sqlx::query(
@@ -439,7 +441,7 @@ DELETE FROM mint_quote
WHERE id=? WHERE id=?
"#, "#,
) )
.bind(quote_id) .bind(quote_id.as_hyphenated())
.execute(&mut transaction) .execute(&mut transaction)
.await; .await;
@@ -497,7 +499,7 @@ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);
} }
} }
} }
async fn get_melt_quote(&self, quote_id: &str) -> Result<Option<mint::MeltQuote>, Self::Err> { async fn get_melt_quote(&self, quote_id: &Uuid) -> Result<Option<mint::MeltQuote>, Self::Err> {
let mut transaction = self.pool.begin().await.map_err(Error::from)?; let mut transaction = self.pool.begin().await.map_err(Error::from)?;
let rec = sqlx::query( let rec = sqlx::query(
r#" r#"
@@ -506,7 +508,7 @@ FROM melt_quote
WHERE id=?; WHERE id=?;
"#, "#,
) )
.bind(quote_id) .bind(quote_id.as_hyphenated())
.fetch_one(&mut transaction) .fetch_one(&mut transaction)
.await; .await;
@@ -564,7 +566,7 @@ FROM melt_quote
async fn update_melt_quote_state( async fn update_melt_quote_state(
&self, &self,
quote_id: &str, quote_id: &Uuid,
state: MeltQuoteState, state: MeltQuoteState,
) -> Result<MeltQuoteState, Self::Err> { ) -> Result<MeltQuoteState, Self::Err> {
let mut transaction = self.pool.begin().await.map_err(Error::from)?; let mut transaction = self.pool.begin().await.map_err(Error::from)?;
@@ -576,7 +578,7 @@ FROM melt_quote
WHERE id=?; WHERE id=?;
"#, "#,
) )
.bind(quote_id) .bind(quote_id.as_hyphenated())
.fetch_one(&mut transaction) .fetch_one(&mut transaction)
.await; .await;
@@ -598,7 +600,7 @@ WHERE id=?;
"#, "#,
) )
.bind(state.to_string()) .bind(state.to_string())
.bind(quote_id) .bind(quote_id.as_hyphenated())
.execute(&mut transaction) .execute(&mut transaction)
.await; .await;
@@ -619,7 +621,7 @@ WHERE id=?;
Ok(quote.state) Ok(quote.state)
} }
async fn remove_melt_quote(&self, quote_id: &str) -> Result<(), Self::Err> { async fn remove_melt_quote(&self, quote_id: &Uuid) -> Result<(), Self::Err> {
let mut transaction = self.pool.begin().await.map_err(Error::from)?; let mut transaction = self.pool.begin().await.map_err(Error::from)?;
let res = sqlx::query( let res = sqlx::query(
r#" r#"
@@ -627,7 +629,7 @@ DELETE FROM melt_quote
WHERE id=? WHERE id=?
"#, "#,
) )
.bind(quote_id) .bind(quote_id.as_hyphenated())
.execute(&mut transaction) .execute(&mut transaction)
.await; .await;
@@ -748,7 +750,7 @@ FROM keyset;
} }
} }
async fn add_proofs(&self, proofs: Proofs, quote_id: Option<String>) -> Result<(), Self::Err> { async fn add_proofs(&self, proofs: Proofs, quote_id: Option<Uuid>) -> Result<(), Self::Err> {
let mut transaction = self.pool.begin().await.map_err(Error::from)?; let mut transaction = self.pool.begin().await.map_err(Error::from)?;
for proof in proofs { for proof in proofs {
if let Err(err) = sqlx::query( if let Err(err) = sqlx::query(
@@ -765,7 +767,7 @@ VALUES (?, ?, ?, ?, ?, ?, ?, ?);
.bind(proof.c.to_bytes().to_vec()) .bind(proof.c.to_bytes().to_vec())
.bind(proof.witness.map(|w| serde_json::to_string(&w).unwrap())) .bind(proof.witness.map(|w| serde_json::to_string(&w).unwrap()))
.bind("UNSPENT") .bind("UNSPENT")
.bind(quote_id.clone()) .bind(quote_id.map(|q| q.hyphenated()))
.execute(&mut transaction) .execute(&mut transaction)
.await .await
.map_err(Error::from) .map_err(Error::from)
@@ -812,7 +814,7 @@ VALUES (?, ?, ?, ?, ?, ?, ?, ?);
Ok(ys.iter().map(|y| proofs.remove(y)).collect()) Ok(ys.iter().map(|y| proofs.remove(y)).collect())
} }
async fn get_proof_ys_by_quote_id(&self, quote_id: &str) -> Result<Vec<PublicKey>, Self::Err> { async fn get_proof_ys_by_quote_id(&self, quote_id: &Uuid) -> Result<Vec<PublicKey>, Self::Err> {
let mut transaction = self.pool.begin().await.map_err(Error::from)?; let mut transaction = self.pool.begin().await.map_err(Error::from)?;
let rec = sqlx::query( let rec = sqlx::query(
@@ -822,7 +824,7 @@ FROM proof
WHERE quote_id=?; WHERE quote_id=?;
"#, "#,
) )
.bind(quote_id) .bind(quote_id.as_hyphenated())
.fetch_all(&mut transaction) .fetch_all(&mut transaction)
.await; .await;
@@ -996,7 +998,7 @@ WHERE keyset_id=?;
&self, &self,
blinded_messages: &[PublicKey], blinded_messages: &[PublicKey],
blinded_signatures: &[BlindSignature], blinded_signatures: &[BlindSignature],
quote_id: Option<String>, quote_id: Option<Uuid>,
) -> Result<(), Self::Err> { ) -> Result<(), Self::Err> {
let mut transaction = self.pool.begin().await.map_err(Error::from)?; let mut transaction = self.pool.begin().await.map_err(Error::from)?;
for (message, signature) in blinded_messages.iter().zip(blinded_signatures) { for (message, signature) in blinded_messages.iter().zip(blinded_signatures) {
@@ -1011,7 +1013,7 @@ VALUES (?, ?, ?, ?, ?, ?, ?);
.bind(u64::from(signature.amount) as i64) .bind(u64::from(signature.amount) as i64)
.bind(signature.keyset_id.to_string()) .bind(signature.keyset_id.to_string())
.bind(signature.c.to_bytes().to_vec()) .bind(signature.c.to_bytes().to_vec())
.bind(quote_id.clone()) .bind(quote_id.map(|q| q.hyphenated()))
.bind(signature.dleq.as_ref().map(|dleq| dleq.e.to_secret_hex())) .bind(signature.dleq.as_ref().map(|dleq| dleq.e.to_secret_hex()))
.bind(signature.dleq.as_ref().map(|dleq| dleq.s.to_secret_hex())) .bind(signature.dleq.as_ref().map(|dleq| dleq.s.to_secret_hex()))
.execute(&mut transaction) .execute(&mut transaction)
@@ -1111,7 +1113,7 @@ WHERE keyset_id=?;
async fn add_melt_request( async fn add_melt_request(
&self, &self,
melt_request: MeltBolt11Request, melt_request: MeltBolt11Request<Uuid>,
ln_key: LnKey, ln_key: LnKey,
) -> Result<(), Self::Err> { ) -> Result<(), Self::Err> {
let mut transaction = self.pool.begin().await.map_err(Error::from)?; let mut transaction = self.pool.begin().await.map_err(Error::from)?;
@@ -1149,8 +1151,8 @@ VALUES (?, ?, ?, ?, ?);
async fn get_melt_request( async fn get_melt_request(
&self, &self,
quote_id: &str, quote_id: &Uuid,
) -> Result<Option<(MeltBolt11Request, LnKey)>, Self::Err> { ) -> Result<Option<(MeltBolt11Request<Uuid>, LnKey)>, Self::Err> {
let mut transaction = self.pool.begin().await.map_err(Error::from)?; let mut transaction = self.pool.begin().await.map_err(Error::from)?;
let rec = sqlx::query( let rec = sqlx::query(
@@ -1160,7 +1162,7 @@ FROM melt_request
WHERE id=?; WHERE id=?;
"#, "#,
) )
.bind(quote_id) .bind(quote_id.as_hyphenated())
.fetch_one(&mut transaction) .fetch_one(&mut transaction)
.await; .await;
@@ -1192,7 +1194,7 @@ WHERE id=?;
/// Get [`BlindSignature`]s for quote /// Get [`BlindSignature`]s for quote
async fn get_blind_signatures_for_quote( async fn get_blind_signatures_for_quote(
&self, &self,
quote_id: &str, quote_id: &Uuid,
) -> Result<Vec<BlindSignature>, Self::Err> { ) -> Result<Vec<BlindSignature>, Self::Err> {
let mut transaction = self.pool.begin().await.map_err(Error::from)?; let mut transaction = self.pool.begin().await.map_err(Error::from)?;
@@ -1203,7 +1205,7 @@ FROM blind_signature
WHERE quote_id=?; WHERE quote_id=?;
"#, "#,
) )
.bind(quote_id) .bind(quote_id.as_hyphenated())
.fetch_all(&mut transaction) .fetch_all(&mut transaction)
.await; .await;
@@ -1254,7 +1256,7 @@ fn sqlite_row_to_keyset_info(row: SqliteRow) -> Result<MintKeySetInfo, Error> {
} }
fn sqlite_row_to_mint_quote(row: SqliteRow) -> Result<MintQuote, Error> { fn sqlite_row_to_mint_quote(row: SqliteRow) -> Result<MintQuote, Error> {
let row_id: String = row.try_get("id").map_err(Error::from)?; let row_id: Hyphenated = row.try_get("id").map_err(Error::from)?;
let row_mint_url: String = row.try_get("mint_url").map_err(Error::from)?; let row_mint_url: String = row.try_get("mint_url").map_err(Error::from)?;
let row_amount: i64 = row.try_get("amount").map_err(Error::from)?; let row_amount: i64 = row.try_get("amount").map_err(Error::from)?;
let row_unit: String = row.try_get("unit").map_err(Error::from)?; let row_unit: String = row.try_get("unit").map_err(Error::from)?;
@@ -1273,7 +1275,7 @@ fn sqlite_row_to_mint_quote(row: SqliteRow) -> Result<MintQuote, Error> {
}; };
Ok(MintQuote { Ok(MintQuote {
id: row_id, id: row_id.into_uuid(),
mint_url: MintUrl::from_str(&row_mint_url)?, mint_url: MintUrl::from_str(&row_mint_url)?,
amount: Amount::from(row_amount as u64), amount: Amount::from(row_amount as u64),
unit: CurrencyUnit::from_str(&row_unit).map_err(Error::from)?, unit: CurrencyUnit::from_str(&row_unit).map_err(Error::from)?,
@@ -1285,7 +1287,7 @@ fn sqlite_row_to_mint_quote(row: SqliteRow) -> Result<MintQuote, Error> {
} }
fn sqlite_row_to_melt_quote(row: SqliteRow) -> Result<mint::MeltQuote, Error> { fn sqlite_row_to_melt_quote(row: SqliteRow) -> Result<mint::MeltQuote, Error> {
let row_id: String = row.try_get("id").map_err(Error::from)?; let row_id: Hyphenated = row.try_get("id").map_err(Error::from)?;
let row_unit: String = row.try_get("unit").map_err(Error::from)?; let row_unit: String = row.try_get("unit").map_err(Error::from)?;
let row_amount: i64 = row.try_get("amount").map_err(Error::from)?; let row_amount: i64 = row.try_get("amount").map_err(Error::from)?;
let row_request: String = row.try_get("request").map_err(Error::from)?; let row_request: String = row.try_get("request").map_err(Error::from)?;
@@ -1299,7 +1301,7 @@ fn sqlite_row_to_melt_quote(row: SqliteRow) -> Result<mint::MeltQuote, Error> {
let request_lookup_id = row_request_lookup.unwrap_or(row_request.clone()); let request_lookup_id = row_request_lookup.unwrap_or(row_request.clone());
Ok(mint::MeltQuote { Ok(mint::MeltQuote {
id: row_id, id: row_id.into_uuid(),
amount: Amount::from(row_amount as u64), amount: Amount::from(row_amount as u64),
unit: CurrencyUnit::from_str(&row_unit).map_err(Error::from)?, unit: CurrencyUnit::from_str(&row_unit).map_err(Error::from)?,
request: row_request, request: row_request,
@@ -1375,15 +1377,15 @@ fn sqlite_row_to_blind_signature(row: SqliteRow) -> Result<BlindSignature, Error
}) })
} }
fn sqlite_row_to_melt_request(row: SqliteRow) -> Result<(MeltBolt11Request, LnKey), Error> { fn sqlite_row_to_melt_request(row: SqliteRow) -> Result<(MeltBolt11Request<Uuid>, LnKey), Error> {
let quote_id: String = row.try_get("id").map_err(Error::from)?; let quote_id: Hyphenated = row.try_get("id").map_err(Error::from)?;
let row_inputs: String = row.try_get("inputs").map_err(Error::from)?; let row_inputs: String = row.try_get("inputs").map_err(Error::from)?;
let row_outputs: Option<String> = row.try_get("outputs").map_err(Error::from)?; let row_outputs: Option<String> = row.try_get("outputs").map_err(Error::from)?;
let row_method: String = row.try_get("method").map_err(Error::from)?; let row_method: String = row.try_get("method").map_err(Error::from)?;
let row_unit: String = row.try_get("unit").map_err(Error::from)?; let row_unit: String = row.try_get("unit").map_err(Error::from)?;
let melt_request = MeltBolt11Request { let melt_request = MeltBolt11Request {
quote: quote_id, quote: quote_id.into_uuid(),
inputs: serde_json::from_str(&row_inputs)?, inputs: serde_json::from_str(&row_inputs)?,
outputs: row_outputs.and_then(|o| serde_json::from_str(&o).ok()), outputs: row_outputs.and_then(|o| serde_json::from_str(&o).ok()),
}; };

View File

@@ -42,7 +42,7 @@ thiserror = "1"
futures = { version = "0.3.28", default-features = false, optional = true, features = ["alloc"] } futures = { version = "0.3.28", default-features = false, optional = true, features = ["alloc"] }
url = "2.3" url = "2.3"
utoipa = { version = "4", optional = true } utoipa = { version = "4", optional = true }
uuid = { version = "1", features = ["v4"] } uuid = { version = "1", features = ["v4", "serde"] }
# -Z minimal-versions # -Z minimal-versions
sync_wrapper = "0.1.2" sync_wrapper = "0.1.2"

View File

@@ -5,6 +5,7 @@ use std::sync::Arc;
use async_trait::async_trait; use async_trait::async_trait;
use tokio::sync::{Mutex, RwLock}; use tokio::sync::{Mutex, RwLock};
use uuid::Uuid;
use super::{Error, MintDatabase}; use super::{Error, MintDatabase};
use crate::dhke::hash_to_curve; use crate::dhke::hash_to_curve;
@@ -19,17 +20,18 @@ use crate::types::LnKey;
/// Mint Memory Database /// Mint Memory Database
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
#[allow(clippy::type_complexity)]
pub struct MintMemoryDatabase { pub struct MintMemoryDatabase {
active_keysets: Arc<RwLock<HashMap<CurrencyUnit, Id>>>, active_keysets: Arc<RwLock<HashMap<CurrencyUnit, Id>>>,
keysets: Arc<RwLock<HashMap<Id, MintKeySetInfo>>>, keysets: Arc<RwLock<HashMap<Id, MintKeySetInfo>>>,
mint_quotes: Arc<RwLock<HashMap<String, MintQuote>>>, mint_quotes: Arc<RwLock<HashMap<Uuid, MintQuote>>>,
melt_quotes: Arc<RwLock<HashMap<String, mint::MeltQuote>>>, melt_quotes: Arc<RwLock<HashMap<Uuid, mint::MeltQuote>>>,
proofs: Arc<RwLock<HashMap<[u8; 33], Proof>>>, proofs: Arc<RwLock<HashMap<[u8; 33], Proof>>>,
proof_state: Arc<Mutex<HashMap<[u8; 33], nut07::State>>>, proof_state: Arc<Mutex<HashMap<[u8; 33], nut07::State>>>,
quote_proofs: Arc<Mutex<HashMap<String, Vec<PublicKey>>>>, quote_proofs: Arc<Mutex<HashMap<Uuid, Vec<PublicKey>>>>,
blinded_signatures: Arc<RwLock<HashMap<[u8; 33], BlindSignature>>>, blinded_signatures: Arc<RwLock<HashMap<[u8; 33], BlindSignature>>>,
quote_signatures: Arc<RwLock<HashMap<String, Vec<BlindSignature>>>>, quote_signatures: Arc<RwLock<HashMap<Uuid, Vec<BlindSignature>>>>,
melt_requests: Arc<RwLock<HashMap<String, (MeltBolt11Request, LnKey)>>>, melt_requests: Arc<RwLock<HashMap<Uuid, (MeltBolt11Request<Uuid>, LnKey)>>>,
} }
impl MintMemoryDatabase { impl MintMemoryDatabase {
@@ -42,10 +44,10 @@ impl MintMemoryDatabase {
melt_quotes: Vec<mint::MeltQuote>, melt_quotes: Vec<mint::MeltQuote>,
pending_proofs: Proofs, pending_proofs: Proofs,
spent_proofs: Proofs, spent_proofs: Proofs,
quote_proofs: HashMap<String, Vec<PublicKey>>, quote_proofs: HashMap<Uuid, Vec<PublicKey>>,
blinded_signatures: HashMap<[u8; 33], BlindSignature>, blinded_signatures: HashMap<[u8; 33], BlindSignature>,
quote_signatures: HashMap<String, Vec<BlindSignature>>, quote_signatures: HashMap<Uuid, Vec<BlindSignature>>,
melt_request: Vec<(MeltBolt11Request, LnKey)>, melt_request: Vec<(MeltBolt11Request<Uuid>, LnKey)>,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
let mut proofs = HashMap::new(); let mut proofs = HashMap::new();
let mut proof_states = HashMap::new(); let mut proof_states = HashMap::new();
@@ -64,7 +66,7 @@ impl MintMemoryDatabase {
let melt_requests = melt_request let melt_requests = melt_request
.into_iter() .into_iter()
.map(|(request, ln_key)| (request.quote.clone(), (request, ln_key))) .map(|(request, ln_key)| (request.quote, (request, ln_key)))
.collect(); .collect();
Ok(Self { Ok(Self {
@@ -73,10 +75,10 @@ impl MintMemoryDatabase {
keysets.into_iter().map(|k| (k.id, k)).collect(), keysets.into_iter().map(|k| (k.id, k)).collect(),
)), )),
mint_quotes: Arc::new(RwLock::new( mint_quotes: Arc::new(RwLock::new(
mint_quotes.into_iter().map(|q| (q.id.clone(), q)).collect(), mint_quotes.into_iter().map(|q| (q.id, q)).collect(),
)), )),
melt_quotes: Arc::new(RwLock::new( melt_quotes: Arc::new(RwLock::new(
melt_quotes.into_iter().map(|q| (q.id.clone(), q)).collect(), melt_quotes.into_iter().map(|q| (q.id, q)).collect(),
)), )),
proofs: Arc::new(RwLock::new(proofs)), proofs: Arc::new(RwLock::new(proofs)),
proof_state: Arc::new(Mutex::new(proof_states)), proof_state: Arc::new(Mutex::new(proof_states)),
@@ -119,20 +121,17 @@ impl MintDatabase for MintMemoryDatabase {
} }
async fn add_mint_quote(&self, quote: MintQuote) -> Result<(), Self::Err> { async fn add_mint_quote(&self, quote: MintQuote) -> Result<(), Self::Err> {
self.mint_quotes self.mint_quotes.write().await.insert(quote.id, quote);
.write()
.await
.insert(quote.id.clone(), quote);
Ok(()) Ok(())
} }
async fn get_mint_quote(&self, quote_id: &str) -> Result<Option<MintQuote>, Self::Err> { async fn get_mint_quote(&self, quote_id: &Uuid) -> Result<Option<MintQuote>, Self::Err> {
Ok(self.mint_quotes.read().await.get(quote_id).cloned()) Ok(self.mint_quotes.read().await.get(quote_id).cloned())
} }
async fn update_mint_quote_state( async fn update_mint_quote_state(
&self, &self,
quote_id: &str, quote_id: &Uuid,
state: MintQuoteState, state: MintQuoteState,
) -> Result<MintQuoteState, Self::Err> { ) -> Result<MintQuoteState, Self::Err> {
let mut mint_quotes = self.mint_quotes.write().await; let mut mint_quotes = self.mint_quotes.write().await;
@@ -146,7 +145,7 @@ impl MintDatabase for MintMemoryDatabase {
quote.state = state; quote.state = state;
mint_quotes.insert(quote_id.to_string(), quote.clone()); mint_quotes.insert(*quote_id, quote.clone());
Ok(current_state) Ok(current_state)
} }
@@ -186,27 +185,24 @@ impl MintDatabase for MintMemoryDatabase {
Ok(self.mint_quotes.read().await.values().cloned().collect()) Ok(self.mint_quotes.read().await.values().cloned().collect())
} }
async fn remove_mint_quote(&self, quote_id: &str) -> Result<(), Self::Err> { async fn remove_mint_quote(&self, quote_id: &Uuid) -> Result<(), Self::Err> {
self.mint_quotes.write().await.remove(quote_id); self.mint_quotes.write().await.remove(quote_id);
Ok(()) Ok(())
} }
async fn add_melt_quote(&self, quote: mint::MeltQuote) -> Result<(), Self::Err> { async fn add_melt_quote(&self, quote: mint::MeltQuote) -> Result<(), Self::Err> {
self.melt_quotes self.melt_quotes.write().await.insert(quote.id, quote);
.write()
.await
.insert(quote.id.clone(), quote);
Ok(()) Ok(())
} }
async fn get_melt_quote(&self, quote_id: &str) -> Result<Option<mint::MeltQuote>, Self::Err> { async fn get_melt_quote(&self, quote_id: &Uuid) -> Result<Option<mint::MeltQuote>, Self::Err> {
Ok(self.melt_quotes.read().await.get(quote_id).cloned()) Ok(self.melt_quotes.read().await.get(quote_id).cloned())
} }
async fn update_melt_quote_state( async fn update_melt_quote_state(
&self, &self,
quote_id: &str, quote_id: &Uuid,
state: MeltQuoteState, state: MeltQuoteState,
) -> Result<MeltQuoteState, Self::Err> { ) -> Result<MeltQuoteState, Self::Err> {
let mut melt_quotes = self.melt_quotes.write().await; let mut melt_quotes = self.melt_quotes.write().await;
@@ -220,7 +216,7 @@ impl MintDatabase for MintMemoryDatabase {
quote.state = state; quote.state = state;
melt_quotes.insert(quote_id.to_string(), quote.clone()); melt_quotes.insert(*quote_id, quote.clone());
Ok(current_state) Ok(current_state)
} }
@@ -229,7 +225,7 @@ impl MintDatabase for MintMemoryDatabase {
Ok(self.melt_quotes.read().await.values().cloned().collect()) Ok(self.melt_quotes.read().await.values().cloned().collect())
} }
async fn remove_melt_quote(&self, quote_id: &str) -> Result<(), Self::Err> { async fn remove_melt_quote(&self, quote_id: &Uuid) -> Result<(), Self::Err> {
self.melt_quotes.write().await.remove(quote_id); self.melt_quotes.write().await.remove(quote_id);
Ok(()) Ok(())
@@ -237,18 +233,18 @@ impl MintDatabase for MintMemoryDatabase {
async fn add_melt_request( async fn add_melt_request(
&self, &self,
melt_request: MeltBolt11Request, melt_request: MeltBolt11Request<Uuid>,
ln_key: LnKey, ln_key: LnKey,
) -> Result<(), Self::Err> { ) -> Result<(), Self::Err> {
let mut melt_requests = self.melt_requests.write().await; let mut melt_requests = self.melt_requests.write().await;
melt_requests.insert(melt_request.quote.clone(), (melt_request, ln_key)); melt_requests.insert(melt_request.quote, (melt_request, ln_key));
Ok(()) Ok(())
} }
async fn get_melt_request( async fn get_melt_request(
&self, &self,
quote_id: &str, quote_id: &Uuid,
) -> Result<Option<(MeltBolt11Request, LnKey)>, Self::Err> { ) -> Result<Option<(MeltBolt11Request<Uuid>, LnKey)>, Self::Err> {
let melt_requests = self.melt_requests.read().await; let melt_requests = self.melt_requests.read().await;
let melt_request = melt_requests.get(quote_id); let melt_request = melt_requests.get(quote_id);
@@ -256,7 +252,7 @@ impl MintDatabase for MintMemoryDatabase {
Ok(melt_request.cloned()) Ok(melt_request.cloned())
} }
async fn add_proofs(&self, proofs: Proofs, quote_id: Option<String>) -> Result<(), Self::Err> { async fn add_proofs(&self, proofs: Proofs, quote_id: Option<Uuid>) -> Result<(), Self::Err> {
let mut db_proofs = self.proofs.write().await; let mut db_proofs = self.proofs.write().await;
let mut ys = Vec::with_capacity(proofs.capacity()); let mut ys = Vec::with_capacity(proofs.capacity());
@@ -293,7 +289,7 @@ impl MintDatabase for MintMemoryDatabase {
Ok(proofs) Ok(proofs)
} }
async fn get_proof_ys_by_quote_id(&self, quote_id: &str) -> Result<Vec<PublicKey>, Self::Err> { async fn get_proof_ys_by_quote_id(&self, quote_id: &Uuid) -> Result<Vec<PublicKey>, Self::Err> {
let quote_proofs = &__self.quote_proofs.lock().await; let quote_proofs = &__self.quote_proofs.lock().await;
match quote_proofs.get(quote_id) { match quote_proofs.get(quote_id) {
@@ -360,7 +356,7 @@ impl MintDatabase for MintMemoryDatabase {
&self, &self,
blinded_message: &[PublicKey], blinded_message: &[PublicKey],
blind_signatures: &[BlindSignature], blind_signatures: &[BlindSignature],
quote_id: Option<String>, quote_id: Option<Uuid>,
) -> Result<(), Self::Err> { ) -> Result<(), Self::Err> {
let mut current_blinded_signatures = self.blinded_signatures.write().await; let mut current_blinded_signatures = self.blinded_signatures.write().await;
@@ -370,7 +366,7 @@ impl MintDatabase for MintMemoryDatabase {
if let Some(quote_id) = quote_id { if let Some(quote_id) = quote_id {
let mut current_quote_signatures = self.quote_signatures.write().await; let mut current_quote_signatures = self.quote_signatures.write().await;
current_quote_signatures.insert(quote_id.clone(), blind_signatures.to_vec()); current_quote_signatures.insert(quote_id, blind_signatures.to_vec());
let t = current_quote_signatures.get(&quote_id); let t = current_quote_signatures.get(&quote_id);
println!("after insert: {:?}", t); println!("after insert: {:?}", t);
} }
@@ -411,7 +407,7 @@ impl MintDatabase for MintMemoryDatabase {
/// Get [`BlindSignature`]s for quote /// Get [`BlindSignature`]s for quote
async fn get_blind_signatures_for_quote( async fn get_blind_signatures_for_quote(
&self, &self,
quote_id: &str, quote_id: &Uuid,
) -> Result<Vec<BlindSignature>, Self::Err> { ) -> Result<Vec<BlindSignature>, Self::Err> {
let ys = self.quote_signatures.read().await; let ys = self.quote_signatures.read().await;

View File

@@ -7,6 +7,8 @@ use std::fmt::Debug;
#[cfg(any(feature = "wallet", feature = "mint"))] #[cfg(any(feature = "wallet", feature = "mint"))]
use async_trait::async_trait; use async_trait::async_trait;
use thiserror::Error; use thiserror::Error;
#[cfg(feature = "mint")]
use uuid::Uuid;
#[cfg(feature = "mint")] #[cfg(feature = "mint")]
use crate::mint; use crate::mint;
@@ -187,11 +189,11 @@ pub trait MintDatabase {
/// Add [`MintMintQuote`] /// Add [`MintMintQuote`]
async fn add_mint_quote(&self, quote: MintMintQuote) -> Result<(), Self::Err>; async fn add_mint_quote(&self, quote: MintMintQuote) -> Result<(), Self::Err>;
/// Get [`MintMintQuote`] /// Get [`MintMintQuote`]
async fn get_mint_quote(&self, quote_id: &str) -> Result<Option<MintMintQuote>, Self::Err>; async fn get_mint_quote(&self, quote_id: &Uuid) -> Result<Option<MintMintQuote>, Self::Err>;
/// Update state of [`MintMintQuote`] /// Update state of [`MintMintQuote`]
async fn update_mint_quote_state( async fn update_mint_quote_state(
&self, &self,
quote_id: &str, quote_id: &Uuid,
state: MintQuoteState, state: MintQuoteState,
) -> Result<MintQuoteState, Self::Err>; ) -> Result<MintQuoteState, Self::Err>;
/// Get all [`MintMintQuote`]s /// Get all [`MintMintQuote`]s
@@ -207,34 +209,34 @@ pub trait MintDatabase {
/// Get Mint Quotes /// Get Mint Quotes
async fn get_mint_quotes(&self) -> Result<Vec<MintMintQuote>, Self::Err>; async fn get_mint_quotes(&self) -> Result<Vec<MintMintQuote>, Self::Err>;
/// Remove [`MintMintQuote`] /// Remove [`MintMintQuote`]
async fn remove_mint_quote(&self, quote_id: &str) -> Result<(), Self::Err>; async fn remove_mint_quote(&self, quote_id: &Uuid) -> Result<(), Self::Err>;
/// Add [`mint::MeltQuote`] /// Add [`mint::MeltQuote`]
async fn add_melt_quote(&self, quote: mint::MeltQuote) -> Result<(), Self::Err>; async fn add_melt_quote(&self, quote: mint::MeltQuote) -> Result<(), Self::Err>;
/// Get [`mint::MeltQuote`] /// Get [`mint::MeltQuote`]
async fn get_melt_quote(&self, quote_id: &str) -> Result<Option<mint::MeltQuote>, Self::Err>; async fn get_melt_quote(&self, quote_id: &Uuid) -> Result<Option<mint::MeltQuote>, Self::Err>;
/// Update [`mint::MeltQuote`] state /// Update [`mint::MeltQuote`] state
async fn update_melt_quote_state( async fn update_melt_quote_state(
&self, &self,
quote_id: &str, quote_id: &Uuid,
state: MeltQuoteState, state: MeltQuoteState,
) -> Result<MeltQuoteState, Self::Err>; ) -> Result<MeltQuoteState, Self::Err>;
/// Get all [`mint::MeltQuote`]s /// Get all [`mint::MeltQuote`]s
async fn get_melt_quotes(&self) -> Result<Vec<mint::MeltQuote>, Self::Err>; async fn get_melt_quotes(&self) -> Result<Vec<mint::MeltQuote>, Self::Err>;
/// Remove [`mint::MeltQuote`] /// Remove [`mint::MeltQuote`]
async fn remove_melt_quote(&self, quote_id: &str) -> Result<(), Self::Err>; async fn remove_melt_quote(&self, quote_id: &Uuid) -> Result<(), Self::Err>;
/// Add melt request /// Add melt request
async fn add_melt_request( async fn add_melt_request(
&self, &self,
melt_request: MeltBolt11Request, melt_request: MeltBolt11Request<Uuid>,
ln_key: LnKey, ln_key: LnKey,
) -> Result<(), Self::Err>; ) -> Result<(), Self::Err>;
/// Get melt request /// Get melt request
async fn get_melt_request( async fn get_melt_request(
&self, &self,
quote_id: &str, quote_id: &Uuid,
) -> Result<Option<(MeltBolt11Request, LnKey)>, Self::Err>; ) -> Result<Option<(MeltBolt11Request<Uuid>, LnKey)>, Self::Err>;
/// Add [`MintKeySetInfo`] /// Add [`MintKeySetInfo`]
async fn add_keyset_info(&self, keyset: MintKeySetInfo) -> Result<(), Self::Err>; async fn add_keyset_info(&self, keyset: MintKeySetInfo) -> Result<(), Self::Err>;
@@ -244,11 +246,11 @@ pub trait MintDatabase {
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`] /// Add spent [`Proofs`]
async fn add_proofs(&self, proof: Proofs, quote_id: Option<String>) -> Result<(), Self::Err>; async fn add_proofs(&self, proof: Proofs, quote_id: Option<Uuid>) -> Result<(), Self::Err>;
/// Get [`Proofs`] by ys /// Get [`Proofs`] by ys
async fn get_proofs_by_ys(&self, ys: &[PublicKey]) -> Result<Vec<Option<Proof>>, Self::Err>; async fn get_proofs_by_ys(&self, ys: &[PublicKey]) -> Result<Vec<Option<Proof>>, Self::Err>;
/// Get ys by quote id /// Get ys by quote id
async fn get_proof_ys_by_quote_id(&self, quote_id: &str) -> Result<Vec<PublicKey>, Self::Err>; async fn get_proof_ys_by_quote_id(&self, quote_id: &Uuid) -> Result<Vec<PublicKey>, Self::Err>;
/// Get [`Proofs`] state /// Get [`Proofs`] state
async fn get_proofs_states(&self, ys: &[PublicKey]) -> Result<Vec<Option<State>>, Self::Err>; async fn get_proofs_states(&self, ys: &[PublicKey]) -> Result<Vec<Option<State>>, Self::Err>;
/// Get [`Proofs`] state /// Get [`Proofs`] state
@@ -268,7 +270,7 @@ pub trait MintDatabase {
&self, &self,
blinded_messages: &[PublicKey], blinded_messages: &[PublicKey],
blind_signatures: &[BlindSignature], blind_signatures: &[BlindSignature],
quote_id: Option<String>, quote_id: Option<Uuid>,
) -> Result<(), Self::Err>; ) -> Result<(), Self::Err>;
/// Get [`BlindSignature`]s /// Get [`BlindSignature`]s
async fn get_blind_signatures( async fn get_blind_signatures(
@@ -283,6 +285,6 @@ pub trait MintDatabase {
/// Get [`BlindSignature`]s for quote /// Get [`BlindSignature`]s for quote
async fn get_blind_signatures_for_quote( async fn get_blind_signatures_for_quote(
&self, &self,
quote_id: &str, quote_id: &Uuid,
) -> Result<Vec<BlindSignature>, Self::Err>; ) -> Result<Vec<BlindSignature>, Self::Err>;
} }

View File

@@ -4,6 +4,7 @@ use std::str::FromStr;
use anyhow::bail; use anyhow::bail;
use lightning_invoice::Bolt11Invoice; use lightning_invoice::Bolt11Invoice;
use tracing::instrument; use tracing::instrument;
use uuid::Uuid;
use super::{ use super::{
CurrencyUnit, MeltBolt11Request, MeltQuote, MeltQuoteBolt11Request, MeltQuoteBolt11Response, CurrencyUnit, MeltBolt11Request, MeltQuote, MeltQuoteBolt11Request, MeltQuoteBolt11Response,
@@ -53,7 +54,7 @@ impl Mint {
pub async fn get_melt_bolt11_quote( pub async fn get_melt_bolt11_quote(
&self, &self,
melt_request: &MeltQuoteBolt11Request, melt_request: &MeltQuoteBolt11Request,
) -> Result<MeltQuoteBolt11Response, Error> { ) -> Result<MeltQuoteBolt11Response<Uuid>, Error> {
let MeltQuoteBolt11Request { let MeltQuoteBolt11Request {
request, request,
unit, unit,
@@ -117,7 +118,10 @@ impl Mint {
/// Check melt quote status /// Check melt quote status
#[instrument(skip(self))] #[instrument(skip(self))]
pub async fn check_melt_quote(&self, quote_id: &str) -> Result<MeltQuoteBolt11Response, Error> { pub async fn check_melt_quote(
&self,
quote_id: &Uuid,
) -> Result<MeltQuoteBolt11Response<Uuid>, Error> {
let quote = self let quote = self
.localstore .localstore
.get_melt_quote(quote_id) .get_melt_quote(quote_id)
@@ -159,7 +163,7 @@ impl Mint {
/// Remove melt quote /// Remove melt quote
#[instrument(skip(self))] #[instrument(skip(self))]
pub async fn remove_melt_quote(&self, quote_id: &str) -> Result<(), Error> { pub async fn remove_melt_quote(&self, quote_id: &Uuid) -> Result<(), Error> {
self.localstore.remove_melt_quote(quote_id).await?; self.localstore.remove_melt_quote(quote_id).await?;
Ok(()) Ok(())
@@ -170,7 +174,7 @@ impl Mint {
pub async fn check_melt_expected_ln_fees( pub async fn check_melt_expected_ln_fees(
&self, &self,
melt_quote: &MeltQuote, melt_quote: &MeltQuote,
melt_request: &MeltBolt11Request, melt_request: &MeltBolt11Request<Uuid>,
) -> Result<Option<Amount>, Error> { ) -> Result<Option<Amount>, Error> {
let invoice = Bolt11Invoice::from_str(&melt_quote.request)?; let invoice = Bolt11Invoice::from_str(&melt_quote.request)?;
@@ -226,7 +230,7 @@ impl Mint {
#[instrument(skip_all)] #[instrument(skip_all)]
pub async fn verify_melt_request( pub async fn verify_melt_request(
&self, &self,
melt_request: &MeltBolt11Request, melt_request: &MeltBolt11Request<Uuid>,
) -> Result<MeltQuote, Error> { ) -> Result<MeltQuote, Error> {
let state = self let state = self
.localstore .localstore
@@ -248,10 +252,7 @@ impl Mint {
} }
self.localstore self.localstore
.add_proofs( .add_proofs(melt_request.inputs.clone(), Some(melt_request.quote))
melt_request.inputs.clone(),
Some(melt_request.quote.clone()),
)
.await?; .await?;
self.check_ys_spendable(&ys, State::Pending).await?; self.check_ys_spendable(&ys, State::Pending).await?;
@@ -345,7 +346,10 @@ impl Mint {
/// made The [`Proofs`] should be returned to an unspent state and the /// made The [`Proofs`] should be returned to an unspent state and the
/// quote should be unpaid /// quote should be unpaid
#[instrument(skip_all)] #[instrument(skip_all)]
pub async fn process_unpaid_melt(&self, melt_request: &MeltBolt11Request) -> Result<(), Error> { pub async fn process_unpaid_melt(
&self,
melt_request: &MeltBolt11Request<Uuid>,
) -> Result<(), Error> {
let input_ys = melt_request.inputs.ys()?; let input_ys = melt_request.inputs.ys()?;
self.localstore self.localstore
@@ -373,8 +377,8 @@ impl Mint {
#[instrument(skip_all)] #[instrument(skip_all)]
pub async fn melt_bolt11( pub async fn melt_bolt11(
&self, &self,
melt_request: &MeltBolt11Request, melt_request: &MeltBolt11Request<Uuid>,
) -> Result<MeltQuoteBolt11Response, Error> { ) -> Result<MeltQuoteBolt11Response<Uuid>, Error> {
use std::sync::Arc; use std::sync::Arc;
async fn check_payment_state( async fn check_payment_state(
ln: Arc<dyn MintLightning<Err = cdk_lightning::Error> + Send + Sync>, ln: Arc<dyn MintLightning<Err = cdk_lightning::Error> + Send + Sync>,
@@ -584,10 +588,10 @@ impl Mint {
#[instrument(skip_all)] #[instrument(skip_all)]
pub async fn process_melt_request( pub async fn process_melt_request(
&self, &self,
melt_request: &MeltBolt11Request, melt_request: &MeltBolt11Request<Uuid>,
payment_preimage: Option<String>, payment_preimage: Option<String>,
total_spent: Amount, total_spent: Amount,
) -> Result<MeltQuoteBolt11Response, Error> { ) -> Result<MeltQuoteBolt11Response<Uuid>, Error> {
tracing::debug!("Processing melt quote: {}", melt_request.quote); tracing::debug!("Processing melt quote: {}", melt_request.quote);
let quote = self let quote = self
@@ -673,7 +677,7 @@ impl Mint {
.map(|o| o.blinded_secret) .map(|o| o.blinded_secret)
.collect::<Vec<PublicKey>>(), .collect::<Vec<PublicKey>>(),
&change_sigs, &change_sigs,
Some(quote.id.clone()), Some(quote.id),
) )
.await?; .await?;

View File

@@ -1,4 +1,5 @@
use tracing::instrument; use tracing::instrument;
use uuid::Uuid;
use super::{ use super::{
nut04, CurrencyUnit, Mint, MintQuote, MintQuoteBolt11Request, MintQuoteBolt11Response, nut04, CurrencyUnit, Mint, MintQuote, MintQuoteBolt11Request, MintQuoteBolt11Response,
@@ -59,7 +60,7 @@ impl Mint {
pub async fn get_mint_bolt11_quote( pub async fn get_mint_bolt11_quote(
&self, &self,
mint_quote_request: MintQuoteBolt11Request, mint_quote_request: MintQuoteBolt11Request,
) -> Result<MintQuoteBolt11Response, Error> { ) -> Result<MintQuoteBolt11Response<Uuid>, Error> {
let MintQuoteBolt11Request { let MintQuoteBolt11Request {
amount, amount,
unit, unit,
@@ -116,7 +117,7 @@ impl Mint {
self.localstore.add_mint_quote(quote.clone()).await?; self.localstore.add_mint_quote(quote.clone()).await?;
let quote: MintQuoteBolt11Response = quote.into(); let quote: MintQuoteBolt11Response<Uuid> = quote.into();
self.pubsub_manager self.pubsub_manager
.broadcast(NotificationPayload::MintQuoteBolt11Response(quote.clone())); .broadcast(NotificationPayload::MintQuoteBolt11Response(quote.clone()));
@@ -126,7 +127,10 @@ impl Mint {
/// Check mint quote /// Check mint quote
#[instrument(skip(self))] #[instrument(skip(self))]
pub async fn check_mint_quote(&self, quote_id: &str) -> Result<MintQuoteBolt11Response, Error> { pub async fn check_mint_quote(
&self,
quote_id: &Uuid,
) -> Result<MintQuoteBolt11Response<Uuid>, Error> {
let quote = self let quote = self
.localstore .localstore
.get_mint_quote(quote_id) .get_mint_quote(quote_id)
@@ -187,7 +191,7 @@ impl Mint {
/// Remove mint quote /// Remove mint quote
#[instrument(skip_all)] #[instrument(skip_all)]
pub async fn remove_mint_quote(&self, quote_id: &str) -> Result<(), Error> { pub async fn remove_mint_quote(&self, quote_id: &Uuid) -> Result<(), Error> {
self.localstore.remove_mint_quote(quote_id).await?; self.localstore.remove_mint_quote(quote_id).await?;
Ok(()) Ok(())
@@ -250,7 +254,7 @@ impl Mint {
#[instrument(skip_all)] #[instrument(skip_all)]
pub async fn process_mint_request( pub async fn process_mint_request(
&self, &self,
mint_request: nut04::MintBolt11Request, mint_request: nut04::MintBolt11Request<Uuid>,
) -> Result<nut04::MintBolt11Response, Error> { ) -> Result<nut04::MintBolt11Response, Error> {
let mint_quote = let mint_quote =
if let Some(mint_quote) = self.localstore.get_mint_quote(&mint_request.quote).await? { if let Some(mint_quote) = self.localstore.get_mint_quote(&mint_request.quote).await? {
@@ -321,7 +325,7 @@ impl Mint {
.map(|p| p.blinded_secret) .map(|p| p.blinded_secret)
.collect::<Vec<PublicKey>>(), .collect::<Vec<PublicKey>>(),
&blind_signatures, &blind_signatures,
Some(mint_request.quote.clone()), Some(mint_request.quote),
) )
.await?; .await?;

View File

@@ -11,6 +11,7 @@ use serde::{Deserialize, Serialize};
use tokio::sync::{Notify, RwLock}; use tokio::sync::{Notify, RwLock};
use tokio::task::JoinSet; use tokio::task::JoinSet;
use tracing::instrument; use tracing::instrument;
use uuid::Uuid;
use crate::cdk_database::{self, MintDatabase}; use crate::cdk_database::{self, MintDatabase};
use crate::cdk_lightning::{self, MintLightning}; use crate::cdk_lightning::{self, MintLightning};
@@ -380,7 +381,7 @@ impl Mint {
pub async fn handle_internal_melt_mint( pub async fn handle_internal_melt_mint(
&self, &self,
melt_quote: &MeltQuote, melt_quote: &MeltQuote,
melt_request: &MeltBolt11Request, melt_request: &MeltBolt11Request<Uuid>,
) -> Result<Option<Amount>, Error> { ) -> Result<Option<Amount>, Error> {
let mint_quote = match self let mint_quote = match self
.localstore .localstore
@@ -608,6 +609,7 @@ mod tests {
use bitcoin::Network; use bitcoin::Network;
use secp256k1::Secp256k1; use secp256k1::Secp256k1;
use uuid::Uuid;
use super::*; use super::*;
use crate::types::LnKey; use crate::types::LnKey;
@@ -709,13 +711,13 @@ mod tests {
pending_proofs: Proofs, pending_proofs: Proofs,
spent_proofs: Proofs, spent_proofs: Proofs,
blinded_signatures: HashMap<[u8; 33], BlindSignature>, blinded_signatures: HashMap<[u8; 33], BlindSignature>,
quote_proofs: HashMap<String, Vec<PublicKey>>, quote_proofs: HashMap<Uuid, Vec<PublicKey>>,
quote_signatures: HashMap<String, Vec<BlindSignature>>, quote_signatures: HashMap<Uuid, Vec<BlindSignature>>,
mint_url: &'a str, mint_url: &'a str,
seed: &'a [u8], seed: &'a [u8],
mint_info: MintInfo, mint_info: MintInfo,
supported_units: HashMap<CurrencyUnit, (u64, u8)>, supported_units: HashMap<CurrencyUnit, (u64, u8)>,
melt_requests: Vec<(MeltBolt11Request, LnKey)>, melt_requests: Vec<(MeltBolt11Request<Uuid>, LnKey)>,
quote_ttl: QuoteTTL, quote_ttl: QuoteTTL,
} }

View File

@@ -12,7 +12,7 @@ use crate::Amount;
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub struct MintQuote { pub struct MintQuote {
/// Quote id /// Quote id
pub id: String, pub id: Uuid,
/// Mint Url /// Mint Url
pub mint_url: MintUrl, pub mint_url: MintUrl,
/// Amount of quote /// Amount of quote
@@ -43,7 +43,7 @@ impl MintQuote {
Self { Self {
mint_url, mint_url,
id: id.to_string(), id,
amount, amount,
unit, unit,
request, request,
@@ -58,7 +58,7 @@ impl MintQuote {
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub struct MeltQuote { pub struct MeltQuote {
/// Quote id /// Quote id
pub id: String, pub id: Uuid,
/// Quote unit /// Quote unit
pub unit: CurrencyUnit, pub unit: CurrencyUnit,
/// Quote amount /// Quote amount
@@ -90,7 +90,7 @@ impl MeltQuote {
let id = Uuid::new_v4(); let id = Uuid::new_v4();
Self { Self {
id: id.to_string(), id,
amount, amount,
unit, unit,
request, request,

View File

@@ -5,8 +5,11 @@
use std::fmt; use std::fmt;
use std::str::FromStr; use std::str::FromStr;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use thiserror::Error; use thiserror::Error;
#[cfg(feature = "mint")]
use uuid::Uuid;
use super::nut00::{BlindSignature, BlindedMessage, CurrencyUnit, PaymentMethod}; use super::nut00::{BlindSignature, BlindedMessage, CurrencyUnit, PaymentMethod};
use super::MintQuoteState; use super::MintQuoteState;
@@ -81,9 +84,10 @@ impl FromStr for QuoteState {
/// Mint quote response [NUT-04] /// Mint quote response [NUT-04]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] #[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
pub struct MintQuoteBolt11Response { #[serde(bound = "Q: Serialize + DeserializeOwned")]
pub struct MintQuoteBolt11Response<Q> {
/// Quote Id /// Quote Id
pub quote: String, pub quote: Q,
/// Payment request to fulfil /// Payment request to fulfil
pub request: String, pub request: String,
/// Quote State /// Quote State
@@ -93,8 +97,8 @@ pub struct MintQuoteBolt11Response {
} }
#[cfg(feature = "mint")] #[cfg(feature = "mint")]
impl From<crate::mint::MintQuote> for MintQuoteBolt11Response { impl From<crate::mint::MintQuote> for MintQuoteBolt11Response<Uuid> {
fn from(mint_quote: crate::mint::MintQuote) -> MintQuoteBolt11Response { fn from(mint_quote: crate::mint::MintQuote) -> MintQuoteBolt11Response<Uuid> {
MintQuoteBolt11Response { MintQuoteBolt11Response {
quote: mint_quote.id, quote: mint_quote.id,
request: mint_quote.request, request: mint_quote.request,
@@ -107,16 +111,17 @@ impl From<crate::mint::MintQuote> for MintQuoteBolt11Response {
/// Mint request [NUT-04] /// Mint request [NUT-04]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] #[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
pub struct MintBolt11Request { #[serde(bound = "Q: Serialize + DeserializeOwned")]
pub struct MintBolt11Request<Q> {
/// Quote id /// Quote id
#[cfg_attr(feature = "swagger", schema(max_length = 1_000))] #[cfg_attr(feature = "swagger", schema(max_length = 1_000))]
pub quote: String, pub quote: Q,
/// Outputs /// Outputs
#[cfg_attr(feature = "swagger", schema(max_items = 1_000))] #[cfg_attr(feature = "swagger", schema(max_items = 1_000))]
pub outputs: Vec<BlindedMessage>, pub outputs: Vec<BlindedMessage>,
} }
impl MintBolt11Request { impl<Q> MintBolt11Request<Q> {
/// Total [`Amount`] of outputs /// Total [`Amount`] of outputs
pub fn total_amount(&self) -> Result<Amount, Error> { pub fn total_amount(&self) -> Result<Amount, Error> {
Amount::try_sum( Amount::try_sum(

View File

@@ -5,9 +5,12 @@
use std::fmt; use std::fmt;
use std::str::FromStr; use std::str::FromStr;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Deserializer, Serialize}; use serde::{Deserialize, Deserializer, Serialize};
use serde_json::Value; use serde_json::Value;
use thiserror::Error; use thiserror::Error;
#[cfg(feature = "mint")]
use uuid::Uuid;
use super::nut00::{BlindSignature, BlindedMessage, CurrencyUnit, PaymentMethod, Proofs}; use super::nut00::{BlindSignature, BlindedMessage, CurrencyUnit, PaymentMethod, Proofs};
use super::nut15::Mpp; use super::nut15::Mpp;
@@ -88,9 +91,10 @@ impl FromStr for QuoteState {
/// Melt quote response [NUT-05] /// Melt quote response [NUT-05]
#[derive(Debug, Clone, PartialEq, Eq, Serialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] #[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
pub struct MeltQuoteBolt11Response { #[serde(bound = "Q: Serialize")]
pub struct MeltQuoteBolt11Response<Q> {
/// Quote Id /// Quote Id
pub quote: String, pub quote: Q,
/// The amount that needs to be provided /// The amount that needs to be provided
pub amount: Amount, pub amount: Amount,
/// The fee reserve that is required /// The fee reserve that is required
@@ -112,10 +116,10 @@ pub struct MeltQuoteBolt11Response {
} }
#[cfg(feature = "mint")] #[cfg(feature = "mint")]
impl From<&MeltQuote> for MeltQuoteBolt11Response { impl From<&MeltQuote> for MeltQuoteBolt11Response<Uuid> {
fn from(melt_quote: &MeltQuote) -> MeltQuoteBolt11Response { fn from(melt_quote: &MeltQuote) -> MeltQuoteBolt11Response<Uuid> {
MeltQuoteBolt11Response { MeltQuoteBolt11Response {
quote: melt_quote.id.clone(), quote: melt_quote.id,
payment_preimage: None, payment_preimage: None,
change: None, change: None,
state: melt_quote.state, state: melt_quote.state,
@@ -129,14 +133,14 @@ impl From<&MeltQuote> for MeltQuoteBolt11Response {
// A custom deserializer is needed until all mints // A custom deserializer is needed until all mints
// update some will return without the required state. // update some will return without the required state.
impl<'de> Deserialize<'de> for MeltQuoteBolt11Response { impl<'de, Q: DeserializeOwned> Deserialize<'de> for MeltQuoteBolt11Response<Q> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where where
D: Deserializer<'de>, D: Deserializer<'de>,
{ {
let value = Value::deserialize(deserializer)?; let value = Value::deserialize(deserializer)?;
let quote: String = serde_json::from_value( let quote: Q = serde_json::from_value(
value value
.get("quote") .get("quote")
.ok_or(serde::de::Error::missing_field("quote"))? .ok_or(serde::de::Error::missing_field("quote"))?
@@ -212,8 +216,8 @@ impl<'de> Deserialize<'de> for MeltQuoteBolt11Response {
} }
#[cfg(feature = "mint")] #[cfg(feature = "mint")]
impl From<mint::MeltQuote> for MeltQuoteBolt11Response { impl From<mint::MeltQuote> for MeltQuoteBolt11Response<Uuid> {
fn from(melt_quote: mint::MeltQuote) -> MeltQuoteBolt11Response { fn from(melt_quote: mint::MeltQuote) -> MeltQuoteBolt11Response<Uuid> {
let paid = melt_quote.state == QuoteState::Paid; let paid = melt_quote.state == QuoteState::Paid;
MeltQuoteBolt11Response { MeltQuoteBolt11Response {
quote: melt_quote.id, quote: melt_quote.id,
@@ -231,9 +235,10 @@ impl From<mint::MeltQuote> for MeltQuoteBolt11Response {
/// Melt Bolt11 Request [NUT-05] /// Melt Bolt11 Request [NUT-05]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] #[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
pub struct MeltBolt11Request { #[serde(bound = "Q: Serialize + DeserializeOwned")]
pub struct MeltBolt11Request<Q> {
/// Quote ID /// Quote ID
pub quote: String, pub quote: Q,
/// Proofs /// Proofs
#[cfg_attr(feature = "swagger", schema(value_type = Vec<Proof>))] #[cfg_attr(feature = "swagger", schema(value_type = Vec<Proof>))]
pub inputs: Proofs, pub inputs: Proofs,
@@ -242,7 +247,7 @@ pub struct MeltBolt11Request {
pub outputs: Option<Vec<BlindedMessage>>, pub outputs: Option<Vec<BlindedMessage>>,
} }
impl MeltBolt11Request { impl<Q: Serialize + DeserializeOwned> MeltBolt11Request<Q> {
/// Total [`Amount`] of [`Proofs`] /// Total [`Amount`] of [`Proofs`]
pub fn proofs_amount(&self) -> Result<Amount, Error> { pub fn proofs_amount(&self) -> Result<Amount, Error> {
Amount::try_sum(self.inputs.iter().map(|proof| proof.amount)) Amount::try_sum(self.inputs.iter().map(|proof| proof.amount))

View File

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

View File

@@ -1,6 +1,7 @@
//! Specific Subscription for the cdk crate //! Specific Subscription for the cdk crate
use std::ops::Deref; use std::ops::Deref;
use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@@ -8,7 +9,9 @@ use serde::{Deserialize, Serialize};
mod on_subscription; mod on_subscription;
pub use on_subscription::OnSubscription; pub use on_subscription::OnSubscription;
use uuid::Uuid;
use super::PublicKey;
use crate::cdk_database::{self, MintDatabase}; use crate::cdk_database::{self, MintDatabase};
use crate::nuts::{ use crate::nuts::{
BlindSignature, CurrencyUnit, MeltQuoteBolt11Response, MeltQuoteState, MintQuoteBolt11Response, BlindSignature, CurrencyUnit, MeltQuoteBolt11Response, MeltQuoteState, MintQuoteBolt11Response,
@@ -91,9 +94,9 @@ pub enum NotificationPayload {
/// Proof State /// Proof State
ProofState(ProofState), ProofState(ProofState),
/// Melt Quote Bolt11 Response /// Melt Quote Bolt11 Response
MeltQuoteBolt11Response(MeltQuoteBolt11Response), MeltQuoteBolt11Response(MeltQuoteBolt11Response<Uuid>),
/// Mint Quote Bolt11 Response /// Mint Quote Bolt11 Response
MintQuoteBolt11Response(MintQuoteBolt11Response), MintQuoteBolt11Response(MintQuoteBolt11Response<Uuid>),
} }
impl From<ProofState> for NotificationPayload { impl From<ProofState> for NotificationPayload {
@@ -102,37 +105,42 @@ impl From<ProofState> for NotificationPayload {
} }
} }
impl From<MeltQuoteBolt11Response> for NotificationPayload { impl From<MeltQuoteBolt11Response<Uuid>> for NotificationPayload {
fn from(melt_quote: MeltQuoteBolt11Response) -> NotificationPayload { fn from(melt_quote: MeltQuoteBolt11Response<Uuid>) -> NotificationPayload {
NotificationPayload::MeltQuoteBolt11Response(melt_quote) NotificationPayload::MeltQuoteBolt11Response(melt_quote)
} }
} }
impl From<MintQuoteBolt11Response> for NotificationPayload { impl From<MintQuoteBolt11Response<Uuid>> for NotificationPayload {
fn from(mint_quote: MintQuoteBolt11Response) -> NotificationPayload { fn from(mint_quote: MintQuoteBolt11Response<Uuid>) -> NotificationPayload {
NotificationPayload::MintQuoteBolt11Response(mint_quote) NotificationPayload::MintQuoteBolt11Response(mint_quote)
} }
} }
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
/// A parsed notification
pub enum Notification {
/// ProofState id is a Pubkey
ProofState(PublicKey),
/// MeltQuote id is an Uuid
MeltQuoteBolt11(Uuid),
/// MintQuote id is an Uuid
MintQuoteBolt11(Uuid),
}
impl Indexable for NotificationPayload { impl Indexable for NotificationPayload {
type Type = (String, Kind); type Type = Notification;
fn to_indexes(&self) -> Vec<Index<Self::Type>> { fn to_indexes(&self) -> Vec<Index<Self::Type>> {
match self { match self {
NotificationPayload::ProofState(proof_state) => { NotificationPayload::ProofState(proof_state) => {
vec![Index::from((proof_state.y.to_hex(), Kind::ProofState))] vec![Index::from(Notification::ProofState(proof_state.y))]
} }
NotificationPayload::MeltQuoteBolt11Response(melt_quote) => { NotificationPayload::MeltQuoteBolt11Response(melt_quote) => {
vec![Index::from(( vec![Index::from(Notification::MeltQuoteBolt11(melt_quote.quote))]
melt_quote.quote.clone(),
Kind::Bolt11MeltQuote,
))]
} }
NotificationPayload::MintQuoteBolt11Response(mint_quote) => { NotificationPayload::MintQuoteBolt11Response(mint_quote) => {
vec![Index::from(( vec![Index::from(Notification::MintQuoteBolt11(mint_quote.quote))]
mint_quote.quote.clone(),
Kind::Bolt11MintQuote,
))]
} }
} }
} }
@@ -157,13 +165,27 @@ impl AsRef<SubId> for Params {
} }
} }
impl From<Params> for Vec<Index<(String, Kind)>> { impl From<Params> for Vec<Index<Notification>> {
fn from(val: Params) -> Self { fn from(val: Params) -> Self {
let sub_id: SubscriptionGlobalId = Default::default(); let sub_id: SubscriptionGlobalId = Default::default();
val.filters val.filters
.iter() .into_iter()
.map(|filter| Index::from(((filter.clone(), val.kind), val.id.clone(), sub_id))) .map(|filter| {
.collect() let idx = match val.kind {
Kind::Bolt11MeltQuote => {
Notification::MeltQuoteBolt11(Uuid::from_str(&filter)?)
}
Kind::Bolt11MintQuote => {
Notification::MintQuoteBolt11(Uuid::from_str(&filter)?)
}
Kind::ProofState => Notification::ProofState(PublicKey::from_str(&filter)?),
};
Ok(Index::from((idx, val.id.clone(), sub_id)))
})
.collect::<Result<_, anyhow::Error>>()
.unwrap()
// TODO don't unwrap, move to try from
} }
} }
@@ -172,7 +194,7 @@ impl From<Params> for Vec<Index<(String, Kind)>> {
/// ///
/// Nut-17 implementation is system-wide and not only through the WebSocket, so /// Nut-17 implementation is system-wide and not only through the WebSocket, so
/// it is possible for another part of the system to subscribe to events. /// it is possible for another part of the system to subscribe to events.
pub struct PubSubManager(pub_sub::Manager<NotificationPayload, (String, Kind), OnSubscription>); pub struct PubSubManager(pub_sub::Manager<NotificationPayload, Notification, OnSubscription>);
#[allow(clippy::default_constructed_unit_structs)] #[allow(clippy::default_constructed_unit_structs)]
impl Default for PubSubManager { impl Default for PubSubManager {
@@ -188,7 +210,7 @@ impl From<Arc<dyn MintDatabase<Err = cdk_database::Error> + Send + Sync>> for Pu
} }
impl Deref for PubSubManager { impl Deref for PubSubManager {
type Target = pub_sub::Manager<NotificationPayload, (String, Kind), OnSubscription>; type Target = pub_sub::Manager<NotificationPayload, Notification, OnSubscription>;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.0 &self.0
@@ -202,7 +224,7 @@ impl PubSubManager {
} }
/// Helper function to emit a MintQuoteBolt11Response status /// Helper function to emit a MintQuoteBolt11Response status
pub fn mint_quote_bolt11_status<E: Into<MintQuoteBolt11Response>>( pub fn mint_quote_bolt11_status<E: Into<MintQuoteBolt11Response<Uuid>>>(
&self, &self,
quote: E, quote: E,
new_state: MintQuoteState, new_state: MintQuoteState,
@@ -214,7 +236,7 @@ impl PubSubManager {
} }
/// Helper function to emit a MeltQuoteBolt11Response status /// Helper function to emit a MeltQuoteBolt11Response status
pub fn melt_quote_status<E: Into<MeltQuoteBolt11Response>>( pub fn melt_quote_status<E: Into<MeltQuoteBolt11Response<Uuid>>>(
&self, &self,
quote: E, quote: E,
payment_preimage: Option<String>, payment_preimage: Option<String>,
@@ -244,7 +266,11 @@ mod test {
let manager = PubSubManager::default(); let manager = PubSubManager::default();
let params = Params { let params = Params {
kind: Kind::ProofState, kind: Kind::ProofState,
filters: vec!["x".to_string()], filters: vec![PublicKey::from_hex(
"02194603ffa36356f4a56b7df9371fc3192472351453ec7398b8da8117e7c3e104",
)
.unwrap()
.to_string()],
id: "uno".into(), id: "uno".into(),
}; };

View File

@@ -1,10 +1,11 @@
//! On Subscription //! On Subscription
//! //!
//! This module contains the code that is triggered when a new subscription is created. //! This module contains the code that is triggered when a new subscription is created.
use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use super::{Kind, NotificationPayload}; use uuid::Uuid;
use super::{Notification, NotificationPayload};
use crate::cdk_database::{self, MintDatabase}; use crate::cdk_database::{self, MintDatabase};
use crate::nuts::{MeltQuoteBolt11Response, MintQuoteBolt11Response, ProofState, PublicKey}; use crate::nuts::{MeltQuoteBolt11Response, MintQuoteBolt11Response, ProofState, PublicKey};
use crate::pub_sub::OnNewSubscription; use crate::pub_sub::OnNewSubscription;
@@ -22,7 +23,7 @@ pub struct OnSubscription(
#[async_trait::async_trait] #[async_trait::async_trait]
impl OnNewSubscription for OnSubscription { impl OnNewSubscription for OnSubscription {
type Event = NotificationPayload; type Event = NotificationPayload;
type Index = (String, Kind); type Index = Notification;
async fn on_new_subscription( async fn on_new_subscription(
&self, &self,
@@ -35,76 +36,58 @@ impl OnNewSubscription for OnSubscription {
}; };
let mut to_return = vec![]; let mut to_return = vec![];
let mut public_keys: Vec<PublicKey> = Vec::new();
let mut melt_queries = Vec::new();
let mut mint_queries = Vec::new();
for (kind, values) in request.iter().fold( for idx in request.iter() {
HashMap::new(), match idx {
|mut acc: HashMap<&Kind, Vec<&String>>, (data, kind)| { Notification::ProofState(pk) => public_keys.push(*pk),
acc.entry(kind).or_default().push(data); Notification::MeltQuoteBolt11(uuid) => {
acc melt_queries.push(datastore.get_melt_quote(uuid))
},
) {
match kind {
Kind::Bolt11MeltQuote => {
let queries = values
.iter()
.map(|id| datastore.get_melt_quote(id))
.collect::<Vec<_>>();
to_return.extend(
futures::future::try_join_all(queries)
.await
.map(|quotes| {
quotes
.into_iter()
.filter_map(|quote| quote.map(|x| x.into()))
.map(|x: MeltQuoteBolt11Response| x.into())
.collect::<Vec<_>>()
})
.map_err(|e| e.to_string())?,
);
} }
Kind::Bolt11MintQuote => { Notification::MintQuoteBolt11(uuid) => {
let queries = values mint_queries.push(datastore.get_mint_quote(uuid))
.iter()
.map(|id| datastore.get_mint_quote(id))
.collect::<Vec<_>>();
to_return.extend(
futures::future::try_join_all(queries)
.await
.map(|quotes| {
quotes
.into_iter()
.filter_map(|quote| quote.map(|x| x.into()))
.map(|x: MintQuoteBolt11Response| x.into())
.collect::<Vec<_>>()
})
.map_err(|e| e.to_string())?,
);
}
Kind::ProofState => {
let public_keys = values
.iter()
.map(PublicKey::from_hex)
.collect::<Result<Vec<PublicKey>, _>>()
.map_err(|e| e.to_string())?;
to_return.extend(
datastore
.get_proofs_states(&public_keys)
.await
.map_err(|e| e.to_string())?
.into_iter()
.enumerate()
.filter_map(|(idx, state)| {
state.map(|state| (public_keys[idx], state).into())
})
.map(|state: ProofState| state.into()),
);
} }
} }
} }
to_return.extend(
futures::future::try_join_all(melt_queries)
.await
.map(|quotes| {
quotes
.into_iter()
.filter_map(|quote| quote.map(|x| x.into()))
.map(|x: MeltQuoteBolt11Response<Uuid>| x.into())
.collect::<Vec<_>>()
})
.map_err(|e| e.to_string())?,
);
to_return.extend(
futures::future::try_join_all(mint_queries)
.await
.map(|quotes| {
quotes
.into_iter()
.filter_map(|quote| quote.map(|x| x.into()))
.map(|x: MintQuoteBolt11Response<Uuid>| x.into())
.collect::<Vec<_>>()
})
.map_err(|e| e.to_string())?,
);
to_return.extend(
datastore
.get_proofs_states(public_keys.as_slice())
.await
.map_err(|e| e.to_string())?
.into_iter()
.enumerate()
.filter_map(|(idx, state)| state.map(|state| (public_keys[idx], state).into()))
.map(|state: ProofState| state.into()),
);
Ok(to_return) Ok(to_return)
} }
} }

View File

@@ -120,7 +120,7 @@ impl HttpClientMethods for HttpClient {
&self, &self,
mint_url: MintUrl, mint_url: MintUrl,
request: MintQuoteBolt11Request, request: MintQuoteBolt11Request,
) -> Result<MintQuoteBolt11Response, Error> { ) -> Result<MintQuoteBolt11Response<String>, Error> {
let url = mint_url.join_paths(&["v1", "mint", "quote", "bolt11"])?; let url = mint_url.join_paths(&["v1", "mint", "quote", "bolt11"])?;
let res = self let res = self
@@ -132,7 +132,7 @@ impl HttpClientMethods for HttpClient {
.text() .text()
.await?; .await?;
convert_http_response!(MintQuoteBolt11Response, res) convert_http_response!(MintQuoteBolt11Response<String>, res)
} }
/// Mint Quote status /// Mint Quote status
@@ -141,12 +141,12 @@ impl HttpClientMethods for HttpClient {
&self, &self,
mint_url: MintUrl, mint_url: MintUrl,
quote_id: &str, quote_id: &str,
) -> Result<MintQuoteBolt11Response, Error> { ) -> Result<MintQuoteBolt11Response<String>, Error> {
let url = mint_url.join_paths(&["v1", "mint", "quote", "bolt11", quote_id])?; let url = mint_url.join_paths(&["v1", "mint", "quote", "bolt11", quote_id])?;
let res = self.inner.get(url).send().await?.text().await?; let res = self.inner.get(url).send().await?.text().await?;
convert_http_response!(MintQuoteBolt11Response, res) convert_http_response!(MintQuoteBolt11Response<String>, res)
} }
/// Mint Tokens [NUT-04] /// Mint Tokens [NUT-04]
@@ -154,7 +154,7 @@ impl HttpClientMethods for HttpClient {
async fn post_mint( async fn post_mint(
&self, &self,
mint_url: MintUrl, mint_url: MintUrl,
request: MintBolt11Request, request: MintBolt11Request<String>,
) -> Result<MintBolt11Response, Error> { ) -> Result<MintBolt11Response, Error> {
let url = mint_url.join_paths(&["v1", "mint", "bolt11"])?; let url = mint_url.join_paths(&["v1", "mint", "bolt11"])?;
@@ -176,7 +176,7 @@ impl HttpClientMethods for HttpClient {
&self, &self,
mint_url: MintUrl, mint_url: MintUrl,
request: MeltQuoteBolt11Request, request: MeltQuoteBolt11Request,
) -> Result<MeltQuoteBolt11Response, Error> { ) -> Result<MeltQuoteBolt11Response<String>, Error> {
let url = mint_url.join_paths(&["v1", "melt", "quote", "bolt11"])?; let url = mint_url.join_paths(&["v1", "melt", "quote", "bolt11"])?;
let res = self let res = self
@@ -188,7 +188,7 @@ impl HttpClientMethods for HttpClient {
.text() .text()
.await?; .await?;
convert_http_response!(MeltQuoteBolt11Response, res) convert_http_response!(MeltQuoteBolt11Response<String>, res)
} }
/// Melt Quote Status /// Melt Quote Status
@@ -197,12 +197,12 @@ impl HttpClientMethods for HttpClient {
&self, &self,
mint_url: MintUrl, mint_url: MintUrl,
quote_id: &str, quote_id: &str,
) -> Result<MeltQuoteBolt11Response, Error> { ) -> Result<MeltQuoteBolt11Response<String>, Error> {
let url = mint_url.join_paths(&["v1", "melt", "quote", "bolt11", quote_id])?; let url = mint_url.join_paths(&["v1", "melt", "quote", "bolt11", quote_id])?;
let res = self.inner.get(url).send().await?.text().await?; let res = self.inner.get(url).send().await?.text().await?;
convert_http_response!(MeltQuoteBolt11Response, res) convert_http_response!(MeltQuoteBolt11Response<String>, res)
} }
/// Melt [NUT-05] /// Melt [NUT-05]
@@ -211,8 +211,8 @@ impl HttpClientMethods for HttpClient {
async fn post_melt( async fn post_melt(
&self, &self,
mint_url: MintUrl, mint_url: MintUrl,
request: MeltBolt11Request, request: MeltBolt11Request<String>,
) -> Result<MeltQuoteBolt11Response, Error> { ) -> Result<MeltQuoteBolt11Response<String>, Error> {
let url = mint_url.join_paths(&["v1", "melt", "bolt11"])?; let url = mint_url.join_paths(&["v1", "melt", "bolt11"])?;
let res = self let res = self
@@ -224,7 +224,7 @@ impl HttpClientMethods for HttpClient {
.text() .text()
.await?; .await?;
convert_http_response!(MeltQuoteBolt11Response, res) convert_http_response!(MeltQuoteBolt11Response<String>, res)
} }
/// Swap Token [NUT-03] /// Swap Token [NUT-03]
@@ -319,20 +319,20 @@ pub trait HttpClientMethods: Debug {
&self, &self,
mint_url: MintUrl, mint_url: MintUrl,
request: MintQuoteBolt11Request, request: MintQuoteBolt11Request,
) -> Result<MintQuoteBolt11Response, Error>; ) -> Result<MintQuoteBolt11Response<String>, Error>;
/// Mint Quote status /// Mint Quote status
async fn get_mint_quote_status( async fn get_mint_quote_status(
&self, &self,
mint_url: MintUrl, mint_url: MintUrl,
quote_id: &str, quote_id: &str,
) -> Result<MintQuoteBolt11Response, Error>; ) -> Result<MintQuoteBolt11Response<String>, Error>;
/// Mint Tokens [NUT-04] /// Mint Tokens [NUT-04]
async fn post_mint( async fn post_mint(
&self, &self,
mint_url: MintUrl, mint_url: MintUrl,
request: MintBolt11Request, request: MintBolt11Request<String>,
) -> Result<MintBolt11Response, Error>; ) -> Result<MintBolt11Response, Error>;
/// Melt Quote [NUT-05] /// Melt Quote [NUT-05]
@@ -340,22 +340,22 @@ pub trait HttpClientMethods: Debug {
&self, &self,
mint_url: MintUrl, mint_url: MintUrl,
request: MeltQuoteBolt11Request, request: MeltQuoteBolt11Request,
) -> Result<MeltQuoteBolt11Response, Error>; ) -> Result<MeltQuoteBolt11Response<String>, Error>;
/// Melt Quote Status /// Melt Quote Status
async fn get_melt_quote_status( async fn get_melt_quote_status(
&self, &self,
mint_url: MintUrl, mint_url: MintUrl,
quote_id: &str, quote_id: &str,
) -> Result<MeltQuoteBolt11Response, Error>; ) -> Result<MeltQuoteBolt11Response<String>, Error>;
/// Melt [NUT-05] /// Melt [NUT-05]
/// [Nut-08] Lightning fee return if outputs defined /// [Nut-08] Lightning fee return if outputs defined
async fn post_melt( async fn post_melt(
&self, &self,
mint_url: MintUrl, mint_url: MintUrl,
request: MeltBolt11Request, request: MeltBolt11Request<String>,
) -> Result<MeltQuoteBolt11Response, Error>; ) -> Result<MeltQuoteBolt11Response<String>, Error>;
/// Split Token [NUT-06] /// Split Token [NUT-06]
async fn post_swap( async fn post_swap(

View File

@@ -95,7 +95,7 @@ impl Wallet {
pub async fn melt_quote_status( pub async fn melt_quote_status(
&self, &self,
quote_id: &str, quote_id: &str,
) -> Result<MeltQuoteBolt11Response, Error> { ) -> Result<MeltQuoteBolt11Response<String>, Error> {
let response = self let response = self
.client .client
.get_melt_quote_status(self.mint_url.clone(), quote_id) .get_melt_quote_status(self.mint_url.clone(), quote_id)

View File

@@ -78,7 +78,7 @@ impl Wallet {
let quote = MintQuote { let quote = MintQuote {
mint_url, mint_url,
id: quote_res.quote.clone(), id: quote_res.quote,
amount, amount,
unit: unit.clone(), unit: unit.clone(),
request: quote_res.request, request: quote_res.request,
@@ -93,7 +93,10 @@ impl Wallet {
/// Check mint quote status /// Check mint quote status
#[instrument(skip(self, quote_id))] #[instrument(skip(self, quote_id))]
pub async fn mint_quote_state(&self, quote_id: &str) -> Result<MintQuoteBolt11Response, Error> { pub async fn mint_quote_state(
&self,
quote_id: &str,
) -> Result<MintQuoteBolt11Response<String>, Error> {
let response = self let response = self
.client .client
.get_mint_quote_status(self.mint_url.clone(), quote_id) .get_mint_quote_status(self.mint_url.clone(), quote_id)