|
@@ -29,14 +29,14 @@ pub use types::{MeltQuote, MintQuote};
|
|
|
pub struct Mint {
|
|
|
/// Mint Url
|
|
|
pub mint_url: UncheckedUrl,
|
|
|
- mint_info: MintInfo,
|
|
|
+ /// Mint Info
|
|
|
+ pub mint_info: MintInfo,
|
|
|
+ /// Mint Storage backend
|
|
|
+ pub localstore: Arc<dyn MintDatabase<Err = cdk_database::Error> + Send + Sync>,
|
|
|
+ /// Active Mint Keysets
|
|
|
keysets: Arc<RwLock<HashMap<Id, MintKeySet>>>,
|
|
|
secp_ctx: Secp256k1<secp256k1::All>,
|
|
|
xpriv: ExtendedPrivKey,
|
|
|
- /// Mint Expected [`FeeReserve`]
|
|
|
- pub fee_reserve: FeeReserve,
|
|
|
- /// Mint Storage backend
|
|
|
- pub localstore: Arc<dyn MintDatabase<Err = cdk_database::Error> + Send + Sync>,
|
|
|
}
|
|
|
|
|
|
impl Mint {
|
|
@@ -46,46 +46,105 @@ impl Mint {
|
|
|
seed: &[u8],
|
|
|
mint_info: MintInfo,
|
|
|
localstore: Arc<dyn MintDatabase<Err = cdk_database::Error> + Send + Sync>,
|
|
|
- min_fee_reserve: Amount,
|
|
|
- percent_fee_reserve: f32,
|
|
|
- input_fee_ppk: u64,
|
|
|
+ // Hashmap where the key is the unit and value is (input fee ppk, max_order)
|
|
|
+ supported_units: HashMap<CurrencyUnit, (u64, u8)>,
|
|
|
) -> Result<Self, Error> {
|
|
|
let secp_ctx = Secp256k1::new();
|
|
|
let xpriv =
|
|
|
ExtendedPrivKey::new_master(bitcoin::Network::Bitcoin, seed).expect("RNG busted");
|
|
|
|
|
|
- let mut keysets = HashMap::new();
|
|
|
+ let mut active_keysets = HashMap::new();
|
|
|
let keysets_infos = localstore.get_keyset_infos().await?;
|
|
|
|
|
|
match keysets_infos.is_empty() {
|
|
|
false => {
|
|
|
- for keyset_info in keysets_infos {
|
|
|
- let mut keyset_info = keyset_info;
|
|
|
- keyset_info.input_fee_ppk = input_fee_ppk;
|
|
|
- localstore.add_keyset_info(keyset_info.clone()).await?;
|
|
|
- if keyset_info.active {
|
|
|
+ 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<CurrencyUnit, Vec<MintKeySetInfo>> =
|
|
|
+ keysets_infos.iter().fold(HashMap::new(), |mut acc, ks| {
|
|
|
+ acc.entry(ks.unit).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<MintKeySetInfo> = 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
|
|
|
+ {
|
|
|
+ let id = highest_index_keyset.id;
|
|
|
+ let keyset = MintKeySet::generate_from_xpriv(
|
|
|
+ &secp_ctx,
|
|
|
+ xpriv,
|
|
|
+ highest_index_keyset.clone(),
|
|
|
+ );
|
|
|
+ active_keysets.insert(id, keyset);
|
|
|
+ let mut keyset_info = highest_index_keyset;
|
|
|
+ keyset_info.active = true;
|
|
|
+ localstore.add_keyset_info(keyset_info).await?;
|
|
|
+ localstore.add_active_keyset(unit, id).await?;
|
|
|
+ continue;
|
|
|
+ } else {
|
|
|
+ highest_index_keyset.derivation_path_index.unwrap_or(0) + 1
|
|
|
+ };
|
|
|
+
|
|
|
+ let derivation_path =
|
|
|
+ derivation_path_from_unit(unit, derivation_path_index);
|
|
|
+
|
|
|
+ let (keyset, keyset_info) = create_new_keyset(
|
|
|
+ &secp_ctx,
|
|
|
+ xpriv,
|
|
|
+ derivation_path,
|
|
|
+ Some(derivation_path_index),
|
|
|
+ unit,
|
|
|
+ *max_order,
|
|
|
+ *input_fee_ppk,
|
|
|
+ );
|
|
|
+
|
|
|
let id = keyset_info.id;
|
|
|
- let keyset = MintKeySet::generate_from_xpriv(&secp_ctx, xpriv, keyset_info);
|
|
|
- keysets.insert(id, keyset);
|
|
|
+ localstore.add_keyset_info(keyset_info).await?;
|
|
|
+ localstore.add_active_keyset(unit, id).await?;
|
|
|
+ active_keysets.insert(id, keyset);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
true => {
|
|
|
- let derivation_path = DerivationPath::from(vec![
|
|
|
- ChildNumber::from_hardened_idx(0).expect("0 is a valid index")
|
|
|
- ]);
|
|
|
- let (keyset, keyset_info) = create_new_keyset(
|
|
|
- &secp_ctx,
|
|
|
- xpriv,
|
|
|
- derivation_path,
|
|
|
- CurrencyUnit::Sat,
|
|
|
- 64,
|
|
|
- input_fee_ppk,
|
|
|
- );
|
|
|
- let id = keyset_info.id;
|
|
|
- localstore.add_keyset_info(keyset_info).await?;
|
|
|
- localstore.add_active_keyset(CurrencyUnit::Sat, id).await?;
|
|
|
- keysets.insert(id, keyset);
|
|
|
+ for (unit, (input_fee_ppk, max_order)) in supported_units {
|
|
|
+ let derivation_path = derivation_path_from_unit(unit, 0);
|
|
|
+ tracing::debug!("Der: {}", derivation_path);
|
|
|
+ let (keyset, keyset_info) = create_new_keyset(
|
|
|
+ &secp_ctx,
|
|
|
+ xpriv,
|
|
|
+ derivation_path,
|
|
|
+ Some(0),
|
|
|
+ unit,
|
|
|
+ max_order,
|
|
|
+ input_fee_ppk,
|
|
|
+ );
|
|
|
+ let id = keyset_info.id;
|
|
|
+ localstore.add_keyset_info(keyset_info).await?;
|
|
|
+ localstore.add_active_keyset(CurrencyUnit::Sat, id).await?;
|
|
|
+ active_keysets.insert(id, keyset);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -93,14 +152,10 @@ impl Mint {
|
|
|
|
|
|
Ok(Self {
|
|
|
mint_url,
|
|
|
- keysets: Arc::new(RwLock::new(keysets)),
|
|
|
+ keysets: Arc::new(RwLock::new(active_keysets)),
|
|
|
secp_ctx,
|
|
|
xpriv,
|
|
|
localstore,
|
|
|
- fee_reserve: FeeReserve {
|
|
|
- min_fee_reserve,
|
|
|
- percent_fee_reserve,
|
|
|
- },
|
|
|
mint_info,
|
|
|
})
|
|
|
}
|
|
@@ -366,14 +421,16 @@ impl Mint {
|
|
|
pub async fn rotate_keyset(
|
|
|
&self,
|
|
|
unit: CurrencyUnit,
|
|
|
- derivation_path: DerivationPath,
|
|
|
+ derivation_path_index: u32,
|
|
|
max_order: u8,
|
|
|
input_fee_ppk: u64,
|
|
|
) -> Result<(), Error> {
|
|
|
+ let derivation_path = derivation_path_from_unit(unit, derivation_path_index);
|
|
|
let (keyset, keyset_info) = create_new_keyset(
|
|
|
&self.secp_ctx,
|
|
|
self.xpriv,
|
|
|
derivation_path,
|
|
|
+ Some(derivation_path_index),
|
|
|
unit,
|
|
|
max_order,
|
|
|
input_fee_ppk,
|
|
@@ -1019,8 +1076,10 @@ pub struct MintKeySetInfo {
|
|
|
/// When the Keyset is valid to
|
|
|
/// This is not shown to the wallet and can only be used internally
|
|
|
pub valid_to: Option<u64>,
|
|
|
- /// [`DerivationPath`] of Keyset
|
|
|
+ /// [`DerivationPath`] keyset
|
|
|
pub derivation_path: DerivationPath,
|
|
|
+ /// DerivationPath index of Keyset
|
|
|
+ pub derivation_path_index: Option<u32>,
|
|
|
/// Max order of keyset
|
|
|
pub max_order: u8,
|
|
|
/// Input Fee ppk
|
|
@@ -1049,6 +1108,7 @@ fn create_new_keyset<C: secp256k1::Signing>(
|
|
|
secp: &secp256k1::Secp256k1<C>,
|
|
|
xpriv: ExtendedPrivKey,
|
|
|
derivation_path: DerivationPath,
|
|
|
+ derivation_path_index: Option<u32>,
|
|
|
unit: CurrencyUnit,
|
|
|
max_order: u8,
|
|
|
input_fee_ppk: u64,
|
|
@@ -1068,8 +1128,17 @@ fn create_new_keyset<C: secp256k1::Signing>(
|
|
|
valid_from: unix_time(),
|
|
|
valid_to: None,
|
|
|
derivation_path,
|
|
|
+ derivation_path_index,
|
|
|
max_order,
|
|
|
input_fee_ppk,
|
|
|
};
|
|
|
(keyset, keyset_info)
|
|
|
}
|
|
|
+
|
|
|
+fn derivation_path_from_unit(unit: CurrencyUnit, index: u32) -> DerivationPath {
|
|
|
+ DerivationPath::from(vec![
|
|
|
+ ChildNumber::from_hardened_idx(0).expect("0 is a valid index"),
|
|
|
+ ChildNumber::from_hardened_idx(unit.derivation_index()).expect("0 is a valid index"),
|
|
|
+ ChildNumber::from_hardened_idx(index).expect("0 is a valid index"),
|
|
|
+ ])
|
|
|
+}
|