mirror of
https://github.com/aljazceru/cdk.git
synced 2026-02-22 05:25:41 +01:00
feat: add keyset u32 mapping migration (#926)
* feat: add keyset u32 mapping migration and duplicate handling - Add new database migration (version 3) to include u32 representation for keysets - Implement migration for both redb and SQL databases - Add duplicate detection and handling for keyset entries - Create unique index constraint for keyset_u32 column in SQL - Update keyset storage to include u32 identifiers - Handle backwards compatibility for existing databases * chore: clippy * refactor(cashu): simplify keyset ID verification logic - Consolidate match expression into a single expression - Use direct comparison with ensure_cdk macro - Improve readability of keyset ID validation * refactor(cdk): rename `fetch_keyset_keys` to `load_keyset_keys` for clarity - Renamed `fetch_keyset_keys` to `load_keyset_keys` across multiple modules to better reflect its behavior of loading keys from local storage or fetching from mint when missing. - Added debug logging to indicate when keys are being fetched from the mint. - Simplified key loading logic in `update_mint_keysets` by removing redundant existence checks. * chore: remove unused vec
This commit is contained in:
@@ -445,18 +445,17 @@ pub struct KeySet {
|
||||
impl KeySet {
|
||||
/// Verify the keyset id matches keys
|
||||
pub fn verify_id(&self) -> Result<(), Error> {
|
||||
match self.id.version {
|
||||
KeySetVersion::Version00 => {
|
||||
let keys_id: Id = Id::v1_from_keys(&self.keys);
|
||||
let keys_id = match self.id.version {
|
||||
KeySetVersion::Version00 => Id::v1_from_keys(&self.keys),
|
||||
KeySetVersion::Version01 => Id::v2_from_data(&self.keys, &self.unit, self.final_expiry),
|
||||
};
|
||||
|
||||
ensure_cdk!(keys_id == self.id, Error::IncorrectKeysetId);
|
||||
}
|
||||
KeySetVersion::Version01 => {
|
||||
let keys_id: Id = Id::v2_from_data(&self.keys, &self.unit, self.final_expiry);
|
||||
ensure_cdk!(
|
||||
u32::from(keys_id) == u32::from(self.id),
|
||||
Error::IncorrectKeysetId
|
||||
);
|
||||
|
||||
ensure_cdk!(keys_id == self.id, Error::IncorrectKeysetId);
|
||||
}
|
||||
}
|
||||
ensure_cdk!(keys_id == self.id, Error::IncorrectKeysetId);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -64,6 +64,9 @@ pub enum Error {
|
||||
/// Unknown Database Version
|
||||
#[error("Unknown database version")]
|
||||
UnknownDatabaseVersion,
|
||||
/// Duplicate
|
||||
#[error("Duplicate")]
|
||||
Duplicate,
|
||||
}
|
||||
|
||||
impl From<Error> for cdk_common::database::Error {
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
//! Wallet Migrations
|
||||
use std::collections::HashSet;
|
||||
use std::ops::Deref;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use cdk_common::mint_url::MintUrl;
|
||||
use cdk_common::Id;
|
||||
use redb::{
|
||||
Database, MultimapTableDefinition, ReadableMultimapTable, ReadableTable, TableDefinition,
|
||||
};
|
||||
|
||||
use super::Error;
|
||||
use crate::wallet::{KEYSETS_TABLE, KEYSET_U32_MAPPING, MINT_KEYS_TABLE};
|
||||
|
||||
// <Mint_url, Info>
|
||||
const MINTS_TABLE: TableDefinition<&str, &str> = TableDefinition::new("mints_table");
|
||||
@@ -16,6 +19,57 @@ const MINTS_TABLE: TableDefinition<&str, &str> = TableDefinition::new("mints_tab
|
||||
const MINT_KEYSETS_TABLE: MultimapTableDefinition<&str, &[u8]> =
|
||||
MultimapTableDefinition::new("mint_keysets");
|
||||
|
||||
pub(crate) fn migrate_02_to_03(db: Arc<Database>) -> Result<u32, Error> {
|
||||
let write_txn = db.begin_write().map_err(Error::from)?;
|
||||
|
||||
let mut duplicate = false;
|
||||
|
||||
{
|
||||
let table = write_txn.open_table(MINT_KEYS_TABLE).map_err(Error::from)?;
|
||||
|
||||
let ids: Vec<Id> = table
|
||||
.iter()
|
||||
.map_err(Error::from)?
|
||||
.flatten()
|
||||
.flat_map(|(id, _)| Id::from_str(id.value()))
|
||||
.collect();
|
||||
|
||||
let mut table = write_txn
|
||||
.open_table(KEYSET_U32_MAPPING)
|
||||
.map_err(Error::from)?;
|
||||
|
||||
// Also process existing keysets
|
||||
let keysets_table = write_txn.open_table(KEYSETS_TABLE).map_err(Error::from)?;
|
||||
let keyset_ids: Vec<Id> = keysets_table
|
||||
.iter()
|
||||
.map_err(Error::from)?
|
||||
.flatten()
|
||||
.flat_map(|(id_bytes, _)| Id::from_bytes(id_bytes.value()))
|
||||
.collect();
|
||||
|
||||
let ids: HashSet<Id> = ids.into_iter().chain(keyset_ids).collect();
|
||||
|
||||
for id in ids {
|
||||
let t = table.insert(u32::from(id), id.to_string().as_str())?;
|
||||
|
||||
tracing::info!("Adding u32 {} for keyset {}", u32::from(id), id.to_string());
|
||||
|
||||
if t.is_some() {
|
||||
duplicate = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if duplicate {
|
||||
write_txn.abort()?;
|
||||
return Err(Error::Duplicate);
|
||||
}
|
||||
|
||||
write_txn.commit()?;
|
||||
|
||||
Ok(3)
|
||||
}
|
||||
|
||||
pub fn migrate_01_to_02(db: Arc<Database>) -> Result<u32, Error> {
|
||||
migrate_trim_mint_urls_01_to_02(db)?;
|
||||
Ok(2)
|
||||
|
||||
@@ -21,7 +21,7 @@ use tracing::instrument;
|
||||
|
||||
use super::error::Error;
|
||||
use crate::migrations::migrate_00_to_01;
|
||||
use crate::wallet::migrations::migrate_01_to_02;
|
||||
use crate::wallet::migrations::{migrate_01_to_02, migrate_02_to_03};
|
||||
|
||||
mod migrations;
|
||||
|
||||
@@ -44,7 +44,9 @@ const KEYSET_COUNTER: TableDefinition<&str, u32> = TableDefinition::new("keyset_
|
||||
// <Transaction_id, Transaction>
|
||||
const TRANSACTIONS_TABLE: TableDefinition<&[u8], &str> = TableDefinition::new("transactions");
|
||||
|
||||
const DATABASE_VERSION: u32 = 2;
|
||||
const KEYSET_U32_MAPPING: TableDefinition<u32, &str> = TableDefinition::new("keyset_u32_mapping");
|
||||
|
||||
const DATABASE_VERSION: u32 = 3;
|
||||
|
||||
/// Wallet Redb Database
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -90,6 +92,10 @@ impl WalletRedbDatabase {
|
||||
current_file_version = migrate_01_to_02(Arc::clone(&db))?;
|
||||
}
|
||||
|
||||
if current_file_version == 2 {
|
||||
current_file_version = migrate_02_to_03(Arc::clone(&db))?;
|
||||
}
|
||||
|
||||
if current_file_version != DATABASE_VERSION {
|
||||
tracing::warn!(
|
||||
"Database upgrade did not complete at {} current is {}",
|
||||
@@ -136,6 +142,7 @@ impl WalletRedbDatabase {
|
||||
let _ = write_txn.open_table(PROOFS_TABLE)?;
|
||||
let _ = write_txn.open_table(KEYSET_COUNTER)?;
|
||||
let _ = write_txn.open_table(TRANSACTIONS_TABLE)?;
|
||||
let _ = write_txn.open_table(KEYSET_U32_MAPPING)?;
|
||||
table.insert("db_version", DATABASE_VERSION.to_string().as_str())?;
|
||||
}
|
||||
|
||||
@@ -290,20 +297,64 @@ impl WalletDatabase for WalletRedbDatabase {
|
||||
) -> Result<(), Self::Err> {
|
||||
let write_txn = self.db.begin_write().map_err(Error::from)?;
|
||||
|
||||
let mut existing_u32 = false;
|
||||
|
||||
{
|
||||
let mut table = write_txn
|
||||
.open_multimap_table(MINT_KEYSETS_TABLE)
|
||||
.map_err(Error::from)?;
|
||||
let mut keysets_table = write_txn.open_table(KEYSETS_TABLE).map_err(Error::from)?;
|
||||
let mut u32_table = write_txn
|
||||
.open_table(KEYSET_U32_MAPPING)
|
||||
.map_err(Error::from)?;
|
||||
|
||||
for keyset in keysets {
|
||||
table
|
||||
.insert(
|
||||
mint_url.to_string().as_str(),
|
||||
keyset.id.to_bytes().as_slice(),
|
||||
)
|
||||
// Check if keyset already exists
|
||||
let existing_keyset = {
|
||||
let existing_keyset = keysets_table
|
||||
.get(keyset.id.to_bytes().as_slice())
|
||||
.map_err(Error::from)?;
|
||||
|
||||
existing_keyset.map(|r| r.value().to_string())
|
||||
};
|
||||
|
||||
let existing = u32_table
|
||||
.insert(u32::from(keyset.id), keyset.id.to_string().as_str())
|
||||
.map_err(Error::from)?;
|
||||
|
||||
match existing {
|
||||
None => existing_u32 = false,
|
||||
Some(id) => {
|
||||
let id = Id::from_str(id.value())?;
|
||||
|
||||
if id == keyset.id {
|
||||
existing_u32 = false;
|
||||
} else {
|
||||
println!("Breaking here");
|
||||
existing_u32 = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let keyset = if let Some(existing_keyset) = existing_keyset {
|
||||
let mut existing_keyset: KeySetInfo = serde_json::from_str(&existing_keyset)?;
|
||||
|
||||
existing_keyset.active = keyset.active;
|
||||
existing_keyset.input_fee_ppk = keyset.input_fee_ppk;
|
||||
|
||||
existing_keyset
|
||||
} else {
|
||||
table
|
||||
.insert(
|
||||
mint_url.to_string().as_str(),
|
||||
keyset.id.to_bytes().as_slice(),
|
||||
)
|
||||
.map_err(Error::from)?;
|
||||
|
||||
keyset
|
||||
};
|
||||
|
||||
keysets_table
|
||||
.insert(
|
||||
keyset.id.to_bytes().as_slice(),
|
||||
@@ -314,6 +365,14 @@ impl WalletDatabase for WalletRedbDatabase {
|
||||
.map_err(Error::from)?;
|
||||
}
|
||||
}
|
||||
|
||||
if existing_u32 {
|
||||
tracing::warn!("Keyset already exists for keyset id");
|
||||
write_txn.abort().map_err(Error::from)?;
|
||||
|
||||
return Err(database::Error::Duplicate);
|
||||
}
|
||||
|
||||
write_txn.commit().map_err(Error::from)?;
|
||||
|
||||
Ok(())
|
||||
@@ -514,16 +573,45 @@ impl WalletDatabase for WalletRedbDatabase {
|
||||
|
||||
keyset.verify_id()?;
|
||||
|
||||
let existing_keys;
|
||||
let existing_u32;
|
||||
|
||||
{
|
||||
let mut table = write_txn.open_table(MINT_KEYS_TABLE).map_err(Error::from)?;
|
||||
table
|
||||
|
||||
existing_keys = table
|
||||
.insert(
|
||||
keyset.id.to_string().as_str(),
|
||||
serde_json::to_string(&keyset.keys)
|
||||
.map_err(Error::from)?
|
||||
.as_str(),
|
||||
)
|
||||
.map_err(Error::from)?
|
||||
.is_some();
|
||||
|
||||
let mut table = write_txn
|
||||
.open_table(KEYSET_U32_MAPPING)
|
||||
.map_err(Error::from)?;
|
||||
|
||||
let existing = table
|
||||
.insert(u32::from(keyset.id), keyset.id.to_string().as_str())
|
||||
.map_err(Error::from)?;
|
||||
|
||||
match existing {
|
||||
None => existing_u32 = false,
|
||||
Some(id) => {
|
||||
let id = Id::from_str(id.value())?;
|
||||
|
||||
existing_u32 = id != keyset.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if existing_keys || existing_u32 {
|
||||
tracing::warn!("Keys already exist for keyset id");
|
||||
write_txn.abort().map_err(Error::from)?;
|
||||
|
||||
return Err(database::Error::Duplicate);
|
||||
}
|
||||
|
||||
write_txn.commit().map_err(Error::from)?;
|
||||
|
||||
@@ -19,4 +19,5 @@ pub static MIGRATIONS: &[(&str, &str, &str)] = &[
|
||||
("sqlite", "20250401120000_add_transactions_table.sql", include_str!(r#"./migrations/sqlite/20250401120000_add_transactions_table.sql"#)),
|
||||
("sqlite", "20250616144830_add_keyset_expiry.sql", include_str!(r#"./migrations/sqlite/20250616144830_add_keyset_expiry.sql"#)),
|
||||
("sqlite", "20250707093445_bolt12.sql", include_str!(r#"./migrations/sqlite/20250707093445_bolt12.sql"#)),
|
||||
("sqlite", "20250729111701_keyset_v2_u32.sql", include_str!(r#"./migrations/sqlite/20250729111701_keyset_v2_u32.sql"#)),
|
||||
];
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
-- Add u32 representation column to key table with unique constraint
|
||||
ALTER TABLE key ADD COLUMN keyset_u32 INTEGER;
|
||||
|
||||
-- Add unique constraint on the new column
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS keyset_u32_unique ON key(keyset_u32);
|
||||
|
||||
-- Add u32 representation column to keyset table with unique constraint
|
||||
ALTER TABLE keyset ADD COLUMN keyset_u32 INTEGER;
|
||||
|
||||
-- Add unique constraint on the new column
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS keyset_u32_unique_keyset ON keyset(keyset_u32);
|
||||
@@ -53,6 +53,74 @@ where
|
||||
/// Migrate [`WalletSqliteDatabase`]
|
||||
async fn migrate(conn: &DB) -> Result<(), Error> {
|
||||
migrate(conn, DB::name(), migrations::MIGRATIONS).await?;
|
||||
// Update any existing keys with missing keyset_u32 values
|
||||
Self::add_keyset_u32(conn).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn add_keyset_u32(conn: &DB) -> Result<(), Error> {
|
||||
// First get the keysets where keyset_u32 on key is null
|
||||
let keys_without_u32: Vec<Vec<Column>> = query(
|
||||
r#"
|
||||
SELECT
|
||||
id
|
||||
FROM key
|
||||
WHERE keyset_u32 IS NULL
|
||||
"#,
|
||||
)?
|
||||
.fetch_all(conn)
|
||||
.await?;
|
||||
|
||||
for id in keys_without_u32 {
|
||||
let id = column_as_string!(id.first().unwrap());
|
||||
|
||||
if let Ok(id) = Id::from_str(&id) {
|
||||
query(
|
||||
r#"
|
||||
UPDATE
|
||||
key
|
||||
SET keyset_u32 = :u32_keyset
|
||||
WHERE id = :keyset_id
|
||||
"#,
|
||||
)?
|
||||
.bind("u32_keyset", u32::from(id))
|
||||
.bind("keyset_id", id.to_string())
|
||||
.execute(conn)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
// Also update keysets where keyset_u32 is null
|
||||
let keysets_without_u32: Vec<Vec<Column>> = query(
|
||||
r#"
|
||||
SELECT
|
||||
id
|
||||
FROM keyset
|
||||
WHERE keyset_u32 IS NULL
|
||||
"#,
|
||||
)?
|
||||
.fetch_all(conn)
|
||||
.await?;
|
||||
|
||||
for id in keysets_without_u32 {
|
||||
let id = column_as_string!(id.first().unwrap());
|
||||
|
||||
if let Ok(id) = Id::from_str(&id) {
|
||||
query(
|
||||
r#"
|
||||
UPDATE
|
||||
keyset
|
||||
SET keyset_u32 = :u32_keyset
|
||||
WHERE id = :keyset_id
|
||||
"#,
|
||||
)?
|
||||
.bind("u32_keyset", u32::from(id))
|
||||
.bind("keyset_id", id.to_string())
|
||||
.execute(conn)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -301,15 +369,12 @@ ON CONFLICT(mint_url) DO UPDATE SET
|
||||
query(
|
||||
r#"
|
||||
INSERT INTO keyset
|
||||
(mint_url, id, unit, active, input_fee_ppk, final_expiry)
|
||||
(mint_url, id, unit, active, input_fee_ppk, final_expiry, keyset_u32)
|
||||
VALUES
|
||||
(:mint_url, :id, :unit, :active, :input_fee_ppk, :final_expiry)
|
||||
(:mint_url, :id, :unit, :active, :input_fee_ppk, :final_expiry, :keyset_u32)
|
||||
ON CONFLICT(id) DO UPDATE SET
|
||||
mint_url = excluded.mint_url,
|
||||
unit = excluded.unit,
|
||||
active = excluded.active,
|
||||
input_fee_ppk = excluded.input_fee_ppk,
|
||||
final_expiry = excluded.final_expiry;
|
||||
input_fee_ppk = excluded.input_fee_ppk
|
||||
"#,
|
||||
)?
|
||||
.bind("mint_url", mint_url.to_string())
|
||||
@@ -318,6 +383,7 @@ ON CONFLICT(mint_url) DO UPDATE SET
|
||||
.bind("active", keyset.active)
|
||||
.bind("input_fee_ppk", keyset.input_fee_ppk as i64)
|
||||
.bind("final_expiry", keyset.final_expiry.map(|v| v as i64))
|
||||
.bind("keyset_u32", u32::from(keyset.id))
|
||||
.execute(&self.db)
|
||||
.await?;
|
||||
}
|
||||
@@ -554,11 +620,9 @@ ON CONFLICT(id) DO UPDATE SET
|
||||
query(
|
||||
r#"
|
||||
INSERT INTO key
|
||||
(id, keys)
|
||||
(id, keys, keyset_u32)
|
||||
VALUES
|
||||
(:id, :keys)
|
||||
ON CONFLICT(id) DO UPDATE SET
|
||||
keys = excluded.keys
|
||||
(:id, :keys, :keyset_u32)
|
||||
"#,
|
||||
)?
|
||||
.bind("id", keyset.id.to_string())
|
||||
@@ -566,6 +630,7 @@ ON CONFLICT(id) DO UPDATE SET
|
||||
"keys",
|
||||
serde_json::to_string(&keyset.keys).map_err(Error::from)?,
|
||||
)
|
||||
.bind("keyset_u32", u32::from(keyset.id))
|
||||
.execute(&self.db)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -264,12 +264,12 @@ impl Wallet {
|
||||
|
||||
let mint_res = self.client.post_mint(request).await?;
|
||||
|
||||
let keys = self.fetch_keyset_keys(active_keyset_id).await?;
|
||||
let keys = self.load_keyset_keys(active_keyset_id).await?;
|
||||
|
||||
// Verify the signature DLEQ is valid
|
||||
{
|
||||
for (sig, premint) in mint_res.signatures.iter().zip(&premint_secrets.secrets) {
|
||||
let keys = self.fetch_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)?;
|
||||
match sig.verify_dleq(key, premint.blinded_message.blinded_secret) {
|
||||
Ok(_) | Err(nut12::Error::MissingDleqProof) => (),
|
||||
|
||||
@@ -161,12 +161,12 @@ impl Wallet {
|
||||
|
||||
let mint_res = self.client.post_mint(request).await?;
|
||||
|
||||
let keys = self.fetch_keyset_keys(active_keyset_id).await?;
|
||||
let keys = self.load_keyset_keys(active_keyset_id).await?;
|
||||
|
||||
// Verify the signature DLEQ is valid
|
||||
{
|
||||
for (sig, premint) in mint_res.signatures.iter().zip(&premint_secrets.secrets) {
|
||||
let keys = self.fetch_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)?;
|
||||
match sig.verify_dleq(key, premint.blinded_message.blinded_secret) {
|
||||
Ok(_) | Err(nut12::Error::MissingDleqProof) => (),
|
||||
|
||||
@@ -7,15 +7,21 @@ use crate::nuts::{Id, KeySetInfo, Keys};
|
||||
use crate::{Error, Wallet};
|
||||
|
||||
impl Wallet {
|
||||
/// Fetch keys for mint keyset
|
||||
/// Load keys for mint keyset
|
||||
///
|
||||
/// Returns keys from local database if they are already stored.
|
||||
/// 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))]
|
||||
pub async fn fetch_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? {
|
||||
keys
|
||||
} else {
|
||||
tracing::debug!(
|
||||
"Keyset {} not in db fetching from mint {}",
|
||||
keyset_id,
|
||||
self.mint_url
|
||||
);
|
||||
|
||||
let keys = self.client.get_mint_keyset(keyset_id).await?;
|
||||
|
||||
keys.verify_id()?;
|
||||
@@ -92,10 +98,7 @@ impl Wallet {
|
||||
|
||||
// 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?;
|
||||
}
|
||||
self.load_keyset_keys(keyset.id).await?;
|
||||
}
|
||||
|
||||
Ok(keysets)
|
||||
|
||||
@@ -383,7 +383,7 @@ impl Wallet {
|
||||
let mut restored_value = Amount::ZERO;
|
||||
|
||||
for keyset in keysets {
|
||||
let keys = self.fetch_keyset_keys(keyset.id).await?;
|
||||
let keys = self.load_keyset_keys(keyset.id).await?;
|
||||
let mut empty_batch = 0;
|
||||
let mut start_counter = 0;
|
||||
|
||||
@@ -632,7 +632,7 @@ impl Wallet {
|
||||
let mint_pubkey = match keys_cache.get(&proof.keyset_id) {
|
||||
Some(keys) => keys.amount_key(proof.amount),
|
||||
None => {
|
||||
let keys = self.fetch_keyset_keys(proof.keyset_id).await?;
|
||||
let keys = self.load_keyset_keys(proof.keyset_id).await?;
|
||||
|
||||
let key = keys.amount_key(proof.amount);
|
||||
keys_cache.insert(proof.keyset_id, keys);
|
||||
|
||||
@@ -42,7 +42,7 @@ impl Wallet {
|
||||
|
||||
let active_keyset_id = self.fetch_active_keyset().await?.id;
|
||||
|
||||
let keys = self.fetch_keyset_keys(active_keyset_id).await?;
|
||||
let keys = self.load_keyset_keys(active_keyset_id).await?;
|
||||
|
||||
let mut proofs = proofs;
|
||||
|
||||
@@ -70,7 +70,7 @@ impl Wallet {
|
||||
for proof in &mut proofs {
|
||||
// Verify that proof DLEQ is valid
|
||||
if proof.dleq.is_some() {
|
||||
let keys = self.fetch_keyset_keys(proof.keyset_id).await?;
|
||||
let keys = self.load_keyset_keys(proof.keyset_id).await?;
|
||||
let key = keys.amount_key(proof.amount).ok_or(Error::AmountKey)?;
|
||||
proof.verify_dleq(key)?;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user