keysets.rs 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. use std::collections::HashMap;
  2. use cdk_common::amount::{FeeAndAmounts, KeysetFeeAndAmounts};
  3. use cdk_common::nut02::{KeySetInfos, KeySetInfosMethods};
  4. use tracing::instrument;
  5. use crate::nuts::{Id, KeySetInfo, Keys};
  6. use crate::{Error, Wallet};
  7. impl Wallet {
  8. /// Load keys for mint keyset
  9. ///
  10. /// Returns keys from KeyManager cache if available.
  11. /// If keys are not cached, triggers a refresh and waits briefly before checking again.
  12. #[instrument(skip(self))]
  13. pub async fn load_keyset_keys(&self, keyset_id: Id) -> Result<Keys, Error> {
  14. Ok((*self.key_manager.get_keys(&keyset_id).await?).clone())
  15. }
  16. /// Get keysets from KeyManager cache or trigger refresh if missing
  17. ///
  18. /// First checks the KeyManager cache for keysets. If keysets are not cached,
  19. /// triggers a refresh from the mint and waits briefly before checking again.
  20. /// This is the main method for getting keysets in token operations that can work offline
  21. /// but will fall back to online if needed.
  22. #[instrument(skip(self))]
  23. pub async fn load_mint_keysets(&self) -> Result<Vec<KeySetInfo>, Error> {
  24. Ok(self
  25. .key_manager
  26. .get_keysets()
  27. .await?
  28. .into_iter()
  29. .filter(|x| x.unit == self.unit && x.active)
  30. .collect::<Vec<_>>())
  31. }
  32. /// Get keysets from KeyManager cache only - pure offline operation
  33. ///
  34. /// Only checks the KeyManager cache for keysets. If keysets are not cached,
  35. /// returns an error without going online. This is used for operations that must remain
  36. /// offline and rely on previously cached keyset data.
  37. #[instrument(skip(self))]
  38. pub async fn get_mint_keysets(&self) -> Result<Vec<KeySetInfo>, Error> {
  39. let keysets = self
  40. .key_manager
  41. .get_keysets()
  42. .await?
  43. .into_iter()
  44. .filter(|k| k.unit == self.unit && k.active)
  45. .collect::<Vec<_>>();
  46. if !keysets.is_empty() {
  47. Ok(keysets)
  48. } else {
  49. Err(Error::UnknownKeySet)
  50. }
  51. }
  52. /// Refresh keysets by fetching the latest from mint - always goes online
  53. ///
  54. /// This method triggers a KeyManager refresh which fetches the latest keyset
  55. /// information from the mint. The KeyManager handles updating the cache and database.
  56. /// This is used when operations need the most up-to-date keyset information.
  57. #[instrument(skip(self))]
  58. pub async fn refresh_keysets(&self) -> Result<KeySetInfos, Error> {
  59. tracing::debug!("Refreshing keysets via KeyManager");
  60. let keysets = self
  61. .key_manager
  62. .refresh()
  63. .await?
  64. .into_iter()
  65. .filter(|k| k.unit == self.unit && k.active)
  66. .collect::<Vec<_>>();
  67. if !keysets.is_empty() {
  68. Ok(keysets)
  69. } else {
  70. Err(Error::UnknownKeySet)
  71. }
  72. }
  73. /// Get the active keyset with the lowest fees - always goes online
  74. ///
  75. /// This method always goes online to refresh keysets from the mint and then returns
  76. /// the active keyset with the minimum input fees. Use this when you need the most
  77. /// up-to-date keyset information for operations.
  78. #[instrument(skip(self))]
  79. pub async fn fetch_active_keyset(&self) -> Result<KeySetInfo, Error> {
  80. self.refresh_keysets()
  81. .await?
  82. .active()
  83. .min_by_key(|k| k.input_fee_ppk)
  84. .cloned()
  85. .ok_or(Error::NoActiveKeyset)
  86. }
  87. /// Get the active keyset with the lowest fees from KeyManager cache - offline operation
  88. ///
  89. /// Returns the active keyset with minimum input fees from the KeyManager cache.
  90. /// This is an offline operation that does not contact the mint. If no keysets are cached,
  91. /// returns an error. Use this for offline operations or when you want to avoid network calls.
  92. #[instrument(skip(self))]
  93. pub async fn get_active_keyset(&self) -> Result<KeySetInfo, Error> {
  94. let active_keysets = self.key_manager.get_active_keysets().await?;
  95. active_keysets
  96. .into_iter()
  97. .min_by_key(|k| k.input_fee_ppk)
  98. .map(|ks| (*ks).clone())
  99. .ok_or(Error::NoActiveKeyset)
  100. }
  101. /// Get keyset fees and amounts for mint from KeyManager cache - offline operation
  102. ///
  103. /// Returns a HashMap of keyset IDs to their input fee rates (per-proof-per-thousand)
  104. /// from the KeyManager cache. This is an offline operation that does not contact the mint.
  105. /// If no keysets are cached, returns an error.
  106. pub async fn get_keyset_fees_and_amounts(&self) -> Result<KeysetFeeAndAmounts, Error> {
  107. let keysets = self.key_manager.get_keysets().await?;
  108. let mut fees = HashMap::new();
  109. for keyset in keysets {
  110. let keys = self.load_keyset_keys(keyset.id).await?;
  111. fees.insert(
  112. keyset.id,
  113. (
  114. keyset.input_fee_ppk,
  115. keys.iter()
  116. .map(|(amount, _)| amount.to_u64())
  117. .collect::<Vec<_>>(),
  118. )
  119. .into(),
  120. );
  121. }
  122. Ok(fees)
  123. }
  124. /// Get keyset fees and amounts for mint by keyset id from local database only - offline operation
  125. ///
  126. /// Returns the input fee rate (per-proof-per-thousand) for a specific keyset ID from
  127. /// cached keysets in the local database. This is an offline operation that does not
  128. /// contact the mint. If the keyset is not found locally, returns an error.
  129. pub async fn get_keyset_fees_and_amounts_by_id(
  130. &self,
  131. keyset_id: Id,
  132. ) -> Result<FeeAndAmounts, Error> {
  133. self.get_keyset_fees_and_amounts()
  134. .await?
  135. .get(&keyset_id)
  136. .cloned()
  137. .ok_or(Error::UnknownKeySet)
  138. }
  139. }