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"
paste = "1.0.15"
serde = { version = "1.0.210", features = ["derive"] }
uuid = { version = "1", features = ["v4", "serde"] }
[features]
swagger = ["cdk/swagger", "dep:utoipa"]

View File

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

View File

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

View File

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

View File

@@ -120,7 +120,7 @@ async fn test_regtest_mint_melt_round_trip() -> Result<()> {
_ => panic!("Wrong payload"),
};
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);
// get current state
@@ -131,7 +131,7 @@ async fn test_regtest_mint_melt_round_trip() -> Result<()> {
_ => panic!("Wrong payload"),
};
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);
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_json = "1"
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::Amount;
use lightning_invoice::Bolt11Invoice;
use redb::{Database, MultimapTableDefinition, ReadableTable, TableDefinition};
use redb::{
Database, MultimapTableDefinition, ReadableMultimapTable, ReadableTable, TableDefinition,
};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
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> =
TableDefinition::new("pending_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)
}
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
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
struct V1MintQuote {
pub id: String,
pub id: Uuid,
pub mint_url: MintUrl,
pub amount: Amount,
pub unit: CurrencyUnit,
@@ -66,7 +217,7 @@ impl From<V1MintQuote> for MintQuote {
fn migrate_mint_quotes_01_to_02(db: Arc<Database>) -> Result<(), Error> {
let read_txn = db.begin_read().map_err(Error::from)?;
let table = read_txn
.open_table(MINT_QUOTES_TABLE)
.open_table(ID_STR_MINT_QUOTES_TABLE)
.map_err(Error::from)?;
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
.open_table(MINT_QUOTES_TABLE)
.open_table(ID_STR_MINT_QUOTES_TABLE)
.map_err(Error::from)?;
for (quote_id, quote) in migrated_mint_quotes {
match quote {

View File

@@ -17,8 +17,9 @@ use cdk::nuts::{
};
use cdk::types::LnKey;
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 uuid::Uuid;
use super::error::Error;
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 KEYSETS_TABLE: TableDefinition<&str, &str> = TableDefinition::new("keysets");
const MINT_QUOTES_TABLE: TableDefinition<&str, &str> = TableDefinition::new("mint_quotes");
const MELT_QUOTES_TABLE: TableDefinition<&str, &str> = TableDefinition::new("melt_quotes");
const MINT_QUOTES_TABLE: TableDefinition<[u8; 16], &str> = TableDefinition::new("mint_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_STATE_TABLE: TableDefinition<[u8; 33], &str> = TableDefinition::new("proofs_state");
const CONFIG_TABLE: TableDefinition<&str, &str> = TableDefinition::new("config");
// Key is hex blinded_message B_ value is blinded_signature
const BLINDED_SIGNATURES: TableDefinition<[u8; 33], &str> =
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");
const QUOTE_SIGNATURES_TABLE: MultimapTableDefinition<&str, [u8; 33]> =
const QUOTE_SIGNATURES_TABLE: MultimapTableDefinition<[u8; 16], [u8; 33]> =
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
#[derive(Debug, Clone)]
@@ -93,6 +95,10 @@ impl MintRedbDatabase {
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 {
tracing::warn!(
"Database upgrade did not complete at {} current is {}",
@@ -261,7 +267,7 @@ impl MintDatabase for MintRedbDatabase {
.map_err(Error::from)?;
table
.insert(
quote.id.as_str(),
quote.id.as_bytes(),
serde_json::to_string(&quote).map_err(Error::from)?.as_str(),
)
.map_err(Error::from)?;
@@ -271,13 +277,13 @@ impl MintDatabase for MintRedbDatabase {
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 table = read_txn
.open_table(MINT_QUOTES_TABLE)
.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)?),
None => Ok(None),
}
@@ -285,7 +291,7 @@ impl MintDatabase for MintRedbDatabase {
async fn update_mint_quote_state(
&self,
quote_id: &str,
quote_id: &Uuid,
state: MintQuoteState,
) -> Result<MintQuoteState, Self::Err> {
let write_txn = self.db.begin_write().map_err(Error::from)?;
@@ -297,7 +303,7 @@ impl MintDatabase for MintRedbDatabase {
.map_err(Error::from)?;
let quote_guard = table
.get(quote_id)
.get(quote_id.as_bytes())
.map_err(Error::from)?
.ok_or(Error::UnknownMintInfo)?;
@@ -316,7 +322,7 @@ impl MintDatabase for MintRedbDatabase {
table
.insert(
quote_id,
quote_id.as_bytes(),
serde_json::to_string(&mint_quote)
.map_err(Error::from)?
.as_str(),
@@ -376,14 +382,14 @@ impl MintDatabase for MintRedbDatabase {
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 mut table = write_txn
.open_table(MINT_QUOTES_TABLE)
.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)?;
@@ -399,7 +405,7 @@ impl MintDatabase for MintRedbDatabase {
.map_err(Error::from)?;
table
.insert(
quote.id.as_str(),
quote.id.as_bytes(),
serde_json::to_string(&quote).map_err(Error::from)?.as_str(),
)
.map_err(Error::from)?;
@@ -409,20 +415,20 @@ impl MintDatabase for MintRedbDatabase {
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 table = read_txn
.open_table(MELT_QUOTES_TABLE)
.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()))
}
async fn update_melt_quote_state(
&self,
quote_id: &str,
quote_id: &Uuid,
state: MeltQuoteState,
) -> Result<MeltQuoteState, Self::Err> {
let write_txn = self.db.begin_write().map_err(Error::from)?;
@@ -435,7 +441,7 @@ impl MintDatabase for MintRedbDatabase {
.map_err(Error::from)?;
let quote_guard = table
.get(quote_id)
.get(quote_id.as_bytes())
.map_err(Error::from)?
.ok_or(Error::UnknownMintInfo)?;
@@ -454,7 +460,7 @@ impl MintDatabase for MintRedbDatabase {
table
.insert(
quote_id,
quote_id.as_bytes(),
serde_json::to_string(&melt_quote)
.map_err(Error::from)?
.as_str(),
@@ -483,21 +489,21 @@ impl MintDatabase for MintRedbDatabase {
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 mut table = write_txn
.open_table(MELT_QUOTES_TABLE)
.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)?;
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)?;
{
@@ -519,7 +525,7 @@ impl MintDatabase for MintRedbDatabase {
if let Some(quote_id) = &quote_id {
quote_proofs_table
.insert(quote_id.as_str(), y)
.insert(quote_id.as_bytes(), y)
.map_err(Error::from)?;
}
}
@@ -547,13 +553,13 @@ impl MintDatabase for MintRedbDatabase {
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 table = read_txn
.open_multimap_table(QUOTE_PROOFS_TABLE)
.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| {
if let Ok(y) = y {
@@ -660,7 +666,7 @@ impl MintDatabase for MintRedbDatabase {
&self,
blinded_messages: &[PublicKey],
blind_signatures: &[BlindSignature],
quote_id: Option<String>,
quote_id: Option<Uuid>,
) -> Result<(), Self::Err> {
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 {
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)?;
}
}
@@ -740,7 +746,7 @@ impl MintDatabase for MintRedbDatabase {
/// Add melt request
async fn add_melt_request(
&self,
melt_request: MeltBolt11Request,
melt_request: MeltBolt11Request<Uuid>,
ln_key: LnKey,
) -> Result<(), Self::Err> {
let write_txn = self.db.begin_write().map_err(Error::from)?;
@@ -748,7 +754,7 @@ impl MintDatabase for MintRedbDatabase {
table
.insert(
melt_request.quote.as_str(),
melt_request.quote.as_bytes(),
(
serde_json::to_string(&melt_request)?.as_str(),
serde_json::to_string(&ln_key)?.as_str(),
@@ -761,12 +767,12 @@ impl MintDatabase for MintRedbDatabase {
/// Get melt request
async fn get_melt_request(
&self,
quote_id: &str,
) -> Result<Option<(MeltBolt11Request, LnKey)>, Self::Err> {
quote_id: &Uuid,
) -> Result<Option<(MeltBolt11Request<Uuid>, LnKey)>, Self::Err> {
let read_txn = self.db.begin_read().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) => {
let (melt_request_str, ln_key_str) = melt_request.value();
let melt_request = serde_json::from_str(melt_request_str)?;
@@ -781,14 +787,14 @@ impl MintDatabase for MintRedbDatabase {
/// Get [`BlindSignature`]s for quote
async fn get_blind_signatures_for_quote(
&self,
quote_id: &str,
quote_id: &Uuid,
) -> Result<Vec<BlindSignature>, Self::Err> {
let read_txn = self.db.begin_read().map_err(Error::from)?;
let quote_proofs_table = read_txn
.open_multimap_table(QUOTE_SIGNATURES_TABLE)
.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();

View File

@@ -19,7 +19,7 @@ wallet = ["cdk/wallet"]
async-trait = "0.1"
cdk = { path = "../cdk", version = "0.5.0", 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"
tokio = { version = "1", features = [
"time",
@@ -29,3 +29,4 @@ tokio = { version = "1", features = [
tracing = { version = "0.1", default-features = false, features = ["attributes", "log"] }
serde_json = "1"
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 sqlx::sqlite::{SqliteConnectOptions, SqlitePool, SqlitePoolOptions, SqliteRow};
use sqlx::Row;
use uuid::fmt::Hyphenated;
use uuid::Uuid;
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 rec = sqlx::query(
r#"
@@ -245,7 +247,7 @@ FROM mint_quote
WHERE id=?;
"#,
)
.bind(quote_id)
.bind(quote_id.as_hyphenated())
.fetch_one(&mut transaction)
.await;
@@ -345,7 +347,7 @@ WHERE request_lookup_id=?;
async fn update_mint_quote_state(
&self,
quote_id: &str,
quote_id: &Uuid,
state: MintQuoteState,
) -> Result<MintQuoteState, Self::Err> {
let mut transaction = self.pool.begin().await.map_err(Error::from)?;
@@ -357,7 +359,7 @@ FROM mint_quote
WHERE id=?;
"#,
)
.bind(quote_id)
.bind(quote_id.as_hyphenated())
.fetch_one(&mut transaction)
.await;
let quote = match rec {
@@ -378,7 +380,7 @@ WHERE id=?;
"#,
)
.bind(state.to_string())
.bind(quote_id)
.bind(quote_id.as_hyphenated())
.execute(&mut transaction)
.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 res = sqlx::query(
@@ -439,7 +441,7 @@ DELETE FROM mint_quote
WHERE id=?
"#,
)
.bind(quote_id)
.bind(quote_id.as_hyphenated())
.execute(&mut transaction)
.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 rec = sqlx::query(
r#"
@@ -506,7 +508,7 @@ FROM melt_quote
WHERE id=?;
"#,
)
.bind(quote_id)
.bind(quote_id.as_hyphenated())
.fetch_one(&mut transaction)
.await;
@@ -564,7 +566,7 @@ FROM melt_quote
async fn update_melt_quote_state(
&self,
quote_id: &str,
quote_id: &Uuid,
state: MeltQuoteState,
) -> Result<MeltQuoteState, Self::Err> {
let mut transaction = self.pool.begin().await.map_err(Error::from)?;
@@ -576,7 +578,7 @@ FROM melt_quote
WHERE id=?;
"#,
)
.bind(quote_id)
.bind(quote_id.as_hyphenated())
.fetch_one(&mut transaction)
.await;
@@ -598,7 +600,7 @@ WHERE id=?;
"#,
)
.bind(state.to_string())
.bind(quote_id)
.bind(quote_id.as_hyphenated())
.execute(&mut transaction)
.await;
@@ -619,7 +621,7 @@ WHERE id=?;
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 res = sqlx::query(
r#"
@@ -627,7 +629,7 @@ DELETE FROM melt_quote
WHERE id=?
"#,
)
.bind(quote_id)
.bind(quote_id.as_hyphenated())
.execute(&mut transaction)
.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)?;
for proof in proofs {
if let Err(err) = sqlx::query(
@@ -765,7 +767,7 @@ VALUES (?, ?, ?, ?, ?, ?, ?, ?);
.bind(proof.c.to_bytes().to_vec())
.bind(proof.witness.map(|w| serde_json::to_string(&w).unwrap()))
.bind("UNSPENT")
.bind(quote_id.clone())
.bind(quote_id.map(|q| q.hyphenated()))
.execute(&mut transaction)
.await
.map_err(Error::from)
@@ -812,7 +814,7 @@ VALUES (?, ?, ?, ?, ?, ?, ?, ?);
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 rec = sqlx::query(
@@ -822,7 +824,7 @@ FROM proof
WHERE quote_id=?;
"#,
)
.bind(quote_id)
.bind(quote_id.as_hyphenated())
.fetch_all(&mut transaction)
.await;
@@ -996,7 +998,7 @@ WHERE keyset_id=?;
&self,
blinded_messages: &[PublicKey],
blinded_signatures: &[BlindSignature],
quote_id: Option<String>,
quote_id: Option<Uuid>,
) -> Result<(), Self::Err> {
let mut transaction = self.pool.begin().await.map_err(Error::from)?;
for (message, signature) in blinded_messages.iter().zip(blinded_signatures) {
@@ -1011,7 +1013,7 @@ VALUES (?, ?, ?, ?, ?, ?, ?);
.bind(u64::from(signature.amount) as i64)
.bind(signature.keyset_id.to_string())
.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.s.to_secret_hex()))
.execute(&mut transaction)
@@ -1111,7 +1113,7 @@ WHERE keyset_id=?;
async fn add_melt_request(
&self,
melt_request: MeltBolt11Request,
melt_request: MeltBolt11Request<Uuid>,
ln_key: LnKey,
) -> Result<(), Self::Err> {
let mut transaction = self.pool.begin().await.map_err(Error::from)?;
@@ -1149,8 +1151,8 @@ VALUES (?, ?, ?, ?, ?);
async fn get_melt_request(
&self,
quote_id: &str,
) -> Result<Option<(MeltBolt11Request, LnKey)>, Self::Err> {
quote_id: &Uuid,
) -> Result<Option<(MeltBolt11Request<Uuid>, LnKey)>, Self::Err> {
let mut transaction = self.pool.begin().await.map_err(Error::from)?;
let rec = sqlx::query(
@@ -1160,7 +1162,7 @@ FROM melt_request
WHERE id=?;
"#,
)
.bind(quote_id)
.bind(quote_id.as_hyphenated())
.fetch_one(&mut transaction)
.await;
@@ -1192,7 +1194,7 @@ WHERE id=?;
/// Get [`BlindSignature`]s for quote
async fn get_blind_signatures_for_quote(
&self,
quote_id: &str,
quote_id: &Uuid,
) -> Result<Vec<BlindSignature>, Self::Err> {
let mut transaction = self.pool.begin().await.map_err(Error::from)?;
@@ -1203,7 +1205,7 @@ FROM blind_signature
WHERE quote_id=?;
"#,
)
.bind(quote_id)
.bind(quote_id.as_hyphenated())
.fetch_all(&mut transaction)
.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> {
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_amount: i64 = row.try_get("amount").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 {
id: row_id,
id: row_id.into_uuid(),
mint_url: MintUrl::from_str(&row_mint_url)?,
amount: Amount::from(row_amount as u64),
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> {
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_amount: i64 = row.try_get("amount").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());
Ok(mint::MeltQuote {
id: row_id,
id: row_id.into_uuid(),
amount: Amount::from(row_amount as u64),
unit: CurrencyUnit::from_str(&row_unit).map_err(Error::from)?,
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> {
let quote_id: String = row.try_get("id").map_err(Error::from)?;
fn sqlite_row_to_melt_request(row: SqliteRow) -> Result<(MeltBolt11Request<Uuid>, LnKey), Error> {
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_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_unit: String = row.try_get("unit").map_err(Error::from)?;
let melt_request = MeltBolt11Request {
quote: quote_id,
quote: quote_id.into_uuid(),
inputs: serde_json::from_str(&row_inputs)?,
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"] }
url = "2.3"
utoipa = { version = "4", optional = true }
uuid = { version = "1", features = ["v4"] }
uuid = { version = "1", features = ["v4", "serde"] }
# -Z minimal-versions
sync_wrapper = "0.1.2"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,7 @@
//! Specific Subscription for the cdk crate
use std::ops::Deref;
use std::str::FromStr;
use std::sync::Arc;
use serde::{Deserialize, Serialize};
@@ -8,7 +9,9 @@ use serde::{Deserialize, Serialize};
mod on_subscription;
pub use on_subscription::OnSubscription;
use uuid::Uuid;
use super::PublicKey;
use crate::cdk_database::{self, MintDatabase};
use crate::nuts::{
BlindSignature, CurrencyUnit, MeltQuoteBolt11Response, MeltQuoteState, MintQuoteBolt11Response,
@@ -91,9 +94,9 @@ pub enum NotificationPayload {
/// Proof State
ProofState(ProofState),
/// Melt Quote Bolt11 Response
MeltQuoteBolt11Response(MeltQuoteBolt11Response),
MeltQuoteBolt11Response(MeltQuoteBolt11Response<Uuid>),
/// Mint Quote Bolt11 Response
MintQuoteBolt11Response(MintQuoteBolt11Response),
MintQuoteBolt11Response(MintQuoteBolt11Response<Uuid>),
}
impl From<ProofState> for NotificationPayload {
@@ -102,37 +105,42 @@ impl From<ProofState> for NotificationPayload {
}
}
impl From<MeltQuoteBolt11Response> for NotificationPayload {
fn from(melt_quote: MeltQuoteBolt11Response) -> NotificationPayload {
impl From<MeltQuoteBolt11Response<Uuid>> for NotificationPayload {
fn from(melt_quote: MeltQuoteBolt11Response<Uuid>) -> NotificationPayload {
NotificationPayload::MeltQuoteBolt11Response(melt_quote)
}
}
impl From<MintQuoteBolt11Response> for NotificationPayload {
fn from(mint_quote: MintQuoteBolt11Response) -> NotificationPayload {
impl From<MintQuoteBolt11Response<Uuid>> for NotificationPayload {
fn from(mint_quote: MintQuoteBolt11Response<Uuid>) -> NotificationPayload {
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 {
type Type = (String, Kind);
type Type = Notification;
fn to_indexes(&self) -> Vec<Index<Self::Type>> {
match self {
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) => {
vec![Index::from((
melt_quote.quote.clone(),
Kind::Bolt11MeltQuote,
))]
vec![Index::from(Notification::MeltQuoteBolt11(melt_quote.quote))]
}
NotificationPayload::MintQuoteBolt11Response(mint_quote) => {
vec![Index::from((
mint_quote.quote.clone(),
Kind::Bolt11MintQuote,
))]
vec![Index::from(Notification::MintQuoteBolt11(mint_quote.quote))]
}
}
}
@@ -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 {
let sub_id: SubscriptionGlobalId = Default::default();
val.filters
.iter()
.map(|filter| Index::from(((filter.clone(), val.kind), val.id.clone(), sub_id)))
.collect()
.into_iter()
.map(|filter| {
let idx = match val.kind {
Kind::Bolt11MeltQuote => {
Notification::MeltQuoteBolt11(Uuid::from_str(&filter)?)
}
Kind::Bolt11MintQuote => {
Notification::MintQuoteBolt11(Uuid::from_str(&filter)?)
}
Kind::ProofState => Notification::ProofState(PublicKey::from_str(&filter)?),
};
Ok(Index::from((idx, val.id.clone(), sub_id)))
})
.collect::<Result<_, 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
/// 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)]
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 {
type Target = pub_sub::Manager<NotificationPayload, (String, Kind), OnSubscription>;
type Target = pub_sub::Manager<NotificationPayload, Notification, OnSubscription>;
fn deref(&self) -> &Self::Target {
&self.0
@@ -202,7 +224,7 @@ impl PubSubManager {
}
/// 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,
quote: E,
new_state: MintQuoteState,
@@ -214,7 +236,7 @@ impl PubSubManager {
}
/// 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,
quote: E,
payment_preimage: Option<String>,
@@ -244,7 +266,11 @@ mod test {
let manager = PubSubManager::default();
let params = Params {
kind: Kind::ProofState,
filters: vec!["x".to_string()],
filters: vec![PublicKey::from_hex(
"02194603ffa36356f4a56b7df9371fc3192472351453ec7398b8da8117e7c3e104",
)
.unwrap()
.to_string()],
id: "uno".into(),
};

View File

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

View File

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

View File

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

View File

@@ -78,7 +78,7 @@ impl Wallet {
let quote = MintQuote {
mint_url,
id: quote_res.quote.clone(),
id: quote_res.quote,
amount,
unit: unit.clone(),
request: quote_res.request,
@@ -93,7 +93,10 @@ impl Wallet {
/// Check mint quote status
#[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
.client
.get_mint_quote_status(self.mint_url.clone(), quote_id)