mirror of
https://github.com/aljazceru/cdk.git
synced 2026-02-05 05:06:14 +01:00
feat: mint redb localstore
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
[[language]]
|
||||
name = "rust"
|
||||
config = { cargo = { features = [ "wallet", "mint" ] } }
|
||||
config = { cargo = { features = [ "wallet", "mint", "redb" ] } }
|
||||
|
||||
|
||||
@@ -84,8 +84,11 @@ impl LocalStore for MemoryLocalStore {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn add_spent_proof(&self, secret: Secret, proof: Proof) -> Result<(), Error> {
|
||||
self.spent_proofs.lock().await.insert(secret, proof);
|
||||
async fn add_spent_proof(&self, proof: Proof) -> Result<(), Error> {
|
||||
self.spent_proofs
|
||||
.lock()
|
||||
.await
|
||||
.insert(proof.secret.clone(), proof);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -93,8 +96,11 @@ impl LocalStore for MemoryLocalStore {
|
||||
Ok(self.spent_proofs.lock().await.get(secret).cloned())
|
||||
}
|
||||
|
||||
async fn add_pending_proof(&self, secret: Secret, proof: Proof) -> Result<(), Error> {
|
||||
self.pending_proofs.lock().await.insert(secret, proof);
|
||||
async fn add_pending_proof(&self, proof: Proof) -> Result<(), Error> {
|
||||
self.pending_proofs
|
||||
.lock()
|
||||
.await
|
||||
.insert(proof.secret.clone(), proof);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
mod memory;
|
||||
#[cfg(all(not(target_arch = "wasm32"), feature = "redb"))]
|
||||
mod redb_store;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
@@ -7,6 +9,8 @@ use cashu::nuts::nut02::mint::KeySet;
|
||||
use cashu::nuts::{CurrencyUnit, Id, Proof};
|
||||
use cashu::secret::Secret;
|
||||
use cashu::types::{MeltQuote, MintQuote};
|
||||
#[cfg(all(not(target_arch = "wasm32"), feature = "redb"))]
|
||||
pub use redb_store::RedbLocalStore;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
@@ -52,10 +56,10 @@ pub trait LocalStore {
|
||||
async fn get_keyset(&self, id: &Id) -> Result<Option<KeySet>, Error>;
|
||||
async fn get_keysets(&self) -> Result<Vec<KeySet>, Error>;
|
||||
|
||||
async fn add_spent_proof(&self, secret: Secret, proof: Proof) -> Result<(), Error>;
|
||||
async fn add_spent_proof(&self, proof: Proof) -> Result<(), Error>;
|
||||
async fn get_spent_proof(&self, secret: &Secret) -> Result<Option<Proof>, Error>;
|
||||
|
||||
async fn add_pending_proof(&self, secret: Secret, proof: Proof) -> Result<(), Error>;
|
||||
async fn add_pending_proof(&self, proof: Proof) -> Result<(), Error>;
|
||||
async fn get_pending_proof(&self, secret: &Secret) -> Result<Option<Proof>, Error>;
|
||||
async fn remove_pending_proof(&self, secret: &Secret) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
282
crates/cashu-sdk/src/mint/localstore/redb_store.rs
Normal file
282
crates/cashu-sdk/src/mint/localstore/redb_store.rs
Normal file
@@ -0,0 +1,282 @@
|
||||
use std::collections::HashMap;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use cashu::nuts::{CurrencyUnit, Id, MintKeySet as KeySet, Proof};
|
||||
use cashu::secret::Secret;
|
||||
use cashu::types::{MeltQuote, MintQuote};
|
||||
use redb::{Database, ReadableTable, TableDefinition};
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use super::{Error, LocalStore};
|
||||
|
||||
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 PENDING_PROOFS_TABLE: TableDefinition<&str, &str> = TableDefinition::new("pending_proofs");
|
||||
const SPENT_PROOFS_TABLE: TableDefinition<&str, &str> = TableDefinition::new("spent_proofs");
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RedbLocalStore {
|
||||
db: Arc<Mutex<Database>>,
|
||||
}
|
||||
|
||||
impl RedbLocalStore {
|
||||
pub fn new(path: &str) -> Result<Self, Error> {
|
||||
let db = Database::create(path)?;
|
||||
|
||||
let write_txn = db.begin_write()?;
|
||||
{
|
||||
let _ = write_txn.open_table(ACTIVE_KEYSETS_TABLE)?;
|
||||
let _ = write_txn.open_table(KEYSETS_TABLE)?;
|
||||
let _ = write_txn.open_table(MINT_QUOTES_TABLE)?;
|
||||
let _ = write_txn.open_table(MELT_QUOTES_TABLE)?;
|
||||
let _ = write_txn.open_table(PENDING_PROOFS_TABLE)?;
|
||||
let _ = write_txn.open_table(SPENT_PROOFS_TABLE)?;
|
||||
}
|
||||
write_txn.commit()?;
|
||||
|
||||
Ok(Self {
|
||||
db: Arc::new(Mutex::new(db)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl LocalStore for RedbLocalStore {
|
||||
async fn add_active_keyset(&self, unit: CurrencyUnit, id: Id) -> Result<(), Error> {
|
||||
let db = self.db.lock().await;
|
||||
|
||||
let write_txn = db.begin_write()?;
|
||||
|
||||
{
|
||||
let mut table = write_txn.open_table(ACTIVE_KEYSETS_TABLE)?;
|
||||
table.insert(unit.to_string().as_str(), id.to_string().as_str())?;
|
||||
}
|
||||
write_txn.commit()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_active_keyset_id(&self, unit: &CurrencyUnit) -> Result<Option<Id>, Error> {
|
||||
let db = self.db.lock().await;
|
||||
let read_txn = db.begin_read()?;
|
||||
let table = read_txn.open_table(ACTIVE_KEYSETS_TABLE)?;
|
||||
|
||||
if let Some(id) = table.get(unit.to_string().as_str())? {
|
||||
return Ok(Some(Id::from_str(id.value()).unwrap()));
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
async fn get_active_keysets(&self) -> Result<HashMap<CurrencyUnit, Id>, Error> {
|
||||
let db = self.db.lock().await;
|
||||
let read_txn = db.begin_read()?;
|
||||
let table = read_txn.open_table(ACTIVE_KEYSETS_TABLE)?;
|
||||
|
||||
let mut active_keysets = HashMap::new();
|
||||
|
||||
for keyset in table.iter()? {
|
||||
if let Ok((unit, id)) = keyset {
|
||||
let unit = serde_json::from_str(unit.value())?;
|
||||
let id = serde_json::from_str(id.value())?;
|
||||
|
||||
active_keysets.insert(unit, id);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(active_keysets)
|
||||
}
|
||||
|
||||
async fn add_keyset(&self, keyset: KeySet) -> Result<(), Error> {
|
||||
let db = self.db.lock().await;
|
||||
|
||||
let write_txn = db.begin_write()?;
|
||||
|
||||
{
|
||||
let mut table = write_txn.open_table(KEYSETS_TABLE)?;
|
||||
table.insert(
|
||||
Id::from(keyset.clone()).to_string().as_str(),
|
||||
serde_json::to_string(&keyset)?.as_str(),
|
||||
)?;
|
||||
}
|
||||
write_txn.commit()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_keyset(&self, keyset_id: &Id) -> Result<Option<KeySet>, Error> {
|
||||
let db = self.db.lock().await;
|
||||
let read_txn = db.begin_read()?;
|
||||
let table = read_txn.open_table(KEYSETS_TABLE)?;
|
||||
|
||||
let keyset = table.get(keyset_id.to_string().as_str())?;
|
||||
|
||||
Ok(keyset.map(|k| serde_json::from_str(&k.value()).unwrap()))
|
||||
}
|
||||
|
||||
async fn get_keysets(&self) -> Result<Vec<KeySet>, Error> {
|
||||
let db = self.db.lock().await;
|
||||
let read_txn = db.begin_read()?;
|
||||
let table = read_txn.open_table(KEYSETS_TABLE)?;
|
||||
|
||||
let mut keysets = Vec::new();
|
||||
|
||||
for keyset in table.iter()? {
|
||||
if let Ok((_id, keyset)) = keyset {
|
||||
let keyset = serde_json::from_str(keyset.value())?;
|
||||
|
||||
keysets.push(keyset)
|
||||
}
|
||||
}
|
||||
|
||||
Ok(keysets)
|
||||
}
|
||||
|
||||
async fn add_mint_quote(&self, quote: MintQuote) -> Result<(), Error> {
|
||||
let db = self.db.lock().await;
|
||||
|
||||
let write_txn = db.begin_write()?;
|
||||
|
||||
{
|
||||
let mut table = write_txn.open_table(MINT_QUOTES_TABLE)?;
|
||||
table.insert(quote.id.as_str(), serde_json::to_string("e)?.as_str())?;
|
||||
}
|
||||
write_txn.commit()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_mint_quote(&self, quote_id: &str) -> Result<Option<MintQuote>, Error> {
|
||||
let db = self.db.lock().await;
|
||||
let read_txn = db.begin_read()?;
|
||||
let table = read_txn.open_table(MINT_QUOTES_TABLE)?;
|
||||
|
||||
let quote = table.get(quote_id)?;
|
||||
|
||||
Ok(quote.map(|q| serde_json::from_str(&q.value()).unwrap()))
|
||||
}
|
||||
|
||||
async fn remove_mint_quote(&self, quote_id: &str) -> Result<(), Error> {
|
||||
let db = self.db.lock().await;
|
||||
|
||||
let write_txn = db.begin_write()?;
|
||||
|
||||
{
|
||||
let mut table = write_txn.open_table(MINT_QUOTES_TABLE)?;
|
||||
table.remove(quote_id)?;
|
||||
}
|
||||
write_txn.commit()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn add_melt_quote(&self, quote: MeltQuote) -> Result<(), Error> {
|
||||
let db = self.db.lock().await;
|
||||
|
||||
let write_txn = db.begin_write()?;
|
||||
|
||||
{
|
||||
let mut table = write_txn.open_table(MELT_QUOTES_TABLE)?;
|
||||
table.insert(quote.id.as_str(), serde_json::to_string("e)?.as_str())?;
|
||||
}
|
||||
write_txn.commit()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_melt_quote(&self, quote_id: &str) -> Result<Option<MeltQuote>, Error> {
|
||||
let db = self.db.lock().await;
|
||||
let read_txn = db.begin_read()?;
|
||||
let table = read_txn.open_table(MELT_QUOTES_TABLE)?;
|
||||
|
||||
let quote = table.get(quote_id)?;
|
||||
|
||||
Ok(quote.map(|q| serde_json::from_str(&q.value()).unwrap()))
|
||||
}
|
||||
|
||||
async fn remove_melt_quote(&self, quote_id: &str) -> Result<(), Error> {
|
||||
let db = self.db.lock().await;
|
||||
|
||||
let write_txn = db.begin_write()?;
|
||||
|
||||
{
|
||||
let mut table = write_txn.open_table(MELT_QUOTES_TABLE)?;
|
||||
table.remove(quote_id)?;
|
||||
}
|
||||
write_txn.commit()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn add_spent_proof(&self, proof: Proof) -> Result<(), Error> {
|
||||
let db = self.db.lock().await;
|
||||
|
||||
let write_txn = db.begin_write()?;
|
||||
|
||||
{
|
||||
let mut table = write_txn.open_table(SPENT_PROOFS_TABLE)?;
|
||||
table.insert(
|
||||
proof.secret.to_string().as_str(),
|
||||
serde_json::to_string(&proof)?.as_str(),
|
||||
)?;
|
||||
}
|
||||
write_txn.commit()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_spent_proof(&self, secret: &Secret) -> Result<Option<Proof>, Error> {
|
||||
let db = self.db.lock().await;
|
||||
let read_txn = db.begin_read()?;
|
||||
let table = read_txn.open_table(MELT_QUOTES_TABLE)?;
|
||||
|
||||
let quote = table.get(secret.to_string().as_str())?;
|
||||
|
||||
Ok(quote.map(|q| serde_json::from_str(&q.value()).unwrap()))
|
||||
}
|
||||
|
||||
async fn add_pending_proof(&self, proof: Proof) -> Result<(), Error> {
|
||||
let db = self.db.lock().await;
|
||||
|
||||
let write_txn = db.begin_write()?;
|
||||
|
||||
{
|
||||
let mut table = write_txn.open_table(PENDING_PROOFS_TABLE)?;
|
||||
table.insert(
|
||||
proof.secret.to_string().as_str(),
|
||||
serde_json::to_string(&proof)?.as_str(),
|
||||
)?;
|
||||
}
|
||||
write_txn.commit()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_pending_proof(&self, secret: &Secret) -> Result<Option<Proof>, Error> {
|
||||
let db = self.db.lock().await;
|
||||
let read_txn = db.begin_read()?;
|
||||
let table = read_txn.open_table(MELT_QUOTES_TABLE)?;
|
||||
|
||||
let quote = table.get(secret.to_string().as_str())?;
|
||||
|
||||
Ok(quote.map(|q| serde_json::from_str(&q.value()).unwrap()))
|
||||
}
|
||||
|
||||
async fn remove_pending_proof(&self, secret: &Secret) -> Result<(), Error> {
|
||||
let db = self.db.lock().await;
|
||||
|
||||
let write_txn = db.begin_write()?;
|
||||
|
||||
{
|
||||
let mut table = write_txn.open_table(PENDING_PROOFS_TABLE)?;
|
||||
table.remove(secret.to_string().as_str())?;
|
||||
}
|
||||
write_txn.commit()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,9 @@ use crate::Mnemonic;
|
||||
|
||||
mod localstore;
|
||||
|
||||
use localstore::LocalStore;
|
||||
pub use localstore::LocalStore;
|
||||
#[cfg(all(not(target_arch = "wasm32"), feature = "redb"))]
|
||||
pub use localstore::RedbLocalStore;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
@@ -314,10 +316,7 @@ impl<L: LocalStore> Mint<L> {
|
||||
}
|
||||
|
||||
for proof in swap_request.inputs {
|
||||
self.localstore
|
||||
.add_spent_proof(proof.secret.clone(), proof)
|
||||
.await
|
||||
.unwrap();
|
||||
self.localstore.add_spent_proof(proof).await.unwrap();
|
||||
}
|
||||
|
||||
let mut promises = Vec::with_capacity(swap_request.outputs.len());
|
||||
@@ -484,7 +483,7 @@ impl<L: LocalStore> Mint<L> {
|
||||
|
||||
for input in &melt_request.inputs {
|
||||
self.localstore
|
||||
.add_spent_proof(input.secret.clone(), input.clone())
|
||||
.add_spent_proof(input.clone())
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user