mirror of
https://github.com/aljazceru/cdk.git
synced 2025-12-21 06:34:59 +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),
|
Burn(sub_commands::burn::BurnSubCommand),
|
||||||
/// Restore proofs from seed
|
/// Restore proofs from seed
|
||||||
Restore(sub_commands::restore::RestoreSubCommand),
|
Restore(sub_commands::restore::RestoreSubCommand),
|
||||||
|
/// Update Mint Url
|
||||||
|
UpdateMintUrl(sub_commands::update_mint_url::UpdateMintUrlSubCommand),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
@@ -145,5 +147,8 @@ async fn main() -> Result<()> {
|
|||||||
Commands::Restore(sub_command_args) => {
|
Commands::Restore(sub_command_args) => {
|
||||||
sub_commands::restore::restore(wallet, sub_command_args).await
|
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 receive;
|
||||||
pub mod restore;
|
pub mod restore;
|
||||||
pub mod send;
|
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::types::{MeltQuote, MintQuote, ProofInfo};
|
||||||
use cdk::url::UncheckedUrl;
|
use cdk::url::UncheckedUrl;
|
||||||
|
use cdk::util::unix_time;
|
||||||
use redb::{Database, MultimapTableDefinition, ReadableTable, TableDefinition};
|
use redb::{Database, MultimapTableDefinition, ReadableTable, TableDefinition};
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
@@ -116,6 +117,23 @@ impl WalletDatabase for RedbWalletDatabase {
|
|||||||
Ok(())
|
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))]
|
#[instrument(skip(self))]
|
||||||
async fn get_mint(&self, mint_url: UncheckedUrl) -> Result<Option<MintInfo>, Self::Err> {
|
async fn get_mint(&self, mint_url: UncheckedUrl) -> Result<Option<MintInfo>, Self::Err> {
|
||||||
let db = self.db.lock().await;
|
let db = self.db.lock().await;
|
||||||
@@ -152,6 +170,62 @@ impl WalletDatabase for RedbWalletDatabase {
|
|||||||
Ok(mints)
|
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))]
|
#[instrument(skip(self))]
|
||||||
async fn add_mint_keysets(
|
async fn add_mint_keysets(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ use cdk::nuts::{
|
|||||||
};
|
};
|
||||||
use cdk::types::{MeltQuote, MintQuote, ProofInfo};
|
use cdk::types::{MeltQuote, MintQuote, ProofInfo};
|
||||||
use cdk::url::UncheckedUrl;
|
use cdk::url::UncheckedUrl;
|
||||||
|
use cdk::util::unix_time;
|
||||||
use rexie::*;
|
use rexie::*;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
@@ -128,6 +129,24 @@ impl WalletDatabase for RexieWalletDatabase {
|
|||||||
Ok(())
|
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> {
|
async fn get_mint(&self, mint_url: UncheckedUrl) -> Result<Option<MintInfo>, Self::Err> {
|
||||||
let rexie = self.db.lock().await;
|
let rexie = self.db.lock().await;
|
||||||
|
|
||||||
@@ -173,6 +192,55 @@ impl WalletDatabase for RexieWalletDatabase {
|
|||||||
Ok(mints)
|
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(
|
async fn add_mint_keysets(
|
||||||
&self,
|
&self,
|
||||||
mint_url: UncheckedUrl,
|
mint_url: UncheckedUrl,
|
||||||
|
|||||||
@@ -108,6 +108,22 @@ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);
|
|||||||
|
|
||||||
Ok(())
|
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> {
|
async fn get_mint(&self, mint_url: UncheckedUrl) -> Result<Option<MintInfo>, Self::Err> {
|
||||||
let rec = sqlx::query(
|
let rec = sqlx::query(
|
||||||
r#"
|
r#"
|
||||||
@@ -155,6 +171,32 @@ FROM mint
|
|||||||
Ok(mints)
|
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(
|
async fn add_mint_keysets(
|
||||||
&self,
|
&self,
|
||||||
mint_url: UncheckedUrl,
|
mint_url: UncheckedUrl,
|
||||||
|
|||||||
@@ -56,8 +56,14 @@ pub trait WalletDatabase: Debug {
|
|||||||
mint_url: UncheckedUrl,
|
mint_url: UncheckedUrl,
|
||||||
mint_info: Option<MintInfo>,
|
mint_info: Option<MintInfo>,
|
||||||
) -> Result<(), Self::Err>;
|
) -> 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_mint(&self, mint_url: UncheckedUrl) -> Result<Option<MintInfo>, Self::Err>;
|
||||||
async fn get_mints(&self) -> Result<HashMap<UncheckedUrl, 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(
|
async fn add_mint_keysets(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ use crate::nuts::{
|
|||||||
};
|
};
|
||||||
use crate::types::{MeltQuote, MintQuote, ProofInfo};
|
use crate::types::{MeltQuote, MintQuote, ProofInfo};
|
||||||
use crate::url::UncheckedUrl;
|
use crate::url::UncheckedUrl;
|
||||||
|
use crate::util::unix_time;
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone)]
|
#[derive(Default, Debug, Clone)]
|
||||||
pub struct WalletMemoryDatabase {
|
pub struct WalletMemoryDatabase {
|
||||||
@@ -71,6 +72,13 @@ impl WalletDatabase for WalletMemoryDatabase {
|
|||||||
Ok(())
|
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> {
|
async fn get_mint(&self, mint_url: UncheckedUrl) -> Result<Option<MintInfo>, Self::Err> {
|
||||||
Ok(self.mints.read().await.get(&mint_url).cloned().flatten())
|
Ok(self.mints.read().await.get(&mint_url).cloned().flatten())
|
||||||
}
|
}
|
||||||
@@ -79,6 +87,55 @@ impl WalletDatabase for WalletMemoryDatabase {
|
|||||||
Ok(self.mints.read().await.clone())
|
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(
|
async fn add_mint_keysets(
|
||||||
&self,
|
&self,
|
||||||
mint_url: UncheckedUrl,
|
mint_url: UncheckedUrl,
|
||||||
|
|||||||
@@ -204,6 +204,25 @@ impl Wallet {
|
|||||||
Ok(mint_balances)
|
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
|
/// Get unspent proofs for mint
|
||||||
#[instrument(skip(self), fields(mint_url = %mint_url))]
|
#[instrument(skip(self), fields(mint_url = %mint_url))]
|
||||||
pub async fn get_proofs(&self, mint_url: UncheckedUrl) -> Result<Option<Proofs>, Error> {
|
pub async fn get_proofs(&self, mint_url: UncheckedUrl) -> Result<Option<Proofs>, Error> {
|
||||||
|
|||||||
Reference in New Issue
Block a user