common.rs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. use std::collections::HashMap;
  2. use std::sync::Arc;
  3. use bitcoin::bip32::{ChildNumber, DerivationPath, Xpriv};
  4. use bitcoin::secp256k1::{self, All, Secp256k1};
  5. use cdk_common::database;
  6. use cdk_common::error::Error;
  7. use cdk_common::mint::MintKeySetInfo;
  8. use cdk_common::nuts::{CurrencyUnit, Id, MintKeySet};
  9. use cdk_common::util::unix_time;
  10. /// Initialize keysets and returns a [`Result`] with a tuple of the following:
  11. /// * a [`HashMap`] mapping each active keyset `Id` to `MintKeySet`
  12. /// * a [`Vec`] of `CurrencyUnit` containing active keysets units
  13. pub async fn init_keysets(
  14. xpriv: Xpriv,
  15. secp_ctx: &Secp256k1<All>,
  16. localstore: &Arc<dyn database::MintKeysDatabase<Err = database::Error> + Send + Sync>,
  17. supported_units: &HashMap<CurrencyUnit, (u64, u8)>,
  18. custom_paths: &HashMap<CurrencyUnit, DerivationPath>,
  19. ) -> Result<(HashMap<Id, MintKeySet>, Vec<CurrencyUnit>), Error> {
  20. let mut active_keysets: HashMap<Id, MintKeySet> = HashMap::new();
  21. let mut active_keyset_units: Vec<CurrencyUnit> = vec![];
  22. // Get keysets info from DB
  23. let keysets_infos = localstore.get_keyset_infos().await?;
  24. let mut tx = localstore.begin_transaction().await?;
  25. if !keysets_infos.is_empty() {
  26. tracing::debug!("Setting all saved keysets to inactive");
  27. for keyset in keysets_infos.clone() {
  28. // Set all to in active
  29. let mut keyset = keyset;
  30. keyset.active = false;
  31. tx.add_keyset_info(keyset).await?;
  32. }
  33. let keysets_by_unit: HashMap<CurrencyUnit, Vec<MintKeySetInfo>> =
  34. keysets_infos.iter().fold(HashMap::new(), |mut acc, ks| {
  35. acc.entry(ks.unit.clone()).or_default().push(ks.clone());
  36. acc
  37. });
  38. for (unit, keysets) in keysets_by_unit {
  39. let mut keysets = keysets;
  40. keysets.sort_by(|a, b| b.derivation_path_index.cmp(&a.derivation_path_index));
  41. // Get the keyset with the highest counter
  42. let highest_index_keyset = keysets
  43. .first()
  44. .cloned()
  45. .expect("unit will not be added to hashmap if empty");
  46. let keysets: Vec<MintKeySetInfo> = keysets
  47. .into_iter()
  48. .filter(|ks| ks.derivation_path_index.is_some())
  49. .collect();
  50. if let Some((input_fee_ppk, max_order)) = supported_units.get(&unit) {
  51. if !keysets.is_empty()
  52. && &highest_index_keyset.input_fee_ppk == input_fee_ppk
  53. && highest_index_keyset.amounts.len() == (*max_order as usize)
  54. {
  55. tracing::debug!("Current highest index keyset matches expect fee and max order. Setting active");
  56. let id = highest_index_keyset.id;
  57. let keyset = MintKeySet::generate_from_xpriv(
  58. secp_ctx,
  59. xpriv,
  60. &highest_index_keyset.amounts,
  61. highest_index_keyset.unit.clone(),
  62. highest_index_keyset.derivation_path.clone(),
  63. highest_index_keyset.final_expiry,
  64. cdk_common::nut02::KeySetVersion::Version00,
  65. );
  66. active_keysets.insert(id, keyset);
  67. let mut keyset_info = highest_index_keyset;
  68. keyset_info.active = true;
  69. tx.add_keyset_info(keyset_info).await?;
  70. active_keyset_units.push(unit.clone());
  71. tx.set_active_keyset(unit, id).await?;
  72. } else {
  73. // Check to see if there are not keysets by this unit
  74. let derivation_path_index = if keysets.is_empty() {
  75. 1
  76. } else {
  77. highest_index_keyset.derivation_path_index.unwrap_or(0) + 1
  78. };
  79. let derivation_path = match custom_paths.get(&unit) {
  80. Some(path) => path.clone(),
  81. None => derivation_path_from_unit(unit.clone(), derivation_path_index)
  82. .ok_or(Error::UnsupportedUnit)?,
  83. };
  84. let (keyset, keyset_info) = create_new_keyset(
  85. secp_ctx,
  86. xpriv,
  87. derivation_path,
  88. Some(derivation_path_index),
  89. unit.clone(),
  90. &highest_index_keyset.amounts,
  91. *input_fee_ppk,
  92. // TODO: add Mint settings for a final expiry of newly generated keysets
  93. None,
  94. );
  95. let id = keyset_info.id;
  96. tx.add_keyset_info(keyset_info).await?;
  97. tx.set_active_keyset(unit.clone(), id).await?;
  98. active_keysets.insert(id, keyset);
  99. active_keyset_units.push(unit.clone());
  100. };
  101. }
  102. }
  103. }
  104. tx.commit().await?;
  105. Ok((active_keysets, active_keyset_units))
  106. }
  107. /// Generate new [`MintKeySetInfo`] from path
  108. #[tracing::instrument(skip_all)]
  109. #[allow(clippy::too_many_arguments)]
  110. pub fn create_new_keyset<C: secp256k1::Signing>(
  111. secp: &secp256k1::Secp256k1<C>,
  112. xpriv: Xpriv,
  113. derivation_path: DerivationPath,
  114. derivation_path_index: Option<u32>,
  115. unit: CurrencyUnit,
  116. amounts: &[u64],
  117. input_fee_ppk: u64,
  118. final_expiry: Option<u64>,
  119. ) -> (MintKeySet, MintKeySetInfo) {
  120. let keyset = MintKeySet::generate(
  121. secp,
  122. xpriv
  123. .derive_priv(secp, &derivation_path)
  124. .expect("RNG busted"),
  125. unit,
  126. amounts,
  127. final_expiry,
  128. // TODO: change this to Version01 to generate keysets v2
  129. cdk_common::nut02::KeySetVersion::Version00,
  130. );
  131. let keyset_info = MintKeySetInfo {
  132. id: keyset.id,
  133. unit: keyset.unit.clone(),
  134. active: true,
  135. valid_from: unix_time(),
  136. final_expiry: keyset.final_expiry,
  137. derivation_path,
  138. derivation_path_index,
  139. max_order: 0,
  140. amounts: amounts.to_owned(),
  141. input_fee_ppk,
  142. };
  143. (keyset, keyset_info)
  144. }
  145. pub fn derivation_path_from_unit(unit: CurrencyUnit, index: u32) -> Option<DerivationPath> {
  146. let unit_index = unit.derivation_index()?;
  147. Some(DerivationPath::from(vec![
  148. ChildNumber::from_hardened_idx(0).expect("0 is a valid index"),
  149. ChildNumber::from_hardened_idx(unit_index).expect("0 is a valid index"),
  150. ChildNumber::from_hardened_idx(index).expect("0 is a valid index"),
  151. ]))
  152. }