mirror of
https://github.com/aljazceru/cdk.git
synced 2025-12-23 15:44:50 +01:00
Merge pull request #901 from thesimplekid/refresh_keys
feat: refactor wallet keyset management for better clarity
This commit is contained in:
@@ -497,6 +497,28 @@ pub struct KeySetInfo {
|
|||||||
pub final_expiry: Option<u64>,
|
pub final_expiry: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// List of [KeySetInfo]
|
||||||
|
pub type KeySetInfos = Vec<KeySetInfo>;
|
||||||
|
|
||||||
|
/// Utility methods for [KeySetInfos]
|
||||||
|
pub trait KeySetInfosMethods {
|
||||||
|
/// Filter for active keysets
|
||||||
|
fn active(&self) -> impl Iterator<Item = &KeySetInfo> + '_;
|
||||||
|
|
||||||
|
/// Filter keysets for specific unit
|
||||||
|
fn unit(&self, unit: CurrencyUnit) -> impl Iterator<Item = &KeySetInfo> + '_;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KeySetInfosMethods for KeySetInfos {
|
||||||
|
fn active(&self) -> impl Iterator<Item = &KeySetInfo> + '_ {
|
||||||
|
self.iter().filter(|k| k.active)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unit(&self, unit: CurrencyUnit) -> impl Iterator<Item = &KeySetInfo> + '_ {
|
||||||
|
self.iter().filter(move |k| k.unit == unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn deserialize_input_fee_ppk<'de, D>(deserializer: D) -> Result<u64, D::Error>
|
fn deserialize_input_fee_ppk<'de, D>(deserializer: D) -> Result<u64, D::Error>
|
||||||
where
|
where
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ pub async fn pay_request(
|
|||||||
.await?
|
.await?
|
||||||
{
|
{
|
||||||
Some(keysets_info) => keysets_info,
|
Some(keysets_info) => keysets_info,
|
||||||
None => matching_wallet.get_mint_keysets().await?, // Hit the keysets endpoint if we don't have the keysets for this Mint
|
None => matching_wallet.load_mint_keysets().await?, // Hit the keysets endpoint if we don't have the keysets for this Mint
|
||||||
};
|
};
|
||||||
let proofs = token.proofs(&keysets_info)?;
|
let proofs = token.proofs(&keysets_info)?;
|
||||||
|
|
||||||
|
|||||||
@@ -277,7 +277,7 @@ async fn test_regtest_bolt12_mint_extra() -> Result<()> {
|
|||||||
assert_eq!(state.amount_paid, Amount::ZERO);
|
assert_eq!(state.amount_paid, Amount::ZERO);
|
||||||
assert_eq!(state.amount_issued, Amount::ZERO);
|
assert_eq!(state.amount_issued, Amount::ZERO);
|
||||||
|
|
||||||
let active_keyset_id = wallet.get_active_mint_keyset().await?.id;
|
let active_keyset_id = wallet.fetch_active_keyset().await?.id;
|
||||||
|
|
||||||
let pay_amount_msats = 10_000;
|
let pay_amount_msats = 10_000;
|
||||||
|
|
||||||
|
|||||||
@@ -381,7 +381,7 @@ async fn test_fake_melt_change_in_quote() {
|
|||||||
|
|
||||||
let melt_quote = wallet.melt_quote(invoice.to_string(), None).await.unwrap();
|
let melt_quote = wallet.melt_quote(invoice.to_string(), None).await.unwrap();
|
||||||
|
|
||||||
let keyset = wallet.get_active_mint_keyset().await.unwrap();
|
let keyset = wallet.fetch_active_keyset().await.unwrap();
|
||||||
|
|
||||||
let premint_secrets =
|
let premint_secrets =
|
||||||
PreMintSecrets::random(keyset.id, 100.into(), &SplitTarget::default()).unwrap();
|
PreMintSecrets::random(keyset.id, 100.into(), &SplitTarget::default()).unwrap();
|
||||||
@@ -489,7 +489,7 @@ async fn test_fake_mint_without_witness() {
|
|||||||
|
|
||||||
let http_client = HttpClient::new(MINT_URL.parse().unwrap(), None);
|
let http_client = HttpClient::new(MINT_URL.parse().unwrap(), None);
|
||||||
|
|
||||||
let active_keyset_id = wallet.get_active_mint_keyset().await.unwrap().id;
|
let active_keyset_id = wallet.fetch_active_keyset().await.unwrap().id;
|
||||||
|
|
||||||
let premint_secrets =
|
let premint_secrets =
|
||||||
PreMintSecrets::random(active_keyset_id, 100.into(), &SplitTarget::default()).unwrap();
|
PreMintSecrets::random(active_keyset_id, 100.into(), &SplitTarget::default()).unwrap();
|
||||||
@@ -529,7 +529,7 @@ async fn test_fake_mint_with_wrong_witness() {
|
|||||||
|
|
||||||
let http_client = HttpClient::new(MINT_URL.parse().unwrap(), None);
|
let http_client = HttpClient::new(MINT_URL.parse().unwrap(), None);
|
||||||
|
|
||||||
let active_keyset_id = wallet.get_active_mint_keyset().await.unwrap().id;
|
let active_keyset_id = wallet.fetch_active_keyset().await.unwrap().id;
|
||||||
|
|
||||||
let premint_secrets =
|
let premint_secrets =
|
||||||
PreMintSecrets::random(active_keyset_id, 100.into(), &SplitTarget::default()).unwrap();
|
PreMintSecrets::random(active_keyset_id, 100.into(), &SplitTarget::default()).unwrap();
|
||||||
@@ -573,7 +573,7 @@ async fn test_fake_mint_inflated() {
|
|||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let active_keyset_id = wallet.get_active_mint_keyset().await.unwrap().id;
|
let active_keyset_id = wallet.fetch_active_keyset().await.unwrap().id;
|
||||||
|
|
||||||
let pre_mint =
|
let pre_mint =
|
||||||
PreMintSecrets::random(active_keyset_id, 500.into(), &SplitTarget::None).unwrap();
|
PreMintSecrets::random(active_keyset_id, 500.into(), &SplitTarget::None).unwrap();
|
||||||
@@ -631,7 +631,7 @@ async fn test_fake_mint_multiple_units() {
|
|||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let active_keyset_id = wallet.get_active_mint_keyset().await.unwrap().id;
|
let active_keyset_id = wallet.fetch_active_keyset().await.unwrap().id;
|
||||||
|
|
||||||
let pre_mint = PreMintSecrets::random(active_keyset_id, 50.into(), &SplitTarget::None).unwrap();
|
let pre_mint = PreMintSecrets::random(active_keyset_id, 50.into(), &SplitTarget::None).unwrap();
|
||||||
|
|
||||||
@@ -644,7 +644,7 @@ async fn test_fake_mint_multiple_units() {
|
|||||||
)
|
)
|
||||||
.expect("failed to create new wallet");
|
.expect("failed to create new wallet");
|
||||||
|
|
||||||
let active_keyset_id = wallet_usd.get_active_mint_keyset().await.unwrap().id;
|
let active_keyset_id = wallet_usd.fetch_active_keyset().await.unwrap().id;
|
||||||
|
|
||||||
let usd_pre_mint =
|
let usd_pre_mint =
|
||||||
PreMintSecrets::random(active_keyset_id, 50.into(), &SplitTarget::None).unwrap();
|
PreMintSecrets::random(active_keyset_id, 50.into(), &SplitTarget::None).unwrap();
|
||||||
@@ -733,7 +733,7 @@ async fn test_fake_mint_multiple_unit_swap() {
|
|||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let active_keyset_id = wallet.get_active_mint_keyset().await.unwrap().id;
|
let active_keyset_id = wallet.fetch_active_keyset().await.unwrap().id;
|
||||||
|
|
||||||
{
|
{
|
||||||
let inputs: Proofs = vec![
|
let inputs: Proofs = vec![
|
||||||
@@ -767,7 +767,7 @@ async fn test_fake_mint_multiple_unit_swap() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let usd_active_keyset_id = wallet_usd.get_active_mint_keyset().await.unwrap().id;
|
let usd_active_keyset_id = wallet_usd.fetch_active_keyset().await.unwrap().id;
|
||||||
let inputs: Proofs = proofs.into_iter().take(2).collect();
|
let inputs: Proofs = proofs.into_iter().take(2).collect();
|
||||||
|
|
||||||
let total_inputs = inputs.total_amount().unwrap();
|
let total_inputs = inputs.total_amount().unwrap();
|
||||||
@@ -883,8 +883,8 @@ async fn test_fake_mint_multiple_unit_melt() {
|
|||||||
let input_amount: u64 = inputs.total_amount().unwrap().into();
|
let input_amount: u64 = inputs.total_amount().unwrap().into();
|
||||||
|
|
||||||
let invoice = create_fake_invoice((input_amount - 1) * 1000, "".to_string());
|
let invoice = create_fake_invoice((input_amount - 1) * 1000, "".to_string());
|
||||||
let active_keyset_id = wallet.get_active_mint_keyset().await.unwrap().id;
|
let active_keyset_id = wallet.fetch_active_keyset().await.unwrap().id;
|
||||||
let usd_active_keyset_id = wallet_usd.get_active_mint_keyset().await.unwrap().id;
|
let usd_active_keyset_id = wallet_usd.fetch_active_keyset().await.unwrap().id;
|
||||||
|
|
||||||
let usd_pre_mint = PreMintSecrets::random(
|
let usd_pre_mint = PreMintSecrets::random(
|
||||||
usd_active_keyset_id,
|
usd_active_keyset_id,
|
||||||
@@ -952,7 +952,7 @@ async fn test_fake_mint_input_output_mismatch() {
|
|||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
.expect("failed to create new usd wallet");
|
.expect("failed to create new usd wallet");
|
||||||
let usd_active_keyset_id = wallet_usd.get_active_mint_keyset().await.unwrap().id;
|
let usd_active_keyset_id = wallet_usd.fetch_active_keyset().await.unwrap().id;
|
||||||
|
|
||||||
let inputs = proofs;
|
let inputs = proofs;
|
||||||
|
|
||||||
@@ -1001,7 +1001,7 @@ async fn test_fake_mint_swap_inflated() {
|
|||||||
.mint(&mint_quote.id, SplitTarget::None, None)
|
.mint(&mint_quote.id, SplitTarget::None, None)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let active_keyset_id = wallet.get_active_mint_keyset().await.unwrap().id;
|
let active_keyset_id = wallet.fetch_active_keyset().await.unwrap().id;
|
||||||
let pre_mint =
|
let pre_mint =
|
||||||
PreMintSecrets::random(active_keyset_id, 101.into(), &SplitTarget::None).unwrap();
|
PreMintSecrets::random(active_keyset_id, 101.into(), &SplitTarget::None).unwrap();
|
||||||
|
|
||||||
@@ -1045,7 +1045,7 @@ async fn test_fake_mint_swap_spend_after_fail() {
|
|||||||
.mint(&mint_quote.id, SplitTarget::None, None)
|
.mint(&mint_quote.id, SplitTarget::None, None)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let active_keyset_id = wallet.get_active_mint_keyset().await.unwrap().id;
|
let active_keyset_id = wallet.fetch_active_keyset().await.unwrap().id;
|
||||||
|
|
||||||
let pre_mint =
|
let pre_mint =
|
||||||
PreMintSecrets::random(active_keyset_id, 100.into(), &SplitTarget::None).unwrap();
|
PreMintSecrets::random(active_keyset_id, 100.into(), &SplitTarget::None).unwrap();
|
||||||
@@ -1116,7 +1116,7 @@ async fn test_fake_mint_melt_spend_after_fail() {
|
|||||||
.mint(&mint_quote.id, SplitTarget::None, None)
|
.mint(&mint_quote.id, SplitTarget::None, None)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let active_keyset_id = wallet.get_active_mint_keyset().await.unwrap().id;
|
let active_keyset_id = wallet.fetch_active_keyset().await.unwrap().id;
|
||||||
|
|
||||||
let pre_mint =
|
let pre_mint =
|
||||||
PreMintSecrets::random(active_keyset_id, 100.into(), &SplitTarget::None).unwrap();
|
PreMintSecrets::random(active_keyset_id, 100.into(), &SplitTarget::None).unwrap();
|
||||||
@@ -1189,7 +1189,7 @@ async fn test_fake_mint_duplicate_proofs_swap() {
|
|||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let active_keyset_id = wallet.get_active_mint_keyset().await.unwrap().id;
|
let active_keyset_id = wallet.fetch_active_keyset().await.unwrap().id;
|
||||||
|
|
||||||
let inputs = vec![proofs[0].clone(), proofs[0].clone()];
|
let inputs = vec![proofs[0].clone(), proofs[0].clone()];
|
||||||
|
|
||||||
|
|||||||
@@ -358,7 +358,7 @@ async fn test_fake_melt_change_in_quote() {
|
|||||||
|
|
||||||
let melt_quote = wallet.melt_quote(invoice.to_string(), None).await.unwrap();
|
let melt_quote = wallet.melt_quote(invoice.to_string(), None).await.unwrap();
|
||||||
|
|
||||||
let keyset = wallet.get_active_mint_keyset().await.unwrap();
|
let keyset = wallet.fetch_active_keyset().await.unwrap();
|
||||||
|
|
||||||
let premint_secrets =
|
let premint_secrets =
|
||||||
PreMintSecrets::random(keyset.id, 100.into(), &SplitTarget::default()).unwrap();
|
PreMintSecrets::random(keyset.id, 100.into(), &SplitTarget::default()).unwrap();
|
||||||
|
|||||||
@@ -308,7 +308,7 @@ async fn test_cached_mint() {
|
|||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let active_keyset_id = wallet.get_active_mint_keyset().await.unwrap().id;
|
let active_keyset_id = wallet.fetch_active_keyset().await.unwrap().id;
|
||||||
let http_client = HttpClient::new(get_mint_url_from_env().parse().unwrap(), None);
|
let http_client = HttpClient::new(get_mint_url_from_env().parse().unwrap(), None);
|
||||||
let premint_secrets =
|
let premint_secrets =
|
||||||
PreMintSecrets::random(active_keyset_id, 100.into(), &SplitTarget::default()).unwrap();
|
PreMintSecrets::random(active_keyset_id, 100.into(), &SplitTarget::default()).unwrap();
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use cdk::nuts::nut00::ProofsMethods;
|
|||||||
use cdk::nuts::{CurrencyUnit, MintQuoteState, NotificationPayload};
|
use cdk::nuts::{CurrencyUnit, MintQuoteState, NotificationPayload};
|
||||||
use cdk::wallet::{Wallet, WalletSubscription};
|
use cdk::wallet::{Wallet, WalletSubscription};
|
||||||
use cdk::Amount;
|
use cdk::Amount;
|
||||||
|
use cdk_common::nut02::KeySetInfosMethods;
|
||||||
use cdk_sqlite::wallet::memory;
|
use cdk_sqlite::wallet::memory;
|
||||||
use rand::random;
|
use rand::random;
|
||||||
|
|
||||||
@@ -62,9 +63,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
// Select proofs to send
|
// Select proofs to send
|
||||||
let amount = Amount::from(64);
|
let amount = Amount::from(64);
|
||||||
let active_keyset_ids = wallet
|
let active_keyset_ids = wallet
|
||||||
.get_active_mint_keysets()
|
.refresh_keysets()
|
||||||
.await?
|
.await?
|
||||||
.into_iter()
|
.active()
|
||||||
.map(|keyset| keyset.id)
|
.map(|keyset| keyset.id)
|
||||||
.collect();
|
.collect();
|
||||||
let selected =
|
let selected =
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use cdk_common::database::{self, WalletDatabase};
|
use cdk_common::database::{self, WalletDatabase};
|
||||||
use cdk_common::mint_url::MintUrl;
|
use cdk_common::mint_url::MintUrl;
|
||||||
|
use cdk_common::nut02::KeySetInfosMethods;
|
||||||
use cdk_common::{AuthProof, Id, Keys, MintInfo};
|
use cdk_common::{AuthProof, Id, Keys, MintInfo};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
@@ -167,12 +168,12 @@ impl AuthWallet {
|
|||||||
self.client.get_mint_info().await.map(Some).or(Ok(None))
|
self.client.get_mint_info().await.map(Some).or(Ok(None))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get keys for mint keyset
|
/// Fetch keys for mint keyset
|
||||||
///
|
///
|
||||||
/// Selected keys from localstore if they are already known
|
/// Returns keys from local database if they are already stored.
|
||||||
/// If they are not known queries mint for keyset id and stores the [`Keys`]
|
/// If keys are not found locally, goes online to query the mint for the keyset and stores the [`Keys`] in local database.
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip(self))]
|
||||||
pub async fn get_keyset_keys(&self, keyset_id: Id) -> Result<Keys, Error> {
|
pub async fn load_keyset_keys(&self, keyset_id: Id) -> Result<Keys, Error> {
|
||||||
let keys = if let Some(keys) = self.localstore.get_keys(&keyset_id).await? {
|
let keys = if let Some(keys) = self.localstore.get_keys(&keyset_id).await? {
|
||||||
keys
|
keys
|
||||||
} else {
|
} else {
|
||||||
@@ -188,62 +189,88 @@ impl AuthWallet {
|
|||||||
Ok(keys)
|
Ok(keys)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get active keyset for mint
|
/// Get blind auth keysets from local database or go online if missing
|
||||||
///
|
///
|
||||||
/// Queries mint for current keysets then gets [`Keys`] for any unknown
|
/// First checks the local database for cached blind auth keysets. If keysets are not found locally,
|
||||||
/// keysets
|
/// goes online to refresh keysets from the mint and updates the local database.
|
||||||
|
/// This is the main method for getting auth keysets in operations that can work offline
|
||||||
|
/// but will fall back to online if needed.
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip(self))]
|
||||||
pub async fn get_active_mint_blind_auth_keysets(&self) -> Result<Vec<KeySetInfo>, Error> {
|
pub async fn load_mint_keysets(&self) -> Result<Vec<KeySetInfo>, Error> {
|
||||||
let keysets = self.client.get_mint_blind_auth_keysets().await?;
|
|
||||||
let keysets = keysets.keysets;
|
|
||||||
|
|
||||||
self.localstore
|
|
||||||
.add_mint_keysets(self.mint_url.clone(), keysets.clone())
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let active_keysets = keysets
|
|
||||||
.clone()
|
|
||||||
.into_iter()
|
|
||||||
.filter(|k| k.unit == CurrencyUnit::Auth)
|
|
||||||
.collect::<Vec<KeySetInfo>>();
|
|
||||||
|
|
||||||
match self
|
match self
|
||||||
.localstore
|
.localstore
|
||||||
.get_mint_keysets(self.mint_url.clone())
|
.get_mint_keysets(self.mint_url.clone())
|
||||||
.await?
|
.await?
|
||||||
{
|
{
|
||||||
Some(known_keysets) => {
|
Some(keysets_info) => {
|
||||||
let unknown_keysets: Vec<&KeySetInfo> = keysets
|
let auth_keysets: Vec<KeySetInfo> =
|
||||||
.iter()
|
keysets_info.unit(CurrencyUnit::Sat).cloned().collect();
|
||||||
.filter(|k| known_keysets.contains(k))
|
if auth_keysets.is_empty() {
|
||||||
.collect();
|
// If we don't have any auth keysets, fetch them from the mint
|
||||||
|
let keysets = self.refresh_keysets().await?;
|
||||||
for keyset in unknown_keysets {
|
Ok(keysets)
|
||||||
self.get_keyset_keys(keyset.id).await?;
|
} else {
|
||||||
|
Ok(auth_keysets)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
for keyset in keysets {
|
// If we don't have any keysets, fetch them from the mint
|
||||||
self.get_keyset_keys(keyset.id).await?;
|
let keysets = self.refresh_keysets().await?;
|
||||||
|
Ok(keysets)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(active_keysets)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get active keyset for mint
|
/// Refresh blind auth keysets by fetching the latest from mint - always goes online
|
||||||
///
|
///
|
||||||
/// Queries mint for current keysets then gets [`Keys`] for any unknown
|
/// This method always goes online to fetch the latest blind auth keyset information from the mint.
|
||||||
/// keysets
|
/// It updates the local database with the fetched keysets and ensures we have keys for all keysets.
|
||||||
|
/// Returns only the keysets with Auth currency unit. This is used when operations need the most
|
||||||
|
/// up-to-date keyset information and are willing to go online.
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip(self))]
|
||||||
pub async fn get_active_mint_blind_auth_keyset(&self) -> Result<KeySetInfo, Error> {
|
pub async fn refresh_keysets(&self) -> Result<Vec<KeySetInfo>, Error> {
|
||||||
let active_keysets = self.get_active_mint_blind_auth_keysets().await?;
|
let keysets_response = self.client.get_mint_blind_auth_keysets().await?;
|
||||||
|
let keysets = keysets_response.keysets;
|
||||||
|
|
||||||
let keyset = active_keysets.first().ok_or(Error::NoActiveKeyset)?;
|
// Update local store with keysets
|
||||||
|
self.localstore
|
||||||
|
.add_mint_keysets(self.mint_url.clone(), keysets.clone())
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Filter for auth keysets
|
||||||
|
let auth_keysets = keysets
|
||||||
|
.clone()
|
||||||
|
.into_iter()
|
||||||
|
.filter(|k| k.unit == CurrencyUnit::Auth)
|
||||||
|
.collect::<Vec<KeySetInfo>>();
|
||||||
|
|
||||||
|
// Ensure we have keys for all auth keysets
|
||||||
|
for keyset in &auth_keysets {
|
||||||
|
if self.localstore.get_keys(&keyset.id).await?.is_none() {
|
||||||
|
tracing::debug!("Fetching missing keys for auth keyset {}", keyset.id);
|
||||||
|
self.load_keyset_keys(keyset.id).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(auth_keysets)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the first active blind auth keyset - always goes online
|
||||||
|
///
|
||||||
|
/// This method always goes online to refresh keysets from the mint and then returns
|
||||||
|
/// the first active keyset found. Use this when you need the most up-to-date
|
||||||
|
/// keyset information for blind auth operations.
|
||||||
|
#[instrument(skip(self))]
|
||||||
|
pub async fn fetch_active_keyset(&self) -> Result<KeySetInfo, Error> {
|
||||||
|
let auth_keysets = self.refresh_keysets().await?;
|
||||||
|
let keyset = auth_keysets.first().ok_or(Error::NoActiveKeyset)?;
|
||||||
Ok(keyset.clone())
|
Ok(keyset.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get unspent proofs for mint
|
/// Get unspent auth proofs from local database only - offline operation
|
||||||
|
///
|
||||||
|
/// Returns auth proofs from the local database that are in the Unspent state.
|
||||||
|
/// This is an offline operation that does not contact the mint.
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip(self))]
|
||||||
pub async fn get_unspent_auth_proofs(&self) -> Result<Vec<AuthProof>, Error> {
|
pub async fn get_unspent_auth_proofs(&self) -> Result<Vec<AuthProof>, Error> {
|
||||||
Ok(self
|
Ok(self
|
||||||
@@ -334,9 +361,6 @@ impl AuthWallet {
|
|||||||
|
|
||||||
let auth_token = self.client.get_auth_token().await?;
|
let auth_token = self.client.get_auth_token().await?;
|
||||||
|
|
||||||
let active_keyset_id = self.get_active_mint_blind_auth_keysets().await?;
|
|
||||||
tracing::debug!("Active ketset: {:?}", active_keyset_id);
|
|
||||||
|
|
||||||
match &auth_token {
|
match &auth_token {
|
||||||
AuthToken::ClearAuth(cat) => {
|
AuthToken::ClearAuth(cat) => {
|
||||||
if cat.is_empty() {
|
if cat.is_empty() {
|
||||||
@@ -369,7 +393,7 @@ impl AuthWallet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let active_keyset_id = self.get_active_mint_blind_auth_keyset().await?.id;
|
let active_keyset_id = self.fetch_active_keyset().await?.id;
|
||||||
|
|
||||||
let premint_secrets =
|
let premint_secrets =
|
||||||
PreMintSecrets::random(active_keyset_id, amount, &SplitTarget::Value(1.into()))?;
|
PreMintSecrets::random(active_keyset_id, amount, &SplitTarget::Value(1.into()))?;
|
||||||
@@ -380,13 +404,13 @@ impl AuthWallet {
|
|||||||
|
|
||||||
let mint_res = self.client.post_mint_blind_auth(request).await?;
|
let mint_res = self.client.post_mint_blind_auth(request).await?;
|
||||||
|
|
||||||
let keys = self.get_keyset_keys(active_keyset_id).await?;
|
let keys = self.load_keyset_keys(active_keyset_id).await?;
|
||||||
|
|
||||||
// Verify the signature DLEQ is valid
|
// Verify the signature DLEQ is valid
|
||||||
{
|
{
|
||||||
assert!(mint_res.signatures.len() == premint_secrets.secrets.len());
|
assert!(mint_res.signatures.len() == premint_secrets.secrets.len());
|
||||||
for (sig, premint) in mint_res.signatures.iter().zip(&premint_secrets.secrets) {
|
for (sig, premint) in mint_res.signatures.iter().zip(&premint_secrets.secrets) {
|
||||||
let keys = self.get_keyset_keys(sig.keyset_id).await?;
|
let keys = self.load_keyset_keys(sig.keyset_id).await?;
|
||||||
let key = keys.amount_key(sig.amount).ok_or(Error::AmountKey)?;
|
let key = keys.amount_key(sig.amount).ok_or(Error::AmountKey)?;
|
||||||
match sig.verify_dleq(key, premint.blinded_message.blinded_secret) {
|
match sig.verify_dleq(key, premint.blinded_message.blinded_secret) {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
|
|||||||
@@ -227,7 +227,7 @@ impl Wallet {
|
|||||||
tracing::warn!("Attempting to mint with expired quote.");
|
tracing::warn!("Attempting to mint with expired quote.");
|
||||||
}
|
}
|
||||||
|
|
||||||
let active_keyset_id = self.get_active_mint_keyset().await?.id;
|
let active_keyset_id = self.fetch_active_keyset().await?.id;
|
||||||
|
|
||||||
let count = self
|
let count = self
|
||||||
.localstore
|
.localstore
|
||||||
@@ -264,12 +264,12 @@ impl Wallet {
|
|||||||
|
|
||||||
let mint_res = self.client.post_mint(request).await?;
|
let mint_res = self.client.post_mint(request).await?;
|
||||||
|
|
||||||
let keys = self.get_keyset_keys(active_keyset_id).await?;
|
let keys = self.fetch_keyset_keys(active_keyset_id).await?;
|
||||||
|
|
||||||
// Verify the signature DLEQ is valid
|
// Verify the signature DLEQ is valid
|
||||||
{
|
{
|
||||||
for (sig, premint) in mint_res.signatures.iter().zip(&premint_secrets.secrets) {
|
for (sig, premint) in mint_res.signatures.iter().zip(&premint_secrets.secrets) {
|
||||||
let keys = self.get_keyset_keys(sig.keyset_id).await?;
|
let keys = self.fetch_keyset_keys(sig.keyset_id).await?;
|
||||||
let key = keys.amount_key(sig.amount).ok_or(Error::AmountKey)?;
|
let key = keys.amount_key(sig.amount).ok_or(Error::AmountKey)?;
|
||||||
match sig.verify_dleq(key, premint.blinded_message.blinded_secret) {
|
match sig.verify_dleq(key, premint.blinded_message.blinded_secret) {
|
||||||
Ok(_) | Err(nut12::Error::MissingDleqProof) => (),
|
Ok(_) | Err(nut12::Error::MissingDleqProof) => (),
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ impl Wallet {
|
|||||||
return Err(Error::UnknownQuote);
|
return Err(Error::UnknownQuote);
|
||||||
};
|
};
|
||||||
|
|
||||||
let active_keyset_id = self.get_active_mint_keyset().await?.id;
|
let active_keyset_id = self.fetch_active_keyset().await?.id;
|
||||||
|
|
||||||
let count = self
|
let count = self
|
||||||
.localstore
|
.localstore
|
||||||
@@ -161,12 +161,12 @@ impl Wallet {
|
|||||||
|
|
||||||
let mint_res = self.client.post_mint(request).await?;
|
let mint_res = self.client.post_mint(request).await?;
|
||||||
|
|
||||||
let keys = self.get_keyset_keys(active_keyset_id).await?;
|
let keys = self.fetch_keyset_keys(active_keyset_id).await?;
|
||||||
|
|
||||||
// Verify the signature DLEQ is valid
|
// Verify the signature DLEQ is valid
|
||||||
{
|
{
|
||||||
for (sig, premint) in mint_res.signatures.iter().zip(&premint_secrets.secrets) {
|
for (sig, premint) in mint_res.signatures.iter().zip(&premint_secrets.secrets) {
|
||||||
let keys = self.get_keyset_keys(sig.keyset_id).await?;
|
let keys = self.fetch_keyset_keys(sig.keyset_id).await?;
|
||||||
let key = keys.amount_key(sig.amount).ok_or(Error::AmountKey)?;
|
let key = keys.amount_key(sig.amount).ok_or(Error::AmountKey)?;
|
||||||
match sig.verify_dleq(key, premint.blinded_message.blinded_secret) {
|
match sig.verify_dleq(key, premint.blinded_message.blinded_secret) {
|
||||||
Ok(_) | Err(nut12::Error::MissingDleqProof) => (),
|
Ok(_) | Err(nut12::Error::MissingDleqProof) => (),
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use cdk_common::nut02::{KeySetInfos, KeySetInfosMethods};
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
|
|
||||||
use crate::nuts::{Id, KeySetInfo, Keys};
|
use crate::nuts::{Id, KeySetInfo, Keys};
|
||||||
use crate::{Error, Wallet};
|
use crate::{Error, Wallet};
|
||||||
|
|
||||||
impl Wallet {
|
impl Wallet {
|
||||||
/// Get keys for mint keyset
|
/// Fetch keys for mint keyset
|
||||||
///
|
///
|
||||||
/// Selected keys from localstore if they are already known
|
/// Returns keys from local database if they are already stored.
|
||||||
/// If they are not known queries mint for keyset id and stores the [`Keys`]
|
/// If keys are not found locally, goes online to query the mint for the keyset and stores the [`Keys`] in local database.
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip(self))]
|
||||||
pub async fn get_keyset_keys(&self, keyset_id: Id) -> Result<Keys, Error> {
|
pub async fn fetch_keyset_keys(&self, keyset_id: Id) -> Result<Keys, Error> {
|
||||||
let keys = if let Some(keys) = self.localstore.get_keys(&keyset_id).await? {
|
let keys = if let Some(keys) = self.localstore.get_keys(&keyset_id).await? {
|
||||||
keys
|
keys
|
||||||
} else {
|
} else {
|
||||||
@@ -27,10 +28,12 @@ impl Wallet {
|
|||||||
Ok(keys)
|
Ok(keys)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get keysets from DB or fetch them
|
/// Get keysets from local database or go online if missing
|
||||||
///
|
///
|
||||||
/// Checks the database for keysets and queries the Mint if
|
/// First checks the local database for cached keysets. If keysets are not found locally,
|
||||||
/// it can't find any.
|
/// goes online to refresh keysets from the mint and updates the local database.
|
||||||
|
/// This is the main method for getting keysets in token operations that can work offline
|
||||||
|
/// but will fall back to online if needed.
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip(self))]
|
||||||
pub async fn load_mint_keysets(&self) -> Result<Vec<KeySetInfo>, Error> {
|
pub async fn load_mint_keysets(&self) -> Result<Vec<KeySetInfo>, Error> {
|
||||||
match self
|
match self
|
||||||
@@ -39,86 +42,105 @@ impl Wallet {
|
|||||||
.await?
|
.await?
|
||||||
{
|
{
|
||||||
Some(keysets_info) => Ok(keysets_info),
|
Some(keysets_info) => Ok(keysets_info),
|
||||||
None => self.get_mint_keysets().await, // Hit the keysets endpoint if we don't have the keysets for this Mint
|
None => {
|
||||||
|
// If we don't have any keysets, fetch them from the mint
|
||||||
|
let keysets = self.refresh_keysets().await?;
|
||||||
|
Ok(keysets)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get keysets for wallet's mint
|
/// Get keysets from local database only - pure offline operation
|
||||||
///
|
///
|
||||||
/// Queries mint for all keysets
|
/// Only checks the local database for cached keysets. If keysets are not found locally,
|
||||||
|
/// returns an error without going online. This is used for operations that must remain
|
||||||
|
/// offline and rely on previously cached keyset data.
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip(self))]
|
||||||
pub async fn get_mint_keysets(&self) -> Result<Vec<KeySetInfo>, Error> {
|
pub async fn get_mint_keysets(&self) -> Result<Vec<KeySetInfo>, Error> {
|
||||||
let keysets = self.client.get_mint_keysets().await?;
|
|
||||||
|
|
||||||
self.localstore
|
|
||||||
.add_mint_keysets(self.mint_url.clone(), keysets.keysets.clone())
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(keysets.keysets)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get active keyset for mint
|
|
||||||
///
|
|
||||||
/// Queries mint for current keysets then gets [`Keys`] for any unknown
|
|
||||||
/// keysets
|
|
||||||
#[instrument(skip(self))]
|
|
||||||
pub async fn get_active_mint_keysets(&self) -> Result<Vec<KeySetInfo>, Error> {
|
|
||||||
let keysets = self.client.get_mint_keysets().await?;
|
|
||||||
let keysets = keysets.keysets;
|
|
||||||
|
|
||||||
self.localstore
|
|
||||||
.add_mint_keysets(self.mint_url.clone(), keysets.clone())
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let active_keysets = keysets
|
|
||||||
.clone()
|
|
||||||
.into_iter()
|
|
||||||
.filter(|k| k.active && k.unit == self.unit)
|
|
||||||
.collect::<Vec<KeySetInfo>>();
|
|
||||||
|
|
||||||
match self
|
match self
|
||||||
.localstore
|
.localstore
|
||||||
.get_mint_keysets(self.mint_url.clone())
|
.get_mint_keysets(self.mint_url.clone())
|
||||||
.await?
|
.await?
|
||||||
{
|
{
|
||||||
Some(known_keysets) => {
|
Some(keysets_info) => Ok(keysets_info),
|
||||||
let unknown_keysets: Vec<&KeySetInfo> = keysets
|
None => Err(Error::UnknownKeySet),
|
||||||
.iter()
|
|
||||||
.filter(|k| known_keysets.contains(k))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
for keyset in unknown_keysets {
|
|
||||||
self.get_keyset_keys(keyset.id).await?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
for keyset in keysets {
|
|
||||||
self.get_keyset_keys(keyset.id).await?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(active_keysets)
|
/// Refresh keysets by fetching the latest from mint - always goes online
|
||||||
}
|
|
||||||
|
|
||||||
/// Get active keyset for mint with the lowest fees
|
|
||||||
///
|
///
|
||||||
/// Queries mint for current keysets then gets [`Keys`] for any unknown
|
/// This method always goes online to fetch the latest keyset information from the mint.
|
||||||
/// keysets
|
/// It updates the local database with the fetched keysets and ensures we have keys
|
||||||
|
/// for all active keysets. This is used when operations need the most up-to-date
|
||||||
|
/// keyset information and are willing to go online.
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip(self))]
|
||||||
pub async fn get_active_mint_keyset(&self) -> Result<KeySetInfo, Error> {
|
pub async fn refresh_keysets(&self) -> Result<KeySetInfos, Error> {
|
||||||
// Important
|
tracing::debug!("Refreshing keysets and ensuring we have keys");
|
||||||
let _ = self.get_mint_info().await?;
|
let _ = self.get_mint_info().await?;
|
||||||
let active_keysets = self.get_active_mint_keysets().await?;
|
|
||||||
|
|
||||||
let keyset_with_lowest_fee = active_keysets
|
// Fetch all current keysets from mint
|
||||||
.into_iter()
|
let keysets_response = self.client.get_mint_keysets().await?;
|
||||||
.min_by_key(|key| key.input_fee_ppk)
|
let all_keysets = keysets_response.keysets;
|
||||||
.ok_or(Error::NoActiveKeyset)?;
|
|
||||||
Ok(keyset_with_lowest_fee)
|
// Update local storage with keyset info
|
||||||
|
self.localstore
|
||||||
|
.add_mint_keysets(self.mint_url.clone(), all_keysets.clone())
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Filter for active keysets matching our unit
|
||||||
|
let keysets: KeySetInfos = all_keysets.unit(self.unit.clone()).cloned().collect();
|
||||||
|
|
||||||
|
// Ensure we have keys for all active keysets
|
||||||
|
for keyset in &keysets {
|
||||||
|
if self.localstore.get_keys(&keyset.id).await?.is_none() {
|
||||||
|
tracing::debug!("Fetching missing keys for keyset {}", keyset.id);
|
||||||
|
self.fetch_keyset_keys(keyset.id).await?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get keyset fees for mint
|
Ok(keysets)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the active keyset with the lowest fees - always goes online
|
||||||
|
///
|
||||||
|
/// This method always goes online to refresh keysets from the mint and then returns
|
||||||
|
/// the active keyset with the minimum input fees. Use this when you need the most
|
||||||
|
/// up-to-date keyset information for operations.
|
||||||
|
#[instrument(skip(self))]
|
||||||
|
pub async fn fetch_active_keyset(&self) -> Result<KeySetInfo, Error> {
|
||||||
|
self.refresh_keysets()
|
||||||
|
.await?
|
||||||
|
.active()
|
||||||
|
.min_by_key(|k| k.input_fee_ppk)
|
||||||
|
.cloned()
|
||||||
|
.ok_or(Error::NoActiveKeyset)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the active keyset with the lowest fees from local database only - offline operation
|
||||||
|
///
|
||||||
|
/// Returns the active keyset with minimum input fees from cached keysets in the local database.
|
||||||
|
/// This is an offline operation that does not contact the mint. If no keysets are found locally,
|
||||||
|
/// returns an error. Use this for offline operations or when you want to avoid network calls.
|
||||||
|
#[instrument(skip(self))]
|
||||||
|
pub async fn get_active_keyset(&self) -> Result<KeySetInfo, Error> {
|
||||||
|
match self
|
||||||
|
.localstore
|
||||||
|
.get_mint_keysets(self.mint_url.clone())
|
||||||
|
.await?
|
||||||
|
{
|
||||||
|
Some(keysets_info) => keysets_info
|
||||||
|
.into_iter()
|
||||||
|
.min_by_key(|k| k.input_fee_ppk)
|
||||||
|
.ok_or(Error::NoActiveKeyset),
|
||||||
|
None => Err(Error::UnknownKeySet),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get keyset fees for mint from local database only - offline operation
|
||||||
|
///
|
||||||
|
/// Returns a HashMap of keyset IDs to their input fee rates (per-proof-per-thousand)
|
||||||
|
/// from cached keysets in the local database. This is an offline operation that does
|
||||||
|
/// not contact the mint. If no keysets are found locally, returns an error.
|
||||||
pub async fn get_keyset_fees(&self) -> Result<HashMap<Id, u64>, Error> {
|
pub async fn get_keyset_fees(&self) -> Result<HashMap<Id, u64>, Error> {
|
||||||
let keysets = self
|
let keysets = self
|
||||||
.localstore
|
.localstore
|
||||||
@@ -134,7 +156,11 @@ impl Wallet {
|
|||||||
Ok(fees)
|
Ok(fees)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get keyset fees for mint by keyset id
|
/// Get keyset fees for mint by keyset id from local database only - offline operation
|
||||||
|
///
|
||||||
|
/// Returns the input fee rate (per-proof-per-thousand) for a specific keyset ID from
|
||||||
|
/// cached keysets in the local database. This is an offline operation that does not
|
||||||
|
/// contact the mint. If the keyset is not found locally, returns an error.
|
||||||
pub async fn get_keyset_fees_by_id(&self, keyset_id: Id) -> Result<u64, Error> {
|
pub async fn get_keyset_fees_by_id(&self, keyset_id: Id) -> Result<u64, Error> {
|
||||||
self.get_keyset_fees()
|
self.get_keyset_fees()
|
||||||
.await?
|
.await?
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ impl Wallet {
|
|||||||
.update_proofs_state(ys, State::Pending)
|
.update_proofs_state(ys, State::Pending)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let active_keyset_id = self.get_active_mint_keyset().await?.id;
|
let active_keyset_id = self.fetch_active_keyset().await?.id;
|
||||||
|
|
||||||
let count = self
|
let count = self
|
||||||
.localstore
|
.localstore
|
||||||
@@ -317,7 +317,7 @@ impl Wallet {
|
|||||||
let available_proofs = self.get_unspent_proofs().await?;
|
let available_proofs = self.get_unspent_proofs().await?;
|
||||||
|
|
||||||
let active_keyset_ids = self
|
let active_keyset_ids = self
|
||||||
.get_active_mint_keysets()
|
.refresh_keysets()
|
||||||
.await?
|
.await?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|k| k.id)
|
.map(|k| k.id)
|
||||||
|
|||||||
@@ -378,12 +378,12 @@ impl Wallet {
|
|||||||
self.get_mint_info().await?;
|
self.get_mint_info().await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let keysets = self.get_mint_keysets().await?;
|
let keysets = self.load_mint_keysets().await?;
|
||||||
|
|
||||||
let mut restored_value = Amount::ZERO;
|
let mut restored_value = Amount::ZERO;
|
||||||
|
|
||||||
for keyset in keysets {
|
for keyset in keysets {
|
||||||
let keys = self.get_keyset_keys(keyset.id).await?;
|
let keys = self.fetch_keyset_keys(keyset.id).await?;
|
||||||
let mut empty_batch = 0;
|
let mut empty_batch = 0;
|
||||||
let mut start_counter = 0;
|
let mut start_counter = 0;
|
||||||
|
|
||||||
@@ -632,7 +632,7 @@ impl Wallet {
|
|||||||
let mint_pubkey = match keys_cache.get(&proof.keyset_id) {
|
let mint_pubkey = match keys_cache.get(&proof.keyset_id) {
|
||||||
Some(keys) => keys.amount_key(proof.amount),
|
Some(keys) => keys.amount_key(proof.amount),
|
||||||
None => {
|
None => {
|
||||||
let keys = self.get_keyset_keys(proof.keyset_id).await?;
|
let keys = self.fetch_keyset_keys(proof.keyset_id).await?;
|
||||||
|
|
||||||
let key = keys.amount_key(proof.amount);
|
let key = keys.amount_key(proof.amount);
|
||||||
keys_cache.insert(proof.keyset_id, keys);
|
keys_cache.insert(proof.keyset_id, keys);
|
||||||
|
|||||||
@@ -293,7 +293,7 @@ impl MultiMintWallet {
|
|||||||
{
|
{
|
||||||
Some(keysets_info) => keysets_info,
|
Some(keysets_info) => keysets_info,
|
||||||
// Hit the keysets endpoint if we don't have the keysets for this Mint
|
// Hit the keysets endpoint if we don't have the keysets for this Mint
|
||||||
None => wallet.get_mint_keysets().await?,
|
None => wallet.load_mint_keysets().await?,
|
||||||
};
|
};
|
||||||
let proofs = token_data.proofs(&keysets_info)?;
|
let proofs = token_data.proofs(&keysets_info)?;
|
||||||
|
|
||||||
|
|||||||
@@ -38,11 +38,11 @@ impl Wallet {
|
|||||||
self.get_mint_info().await?;
|
self.get_mint_info().await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = self.get_active_mint_keyset().await?;
|
let _ = self.fetch_active_keyset().await?;
|
||||||
|
|
||||||
let active_keyset_id = self.get_active_mint_keyset().await?.id;
|
let active_keyset_id = self.fetch_active_keyset().await?.id;
|
||||||
|
|
||||||
let keys = self.get_keyset_keys(active_keyset_id).await?;
|
let keys = self.fetch_keyset_keys(active_keyset_id).await?;
|
||||||
|
|
||||||
let mut proofs = proofs;
|
let mut proofs = proofs;
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ impl Wallet {
|
|||||||
for proof in &mut proofs {
|
for proof in &mut proofs {
|
||||||
// Verify that proof DLEQ is valid
|
// Verify that proof DLEQ is valid
|
||||||
if proof.dleq.is_some() {
|
if proof.dleq.is_some() {
|
||||||
let keys = self.get_keyset_keys(proof.keyset_id).await?;
|
let keys = self.fetch_keyset_keys(proof.keyset_id).await?;
|
||||||
let key = keys.amount_key(proof.amount).ok_or(Error::AmountKey)?;
|
let key = keys.amount_key(proof.amount).ok_or(Error::AmountKey)?;
|
||||||
proof.verify_dleq(key)?;
|
proof.verify_dleq(key)?;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
use cdk_common::nut02::KeySetInfosMethods;
|
||||||
use cdk_common::util::unix_time;
|
use cdk_common::util::unix_time;
|
||||||
use cdk_common::wallet::{Transaction, TransactionDirection};
|
use cdk_common::wallet::{Transaction, TransactionDirection};
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
@@ -32,11 +33,8 @@ impl Wallet {
|
|||||||
|
|
||||||
// If online send check mint for current keysets fees
|
// If online send check mint for current keysets fees
|
||||||
if opts.send_kind.is_online() {
|
if opts.send_kind.is_online() {
|
||||||
if let Err(e) = self.get_active_mint_keyset().await {
|
if let Err(e) = self.refresh_keysets().await {
|
||||||
tracing::error!(
|
tracing::error!("Error refreshing keysets: {:?}. Using stored keysets", e);
|
||||||
"Error fetching active mint keyset: {:?}. Using stored keysets",
|
|
||||||
e
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,11 +76,12 @@ impl Wallet {
|
|||||||
|
|
||||||
// Select proofs
|
// Select proofs
|
||||||
let active_keyset_ids = self
|
let active_keyset_ids = self
|
||||||
.get_active_mint_keysets()
|
.get_mint_keysets()
|
||||||
.await?
|
.await?
|
||||||
.into_iter()
|
.active()
|
||||||
.map(|k| k.id)
|
.map(|k| k.id)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let selected_proofs = Wallet::select_proofs(
|
let selected_proofs = Wallet::select_proofs(
|
||||||
amount,
|
amount,
|
||||||
available_proofs,
|
available_proofs,
|
||||||
@@ -131,7 +130,7 @@ impl Wallet {
|
|||||||
) -> Result<PreparedSend, Error> {
|
) -> Result<PreparedSend, Error> {
|
||||||
// Split amount with fee if necessary
|
// Split amount with fee if necessary
|
||||||
let (send_amounts, send_fee) = if opts.include_fee {
|
let (send_amounts, send_fee) = if opts.include_fee {
|
||||||
let active_keyset_id = self.get_active_mint_keyset().await?.id;
|
let active_keyset_id = self.get_active_keyset().await?.id;
|
||||||
let keyset_fee_ppk = self.get_keyset_fees_by_id(active_keyset_id).await?;
|
let keyset_fee_ppk = self.get_keyset_fees_by_id(active_keyset_id).await?;
|
||||||
tracing::debug!("Keyset fee per proof: {:?}", keyset_fee_ppk);
|
tracing::debug!("Keyset fee per proof: {:?}", keyset_fee_ppk);
|
||||||
let send_split = amount.split_with_fee(keyset_fee_ppk)?;
|
let send_split = amount.split_with_fee(keyset_fee_ppk)?;
|
||||||
@@ -209,7 +208,7 @@ impl Wallet {
|
|||||||
let mut proofs_to_send = send.proofs_to_send;
|
let mut proofs_to_send = send.proofs_to_send;
|
||||||
|
|
||||||
// Get active keyset ID
|
// Get active keyset ID
|
||||||
let active_keyset_id = self.get_active_mint_keyset().await?.id;
|
let active_keyset_id = self.fetch_active_keyset().await?.id;
|
||||||
tracing::debug!("Active keyset ID: {:?}", active_keyset_id);
|
tracing::debug!("Active keyset ID: {:?}", active_keyset_id);
|
||||||
|
|
||||||
// Get keyset fees
|
// Get keyset fees
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use cdk_common::nut02::KeySetInfosMethods;
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
|
|
||||||
use crate::amount::SplitTarget;
|
use crate::amount::SplitTarget;
|
||||||
@@ -167,11 +168,12 @@ impl Wallet {
|
|||||||
ensure_cdk!(proofs_sum >= amount, Error::InsufficientFunds);
|
ensure_cdk!(proofs_sum >= amount, Error::InsufficientFunds);
|
||||||
|
|
||||||
let active_keyset_ids = self
|
let active_keyset_ids = self
|
||||||
.get_active_mint_keysets()
|
.refresh_keysets()
|
||||||
.await?
|
.await?
|
||||||
.into_iter()
|
.active()
|
||||||
.map(|k| k.id)
|
.map(|k| k.id)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let keyset_fees = self.get_keyset_fees().await?;
|
let keyset_fees = self.get_keyset_fees().await?;
|
||||||
let proofs = Wallet::select_proofs(
|
let proofs = Wallet::select_proofs(
|
||||||
amount,
|
amount,
|
||||||
@@ -203,7 +205,7 @@ impl Wallet {
|
|||||||
include_fees: bool,
|
include_fees: bool,
|
||||||
) -> Result<PreSwap, Error> {
|
) -> Result<PreSwap, Error> {
|
||||||
tracing::info!("Creating swap");
|
tracing::info!("Creating swap");
|
||||||
let active_keyset_id = self.get_active_mint_keyset().await?.id;
|
let active_keyset_id = self.fetch_active_keyset().await?.id;
|
||||||
|
|
||||||
// Desired amount is either amount passed or value of all proof
|
// Desired amount is either amount passed or value of all proof
|
||||||
let proofs_total = proofs.total_amount()?;
|
let proofs_total = proofs.total_amount()?;
|
||||||
|
|||||||
Reference in New Issue
Block a user