mirror of
https://github.com/aljazceru/cdk.git
synced 2025-12-20 22:24:54 +01:00
feat(wallet): update mint url
feat(cli): add change mint
This commit is contained in:
@@ -58,6 +58,8 @@ enum Commands {
|
||||
Burn(sub_commands::burn::BurnSubCommand),
|
||||
/// Restore proofs from seed
|
||||
Restore(sub_commands::restore::RestoreSubCommand),
|
||||
/// Update Mint Url
|
||||
UpdateMintUrl(sub_commands::update_mint_url::UpdateMintUrlSubCommand),
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
@@ -145,5 +147,8 @@ async fn main() -> Result<()> {
|
||||
Commands::Restore(sub_command_args) => {
|
||||
sub_commands::restore::restore(wallet, sub_command_args).await
|
||||
}
|
||||
Commands::UpdateMintUrl(sub_command_args) => {
|
||||
sub_commands::update_mint_url::update_mint_url(wallet, sub_command_args).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,3 +9,4 @@ pub mod pending_mints;
|
||||
pub mod receive;
|
||||
pub mod restore;
|
||||
pub mod send;
|
||||
pub mod update_mint_url;
|
||||
|
||||
30
crates/cdk-cli/src/sub_commands/update_mint_url.rs
Normal file
30
crates/cdk-cli/src/sub_commands/update_mint_url.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
use anyhow::Result;
|
||||
use cdk::url::UncheckedUrl;
|
||||
use cdk::wallet::Wallet;
|
||||
use clap::Args;
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct UpdateMintUrlSubCommand {
|
||||
/// Old Mint Url
|
||||
old_mint_url: UncheckedUrl,
|
||||
/// New Mint Url
|
||||
new_mint_url: UncheckedUrl,
|
||||
}
|
||||
|
||||
pub async fn update_mint_url(
|
||||
wallet: Wallet,
|
||||
sub_command_args: &UpdateMintUrlSubCommand,
|
||||
) -> Result<()> {
|
||||
let UpdateMintUrlSubCommand {
|
||||
old_mint_url,
|
||||
new_mint_url,
|
||||
} = sub_command_args;
|
||||
|
||||
wallet
|
||||
.update_mint_url(old_mint_url.clone(), new_mint_url.clone())
|
||||
.await?;
|
||||
|
||||
println!("Mint Url changed from {} to {}", old_mint_url, new_mint_url);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -11,6 +11,7 @@ use cdk::nuts::{
|
||||
};
|
||||
use cdk::types::{MeltQuote, MintQuote, ProofInfo};
|
||||
use cdk::url::UncheckedUrl;
|
||||
use cdk::util::unix_time;
|
||||
use redb::{Database, MultimapTableDefinition, ReadableTable, TableDefinition};
|
||||
use tokio::sync::Mutex;
|
||||
use tracing::instrument;
|
||||
@@ -116,6 +117,23 @@ impl WalletDatabase for RedbWalletDatabase {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(skip(self))]
|
||||
async fn remove_mint(&self, mint_url: UncheckedUrl) -> Result<(), Self::Err> {
|
||||
let db = self.db.lock().await;
|
||||
|
||||
let write_txn = db.begin_write().map_err(Error::from)?;
|
||||
|
||||
{
|
||||
let mut table = write_txn.open_table(MINTS_TABLE).map_err(Error::from)?;
|
||||
table
|
||||
.remove(mint_url.to_string().as_str())
|
||||
.map_err(Error::from)?;
|
||||
}
|
||||
write_txn.commit().map_err(Error::from)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(skip(self))]
|
||||
async fn get_mint(&self, mint_url: UncheckedUrl) -> Result<Option<MintInfo>, Self::Err> {
|
||||
let db = self.db.lock().await;
|
||||
@@ -152,6 +170,62 @@ impl WalletDatabase for RedbWalletDatabase {
|
||||
Ok(mints)
|
||||
}
|
||||
|
||||
#[instrument(skip(self))]
|
||||
async fn update_mint_url(
|
||||
&self,
|
||||
old_mint_url: UncheckedUrl,
|
||||
new_mint_url: UncheckedUrl,
|
||||
) -> Result<(), Self::Err> {
|
||||
// Update proofs table
|
||||
{
|
||||
let proofs = self
|
||||
.get_proofs(Some(old_mint_url.clone()), None, None, None)
|
||||
.await
|
||||
.map_err(Error::from)?;
|
||||
|
||||
if let Some(proofs) = proofs {
|
||||
// Proofs with new url
|
||||
let updated_proofs: Vec<ProofInfo> = proofs
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|mut p| {
|
||||
p.mint_url = new_mint_url.clone();
|
||||
p
|
||||
})
|
||||
.collect();
|
||||
|
||||
println!("{:?}", updated_proofs);
|
||||
|
||||
self.add_proofs(updated_proofs).await?;
|
||||
}
|
||||
}
|
||||
|
||||
// Update mint quotes
|
||||
{
|
||||
let quotes = self.get_mint_quotes().await?;
|
||||
|
||||
let unix_time = unix_time();
|
||||
|
||||
let quotes: Vec<MintQuote> = quotes
|
||||
.into_iter()
|
||||
.filter_map(|mut q| {
|
||||
if q.expiry < unix_time {
|
||||
q.mint_url = new_mint_url.clone();
|
||||
Some(q)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
for quote in quotes {
|
||||
self.add_mint_quote(quote).await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(skip(self))]
|
||||
async fn add_mint_keysets(
|
||||
&self,
|
||||
|
||||
@@ -9,6 +9,7 @@ use cdk::nuts::{
|
||||
};
|
||||
use cdk::types::{MeltQuote, MintQuote, ProofInfo};
|
||||
use cdk::url::UncheckedUrl;
|
||||
use cdk::util::unix_time;
|
||||
use rexie::*;
|
||||
use thiserror::Error;
|
||||
use tokio::sync::Mutex;
|
||||
@@ -128,6 +129,24 @@ impl WalletDatabase for RexieWalletDatabase {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn remove_mint(&self, mint_url: UncheckedUrl) -> Result<(), Self::Err> {
|
||||
let rexie = self.db.lock().await;
|
||||
|
||||
let transaction = rexie
|
||||
.transaction(&[MINTS], TransactionMode::ReadWrite)
|
||||
.map_err(Error::from)?;
|
||||
|
||||
let mints_store = transaction.store(MINTS).map_err(Error::from)?;
|
||||
|
||||
let mint_url = serde_wasm_bindgen::to_value(&mint_url).map_err(Error::from)?;
|
||||
|
||||
mints_store.delete(&mint_url).await.map_err(Error::from)?;
|
||||
|
||||
transaction.done().await.map_err(Error::from)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_mint(&self, mint_url: UncheckedUrl) -> Result<Option<MintInfo>, Self::Err> {
|
||||
let rexie = self.db.lock().await;
|
||||
|
||||
@@ -173,6 +192,55 @@ impl WalletDatabase for RexieWalletDatabase {
|
||||
Ok(mints)
|
||||
}
|
||||
|
||||
async fn update_mint_url(
|
||||
&self,
|
||||
old_mint_url: UncheckedUrl,
|
||||
new_mint_url: UncheckedUrl,
|
||||
) -> Result<(), Self::Err> {
|
||||
let proofs = self
|
||||
.get_proofs(Some(old_mint_url), None, None, None)
|
||||
.await
|
||||
.map_err(Error::from)?;
|
||||
|
||||
if let Some(proofs) = proofs {
|
||||
let updated_proofs: Vec<ProofInfo> = proofs
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|mut p| {
|
||||
p.mint_url = new_mint_url.clone();
|
||||
p
|
||||
})
|
||||
.collect();
|
||||
|
||||
self.add_proofs(updated_proofs).await?;
|
||||
}
|
||||
|
||||
// Update mint quotes
|
||||
{
|
||||
let quotes = self.get_mint_quotes().await?;
|
||||
|
||||
let unix_time = unix_time();
|
||||
|
||||
let quotes: Vec<MintQuote> = quotes
|
||||
.into_iter()
|
||||
.filter_map(|mut q| {
|
||||
if q.expiry < unix_time {
|
||||
q.mint_url = new_mint_url.clone();
|
||||
Some(q)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
for quote in quotes {
|
||||
self.add_mint_quote(quote).await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn add_mint_keysets(
|
||||
&self,
|
||||
mint_url: UncheckedUrl,
|
||||
|
||||
@@ -108,6 +108,22 @@ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn remove_mint(&self, mint_url: UncheckedUrl) -> Result<(), Self::Err> {
|
||||
sqlx::query(
|
||||
r#"
|
||||
DELETE FROM mint
|
||||
WHERE mint_url=?
|
||||
"#,
|
||||
)
|
||||
.bind(mint_url.to_string())
|
||||
.execute(&self.pool)
|
||||
.await
|
||||
.map_err(Error::from)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_mint(&self, mint_url: UncheckedUrl) -> Result<Option<MintInfo>, Self::Err> {
|
||||
let rec = sqlx::query(
|
||||
r#"
|
||||
@@ -155,6 +171,32 @@ FROM mint
|
||||
Ok(mints)
|
||||
}
|
||||
|
||||
async fn update_mint_url(
|
||||
&self,
|
||||
old_mint_url: UncheckedUrl,
|
||||
new_mint_url: UncheckedUrl,
|
||||
) -> Result<(), Self::Err> {
|
||||
let tables = ["mint_quote", "proof"];
|
||||
for table in &tables {
|
||||
let query = format!(
|
||||
r#"
|
||||
UPDATE {}
|
||||
SET mint_url = ?
|
||||
WHERE mint_url = ?;
|
||||
"#,
|
||||
table
|
||||
);
|
||||
|
||||
sqlx::query(&query)
|
||||
.bind(new_mint_url.to_string())
|
||||
.bind(old_mint_url.to_string())
|
||||
.execute(&self.pool)
|
||||
.await
|
||||
.map_err(Error::from)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn add_mint_keysets(
|
||||
&self,
|
||||
mint_url: UncheckedUrl,
|
||||
|
||||
@@ -56,8 +56,14 @@ pub trait WalletDatabase: Debug {
|
||||
mint_url: UncheckedUrl,
|
||||
mint_info: Option<MintInfo>,
|
||||
) -> Result<(), Self::Err>;
|
||||
async fn remove_mint(&self, mint_url: UncheckedUrl) -> Result<(), Self::Err>;
|
||||
async fn get_mint(&self, mint_url: UncheckedUrl) -> Result<Option<MintInfo>, Self::Err>;
|
||||
async fn get_mints(&self) -> Result<HashMap<UncheckedUrl, Option<MintInfo>>, Self::Err>;
|
||||
async fn update_mint_url(
|
||||
&self,
|
||||
old_mint_url: UncheckedUrl,
|
||||
new_mint_url: UncheckedUrl,
|
||||
) -> Result<(), Self::Err>;
|
||||
|
||||
async fn add_mint_keysets(
|
||||
&self,
|
||||
|
||||
@@ -13,6 +13,7 @@ use crate::nuts::{
|
||||
};
|
||||
use crate::types::{MeltQuote, MintQuote, ProofInfo};
|
||||
use crate::url::UncheckedUrl;
|
||||
use crate::util::unix_time;
|
||||
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct WalletMemoryDatabase {
|
||||
@@ -71,6 +72,13 @@ impl WalletDatabase for WalletMemoryDatabase {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn remove_mint(&self, mint_url: UncheckedUrl) -> Result<(), Self::Err> {
|
||||
let mut mints = self.mints.write().await;
|
||||
mints.remove(&mint_url);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_mint(&self, mint_url: UncheckedUrl) -> Result<Option<MintInfo>, Self::Err> {
|
||||
Ok(self.mints.read().await.get(&mint_url).cloned().flatten())
|
||||
}
|
||||
@@ -79,6 +87,55 @@ impl WalletDatabase for WalletMemoryDatabase {
|
||||
Ok(self.mints.read().await.clone())
|
||||
}
|
||||
|
||||
async fn update_mint_url(
|
||||
&self,
|
||||
old_mint_url: UncheckedUrl,
|
||||
new_mint_url: UncheckedUrl,
|
||||
) -> Result<(), Self::Err> {
|
||||
let proofs = self
|
||||
.get_proofs(Some(old_mint_url), None, None, None)
|
||||
.await
|
||||
.map_err(Error::from)?;
|
||||
|
||||
if let Some(proofs) = proofs {
|
||||
let updated_proofs: Vec<ProofInfo> = proofs
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|mut p| {
|
||||
p.mint_url = new_mint_url.clone();
|
||||
p
|
||||
})
|
||||
.collect();
|
||||
|
||||
self.add_proofs(updated_proofs).await?;
|
||||
}
|
||||
|
||||
// Update mint quotes
|
||||
{
|
||||
let quotes = self.get_mint_quotes().await?;
|
||||
|
||||
let unix_time = unix_time();
|
||||
|
||||
let quotes: Vec<MintQuote> = quotes
|
||||
.into_iter()
|
||||
.filter_map(|mut q| {
|
||||
if q.expiry < unix_time {
|
||||
q.mint_url = new_mint_url.clone();
|
||||
Some(q)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
for quote in quotes {
|
||||
self.add_mint_quote(quote).await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn add_mint_keysets(
|
||||
&self,
|
||||
mint_url: UncheckedUrl,
|
||||
|
||||
@@ -204,6 +204,25 @@ impl Wallet {
|
||||
Ok(mint_balances)
|
||||
}
|
||||
|
||||
/// Update Mint information and related entries in the event a mint changes its URL
|
||||
#[instrument(skip(self), fields(old_mint_url = %old_mint_url, new_mint_url = %new_mint_url))]
|
||||
pub async fn update_mint_url(
|
||||
&self,
|
||||
old_mint_url: UncheckedUrl,
|
||||
new_mint_url: UncheckedUrl,
|
||||
) -> Result<(), Error> {
|
||||
// Adding the new url as a new mint will get the current keysets of the mint
|
||||
self.add_mint(new_mint_url.clone()).await?;
|
||||
|
||||
// Where the mint_url is in the database it must be updated
|
||||
self.localstore
|
||||
.update_mint_url(old_mint_url.clone(), new_mint_url)
|
||||
.await?;
|
||||
|
||||
self.localstore.remove_mint(old_mint_url).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get unspent proofs for mint
|
||||
#[instrument(skip(self), fields(mint_url = %mint_url))]
|
||||
pub async fn get_proofs(&self, mint_url: UncheckedUrl) -> Result<Option<Proofs>, Error> {
|
||||
|
||||
Reference in New Issue
Block a user