From 692e13ff16751d69cf1404544aa610d8f270c98e Mon Sep 17 00:00:00 2001 From: lollerfirst <43107113+lollerfirst@users.noreply.github.com> Date: Mon, 24 Feb 2025 16:16:50 +0100 Subject: [PATCH] Refactor Keyset Initialization (#615) * reorg keyset startup initialization --- crates/cdk/src/mint/keysets.rs | 109 ++++++++++++++++++++++++++++++++- crates/cdk/src/mint/mod.rs | 93 +++------------------------- 2 files changed, 117 insertions(+), 85 deletions(-) diff --git a/crates/cdk/src/mint/keysets.rs b/crates/cdk/src/mint/keysets.rs index fe139755..b8ed6ac9 100644 --- a/crates/cdk/src/mint/keysets.rs +++ b/crates/cdk/src/mint/keysets.rs @@ -1,6 +1,10 @@ use std::collections::{HashMap, HashSet}; +use std::sync::Arc; -use bitcoin::bip32::DerivationPath; +use bitcoin::bip32::{DerivationPath, Xpriv}; +use bitcoin::key::Secp256k1; +use bitcoin::secp256k1::All; +use cdk_common::database::{self, MintDatabase}; use tracing::instrument; use super::{ @@ -10,6 +14,109 @@ use super::{ use crate::Error; impl Mint { + /// Initialize keysets and returns a [`Result`] with a tuple of the following: + /// * a [`HashMap`] mapping each active keyset `Id` to `MintKeySet` + /// * a [`Vec`] of `CurrencyUnit` containing active keysets units + pub async fn init_keysets( + xpriv: Xpriv, + secp_ctx: &Secp256k1, + localstore: &Arc + Send + Sync>, + supported_units: &HashMap, + custom_paths: &HashMap, + ) -> Result<(HashMap, Vec), Error> { + let mut active_keysets: HashMap = HashMap::new(); + let mut active_keyset_units: Vec = vec![]; + + // Get keysets info from DB + let keysets_infos = localstore.get_keyset_infos().await?; + + if !keysets_infos.is_empty() { + tracing::debug!("Setting all saved keysets to inactive"); + for keyset in keysets_infos.clone() { + // Set all to in active + let mut keyset = keyset; + keyset.active = false; + localstore.add_keyset_info(keyset).await?; + } + + let keysets_by_unit: HashMap> = + keysets_infos.iter().fold(HashMap::new(), |mut acc, ks| { + acc.entry(ks.unit.clone()).or_default().push(ks.clone()); + acc + }); + + for (unit, keysets) in keysets_by_unit { + let mut keysets = keysets; + keysets.sort_by(|a, b| b.derivation_path_index.cmp(&a.derivation_path_index)); + + // Get the keyset with the highest counter + let highest_index_keyset = keysets + .first() + .cloned() + .expect("unit will not be added to hashmap if empty"); + + let keysets: Vec = keysets + .into_iter() + .filter(|ks| ks.derivation_path_index.is_some()) + .collect(); + + if let Some((input_fee_ppk, max_order)) = supported_units.get(&unit) { + if !keysets.is_empty() + && &highest_index_keyset.input_fee_ppk == input_fee_ppk + && &highest_index_keyset.max_order == max_order + { + tracing::debug!("Current highest index keyset matches expect fee and max order. Setting active"); + let id = highest_index_keyset.id; + let keyset = MintKeySet::generate_from_xpriv( + secp_ctx, + xpriv, + highest_index_keyset.max_order, + highest_index_keyset.unit.clone(), + highest_index_keyset.derivation_path.clone(), + ); + active_keysets.insert(id, keyset); + let mut keyset_info = highest_index_keyset; + keyset_info.active = true; + localstore.add_keyset_info(keyset_info).await?; + active_keyset_units.push(unit.clone()); + localstore.set_active_keyset(unit, id).await?; + } else { + // Check to see if there are not keysets by this unit + let derivation_path_index = if keysets.is_empty() { + 1 + } else { + highest_index_keyset.derivation_path_index.unwrap_or(0) + 1 + }; + + let derivation_path = match custom_paths.get(&unit) { + Some(path) => path.clone(), + None => derivation_path_from_unit(unit.clone(), derivation_path_index) + .ok_or(Error::UnsupportedUnit)?, + }; + + let (keyset, keyset_info) = create_new_keyset( + secp_ctx, + xpriv, + derivation_path, + Some(derivation_path_index), + unit.clone(), + *max_order, + *input_fee_ppk, + ); + + let id = keyset_info.id; + localstore.add_keyset_info(keyset_info).await?; + localstore.set_active_keyset(unit.clone(), id).await?; + active_keysets.insert(id, keyset); + active_keyset_units.push(unit.clone()); + }; + } + } + } + + Ok((active_keysets, active_keyset_units)) + } + /// Retrieve the public keys of the active keyset for distribution to wallet /// clients #[instrument(skip(self))] diff --git a/crates/cdk/src/mint/mod.rs b/crates/cdk/src/mint/mod.rs index 6ee908e3..9456514a 100644 --- a/crates/cdk/src/mint/mod.rs +++ b/crates/cdk/src/mint/mod.rs @@ -67,91 +67,16 @@ impl Mint { let secp_ctx = Secp256k1::new(); let xpriv = Xpriv::new_master(bitcoin::Network::Bitcoin, seed).expect("RNG busted"); - let mut active_keysets = HashMap::new(); - let keysets_infos = localstore.get_keyset_infos().await?; - - let mut active_keyset_units = vec![]; - - if !keysets_infos.is_empty() { - tracing::debug!("Setting all saved keysets to inactive"); - for keyset in keysets_infos.clone() { - // Set all to in active - let mut keyset = keyset; - keyset.active = false; - localstore.add_keyset_info(keyset).await?; - } - - let keysets_by_unit: HashMap> = - keysets_infos.iter().fold(HashMap::new(), |mut acc, ks| { - acc.entry(ks.unit.clone()).or_default().push(ks.clone()); - acc - }); - - for (unit, keysets) in keysets_by_unit { - let mut keysets = keysets; - keysets.sort_by(|a, b| b.derivation_path_index.cmp(&a.derivation_path_index)); - - let highest_index_keyset = keysets - .first() - .cloned() - .expect("unit will not be added to hashmap if empty"); - - let keysets: Vec = keysets - .into_iter() - .filter(|ks| ks.derivation_path_index.is_some()) - .collect(); - - if let Some((input_fee_ppk, max_order)) = supported_units.get(&unit) { - let derivation_path_index = if keysets.is_empty() { - 1 - } else if &highest_index_keyset.input_fee_ppk == input_fee_ppk - && &highest_index_keyset.max_order == max_order - { - tracing::debug!("Current highest index keyset matches expect fee and max order. Setting active"); - let id = highest_index_keyset.id; - let keyset = MintKeySet::generate_from_xpriv( - &secp_ctx, - xpriv, - highest_index_keyset.max_order, - highest_index_keyset.unit.clone(), - highest_index_keyset.derivation_path.clone(), - ); - active_keysets.insert(id, keyset); - let mut keyset_info = highest_index_keyset; - keyset_info.active = true; - localstore.add_keyset_info(keyset_info).await?; - active_keyset_units.push(unit.clone()); - localstore.set_active_keyset(unit, id).await?; - continue; - } else { - highest_index_keyset.derivation_path_index.unwrap_or(0) + 1 - }; - - let derivation_path = match custom_paths.get(&unit) { - Some(path) => path.clone(), - None => derivation_path_from_unit(unit.clone(), derivation_path_index) - .ok_or(Error::UnsupportedUnit)?, - }; - - let (keyset, keyset_info) = create_new_keyset( - &secp_ctx, - xpriv, - derivation_path, - Some(derivation_path_index), - unit.clone(), - *max_order, - *input_fee_ppk, - ); - - let id = keyset_info.id; - localstore.add_keyset_info(keyset_info).await?; - localstore.set_active_keyset(unit.clone(), id).await?; - active_keysets.insert(id, keyset); - active_keyset_units.push(unit.clone()); - } - } - } + let (mut active_keysets, active_keyset_units) = Mint::init_keysets( + xpriv, + &secp_ctx, + &localstore, + &supported_units, + &custom_paths, + ) + .await?; + // Create new keysets for supported units that aren't covered by the current keysets for (unit, (fee, max_order)) in supported_units { if !active_keyset_units.contains(&unit) { let derivation_path = match custom_paths.get(&unit) {