1use std::collections::HashMap;
4
5use async_trait::async_trait;
6use cashu::quote_id::QuoteId;
7use cashu::Amount;
8
9use super::Error;
10use crate::mint::{self, MintKeySetInfo, MintQuote as MintMintQuote};
11use crate::nuts::{
12 BlindSignature, BlindedMessage, CurrencyUnit, Id, MeltQuoteState, Proof, Proofs, PublicKey,
13 State,
14};
15use crate::payment::PaymentIdentifier;
16
17#[cfg(feature = "auth")]
18mod auth;
19
20#[cfg(feature = "test")]
21pub mod test;
22
23#[cfg(feature = "auth")]
24pub use auth::{DynMintAuthDatabase, MintAuthDatabase, MintAuthTransaction};
25
26pub const KVSTORE_NAMESPACE_KEY_ALPHABET: &str =
28 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-";
29
30pub const KVSTORE_NAMESPACE_KEY_MAX_LEN: usize = 120;
32
33pub fn validate_kvstore_string(s: &str) -> Result<(), Error> {
35 if s.len() > KVSTORE_NAMESPACE_KEY_MAX_LEN {
36 return Err(Error::KVStoreInvalidKey(format!(
37 "{} exceeds maximum length of key characters",
38 KVSTORE_NAMESPACE_KEY_MAX_LEN
39 )));
40 }
41
42 if !s
43 .chars()
44 .all(|c| KVSTORE_NAMESPACE_KEY_ALPHABET.contains(c))
45 {
46 return Err(Error::KVStoreInvalidKey("key contains invalid characters. Only ASCII letters, numbers, underscore, and hyphen are allowed".to_string()));
47 }
48
49 Ok(())
50}
51
52pub fn validate_kvstore_params(
54 primary_namespace: &str,
55 secondary_namespace: &str,
56 key: &str,
57) -> Result<(), Error> {
58 validate_kvstore_string(primary_namespace)?;
60
61 validate_kvstore_string(secondary_namespace)?;
63
64 validate_kvstore_string(key)?;
66
67 if primary_namespace.is_empty() && !secondary_namespace.is_empty() {
69 return Err(Error::KVStoreInvalidKey(
70 "If primary_namespace is empty, secondary_namespace must also be empty".to_string(),
71 ));
72 }
73
74 let namespace_key = format!("{}/{}", primary_namespace, secondary_namespace);
76 if key == primary_namespace || key == secondary_namespace || key == namespace_key {
77 return Err(Error::KVStoreInvalidKey(format!(
78 "Key '{}' conflicts with namespace names",
79 key
80 )));
81 }
82
83 Ok(())
84}
85
86#[derive(Debug, Clone, PartialEq, Eq)]
88pub struct MeltRequestInfo {
89 pub inputs_amount: Amount,
91 pub inputs_fee: Amount,
93 pub change_outputs: Vec<BlindedMessage>,
95}
96
97#[async_trait]
99pub trait KeysDatabaseTransaction<'a, Error>: DbTransactionFinalizer<Err = Error> {
100 async fn set_active_keyset(&mut self, unit: CurrencyUnit, id: Id) -> Result<(), Error>;
102
103 async fn add_keyset_info(&mut self, keyset: MintKeySetInfo) -> Result<(), Error>;
105}
106
107#[async_trait]
109pub trait KeysDatabase {
110 type Err: Into<Error> + From<Error>;
112
113 async fn begin_transaction<'a>(
115 &'a self,
116 ) -> Result<Box<dyn KeysDatabaseTransaction<'a, Self::Err> + Send + Sync + 'a>, Error>;
117
118 async fn get_active_keyset_id(&self, unit: &CurrencyUnit) -> Result<Option<Id>, Self::Err>;
120
121 async fn get_active_keysets(&self) -> Result<HashMap<CurrencyUnit, Id>, Self::Err>;
123
124 async fn get_keyset_info(&self, id: &Id) -> Result<Option<MintKeySetInfo>, Self::Err>;
126
127 async fn get_keyset_infos(&self) -> Result<Vec<MintKeySetInfo>, Self::Err>;
129}
130
131#[async_trait]
133pub trait QuotesTransaction<'a> {
134 type Err: Into<Error> + From<Error>;
136
137 async fn add_melt_request_and_blinded_messages(
139 &mut self,
140 quote_id: &QuoteId,
141 inputs_amount: Amount,
142 inputs_fee: Amount,
143 blinded_messages: &[BlindedMessage],
144 ) -> Result<(), Self::Err>;
145
146 async fn get_melt_request_and_blinded_messages(
148 &mut self,
149 quote_id: &QuoteId,
150 ) -> Result<Option<MeltRequestInfo>, Self::Err>;
151
152 async fn delete_melt_request(&mut self, quote_id: &QuoteId) -> Result<(), Self::Err>;
154
155 async fn get_mint_quote(
157 &mut self,
158 quote_id: &QuoteId,
159 ) -> Result<Option<MintMintQuote>, Self::Err>;
160 async fn add_mint_quote(&mut self, quote: MintMintQuote) -> Result<(), Self::Err>;
162 async fn increment_mint_quote_amount_paid(
164 &mut self,
165 quote_id: &QuoteId,
166 amount_paid: Amount,
167 payment_id: String,
168 ) -> Result<Amount, Self::Err>;
169 async fn increment_mint_quote_amount_issued(
171 &mut self,
172 quote_id: &QuoteId,
173 amount_issued: Amount,
174 ) -> Result<Amount, Self::Err>;
175 async fn remove_mint_quote(&mut self, quote_id: &QuoteId) -> Result<(), Self::Err>;
177 async fn get_melt_quote(
179 &mut self,
180 quote_id: &QuoteId,
181 ) -> Result<Option<mint::MeltQuote>, Self::Err>;
182 async fn add_melt_quote(&mut self, quote: mint::MeltQuote) -> Result<(), Self::Err>;
184
185 async fn update_melt_quote_request_lookup_id(
187 &mut self,
188 quote_id: &QuoteId,
189 new_request_lookup_id: &PaymentIdentifier,
190 ) -> Result<(), Self::Err>;
191
192 async fn update_melt_quote_state(
196 &mut self,
197 quote_id: &QuoteId,
198 new_state: MeltQuoteState,
199 payment_proof: Option<String>,
200 ) -> Result<(MeltQuoteState, mint::MeltQuote), Self::Err>;
201 async fn remove_melt_quote(&mut self, quote_id: &QuoteId) -> Result<(), Self::Err>;
203 async fn get_mint_quote_by_request(
205 &mut self,
206 request: &str,
207 ) -> Result<Option<MintMintQuote>, Self::Err>;
208
209 async fn get_mint_quote_by_request_lookup_id(
211 &mut self,
212 request_lookup_id: &PaymentIdentifier,
213 ) -> Result<Option<MintMintQuote>, Self::Err>;
214}
215
216#[async_trait]
218pub trait QuotesDatabase {
219 type Err: Into<Error> + From<Error>;
221
222 async fn get_mint_quote(&self, quote_id: &QuoteId) -> Result<Option<MintMintQuote>, Self::Err>;
224
225 async fn get_mint_quote_by_request(
227 &self,
228 request: &str,
229 ) -> Result<Option<MintMintQuote>, Self::Err>;
230 async fn get_mint_quote_by_request_lookup_id(
232 &self,
233 request_lookup_id: &PaymentIdentifier,
234 ) -> Result<Option<MintMintQuote>, Self::Err>;
235 async fn get_mint_quotes(&self) -> Result<Vec<MintMintQuote>, Self::Err>;
237 async fn get_melt_quote(
239 &self,
240 quote_id: &QuoteId,
241 ) -> Result<Option<mint::MeltQuote>, Self::Err>;
242 async fn get_melt_quotes(&self) -> Result<Vec<mint::MeltQuote>, Self::Err>;
244}
245
246#[async_trait]
248pub trait ProofsTransaction<'a> {
249 type Err: Into<Error> + From<Error>;
251
252 async fn add_proofs(
257 &mut self,
258 proof: Proofs,
259 quote_id: Option<QuoteId>,
260 ) -> Result<(), Self::Err>;
261 async fn update_proofs_states(
263 &mut self,
264 ys: &[PublicKey],
265 proofs_state: State,
266 ) -> Result<Vec<Option<State>>, Self::Err>;
267
268 async fn remove_proofs(
270 &mut self,
271 ys: &[PublicKey],
272 quote_id: Option<QuoteId>,
273 ) -> Result<(), Self::Err>;
274
275 async fn get_proof_ys_by_quote_id(
277 &self,
278 quote_id: &QuoteId,
279 ) -> Result<Vec<PublicKey>, Self::Err>;
280}
281
282#[async_trait]
284pub trait ProofsDatabase {
285 type Err: Into<Error> + From<Error>;
287
288 async fn get_proofs_by_ys(&self, ys: &[PublicKey]) -> Result<Vec<Option<Proof>>, Self::Err>;
290 async fn get_proof_ys_by_quote_id(
292 &self,
293 quote_id: &QuoteId,
294 ) -> Result<Vec<PublicKey>, Self::Err>;
295 async fn get_proofs_states(&self, ys: &[PublicKey]) -> Result<Vec<Option<State>>, Self::Err>;
297 async fn get_proofs_by_keyset_id(
299 &self,
300 keyset_id: &Id,
301 ) -> Result<(Proofs, Vec<Option<State>>), Self::Err>;
302}
303
304#[async_trait]
305pub trait SignaturesTransaction<'a> {
307 type Err: Into<Error> + From<Error>;
309
310 async fn add_blind_signatures(
312 &mut self,
313 blinded_messages: &[PublicKey],
314 blind_signatures: &[BlindSignature],
315 quote_id: Option<QuoteId>,
316 ) -> Result<(), Self::Err>;
317
318 async fn get_blind_signatures(
320 &mut self,
321 blinded_messages: &[PublicKey],
322 ) -> Result<Vec<Option<BlindSignature>>, Self::Err>;
323}
324
325#[async_trait]
326pub trait SignaturesDatabase {
328 type Err: Into<Error> + From<Error>;
330
331 async fn get_blind_signatures(
333 &self,
334 blinded_messages: &[PublicKey],
335 ) -> Result<Vec<Option<BlindSignature>>, Self::Err>;
336 async fn get_blind_signatures_for_keyset(
338 &self,
339 keyset_id: &Id,
340 ) -> Result<Vec<BlindSignature>, Self::Err>;
341 async fn get_blind_signatures_for_quote(
343 &self,
344 quote_id: &QuoteId,
345 ) -> Result<Vec<BlindSignature>, Self::Err>;
346}
347
348#[async_trait]
349pub trait DbTransactionFinalizer {
351 type Err: Into<Error> + From<Error>;
353
354 async fn commit(self: Box<Self>) -> Result<(), Self::Err>;
356
357 async fn rollback(self: Box<Self>) -> Result<(), Self::Err>;
359}
360
361#[async_trait]
363pub trait KVStoreTransaction<'a, Error>: DbTransactionFinalizer<Err = Error> {
364 async fn kv_read(
366 &mut self,
367 primary_namespace: &str,
368 secondary_namespace: &str,
369 key: &str,
370 ) -> Result<Option<Vec<u8>>, Error>;
371
372 async fn kv_write(
374 &mut self,
375 primary_namespace: &str,
376 secondary_namespace: &str,
377 key: &str,
378 value: &[u8],
379 ) -> Result<(), Error>;
380
381 async fn kv_remove(
383 &mut self,
384 primary_namespace: &str,
385 secondary_namespace: &str,
386 key: &str,
387 ) -> Result<(), Error>;
388
389 async fn kv_list(
391 &mut self,
392 primary_namespace: &str,
393 secondary_namespace: &str,
394 ) -> Result<Vec<String>, Error>;
395}
396
397pub trait Transaction<'a, Error>:
399 DbTransactionFinalizer<Err = Error>
400 + QuotesTransaction<'a, Err = Error>
401 + SignaturesTransaction<'a, Err = Error>
402 + ProofsTransaction<'a, Err = Error>
403 + KVStoreTransaction<'a, Error>
404{
405}
406
407#[async_trait]
409pub trait KVStoreDatabase {
410 type Err: Into<Error> + From<Error>;
412
413 async fn kv_read(
415 &self,
416 primary_namespace: &str,
417 secondary_namespace: &str,
418 key: &str,
419 ) -> Result<Option<Vec<u8>>, Self::Err>;
420
421 async fn kv_list(
423 &self,
424 primary_namespace: &str,
425 secondary_namespace: &str,
426 ) -> Result<Vec<String>, Self::Err>;
427}
428
429#[async_trait]
431pub trait KVStore: KVStoreDatabase {
432 async fn begin_transaction<'a>(
434 &'a self,
435 ) -> Result<Box<dyn KVStoreTransaction<'a, Self::Err> + Send + Sync + 'a>, Error>;
436}
437
438pub type DynMintKVStore = std::sync::Arc<dyn KVStore<Err = Error> + Send + Sync>;
440
441#[async_trait]
443pub trait Database<Error>:
444 KVStoreDatabase<Err = Error>
445 + QuotesDatabase<Err = Error>
446 + ProofsDatabase<Err = Error>
447 + SignaturesDatabase<Err = Error>
448{
449 async fn begin_transaction<'a>(
451 &'a self,
452 ) -> Result<Box<dyn Transaction<'a, Error> + Send + Sync + 'a>, Error>;
453}
454
455pub type DynMintDatabase = std::sync::Arc<dyn Database<Error> + Send + Sync>;