mirror of
https://github.com/aljazceru/cdk.git
synced 2026-01-06 06:26:11 +01:00
feat: use derivation path by unit
This commit is contained in:
@@ -213,14 +213,16 @@ async fn main() -> anyhow::Result<()> {
|
||||
|
||||
let input_fee_ppk = settings.info.input_fee_ppk.unwrap_or(0);
|
||||
|
||||
let mut supported_units = HashMap::new();
|
||||
|
||||
supported_units.insert(CurrencyUnit::Sat, (input_fee_ppk, 64));
|
||||
|
||||
let mint = Mint::new(
|
||||
&settings.info.url,
|
||||
&mnemonic.to_seed_normalized(""),
|
||||
mint_info,
|
||||
localstore,
|
||||
absolute_ln_fee_reserve,
|
||||
relative_ln_fee,
|
||||
input_fee_ppk,
|
||||
supported_units,
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE keyset ADD derivation_path_index INTEGER;
|
||||
@@ -408,8 +408,8 @@ WHERE id=?
|
||||
sqlx::query(
|
||||
r#"
|
||||
INSERT OR REPLACE INTO keyset
|
||||
(id, unit, active, valid_from, valid_to, derivation_path, max_order, input_fee_ppk)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?);
|
||||
(id, unit, active, valid_from, valid_to, derivation_path, max_order, input_fee_ppk, derivation_path_index)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);
|
||||
"#,
|
||||
)
|
||||
.bind(keyset.id.to_string())
|
||||
@@ -420,6 +420,7 @@ VALUES (?, ?, ?, ?, ?, ?, ?, ?);
|
||||
.bind(keyset.derivation_path.to_string())
|
||||
.bind(keyset.max_order)
|
||||
.bind(keyset.input_fee_ppk as i64)
|
||||
.bind(keyset.derivation_path_index)
|
||||
.execute(&self.pool)
|
||||
.await
|
||||
.map_err(Error::from)?;
|
||||
@@ -716,6 +717,8 @@ fn sqlite_row_to_keyset_info(row: SqliteRow) -> Result<MintKeySetInfo, Error> {
|
||||
let row_derivation_path: String = row.try_get("derivation_path").map_err(Error::from)?;
|
||||
let row_max_order: u8 = row.try_get("max_order").map_err(Error::from)?;
|
||||
let row_keyset_ppk: Option<i64> = row.try_get("input_fee_ppk").map_err(Error::from)?;
|
||||
let row_derivation_path_index: Option<i64> =
|
||||
row.try_get("derivation_path_index").map_err(Error::from)?;
|
||||
|
||||
Ok(MintKeySetInfo {
|
||||
id: Id::from_str(&row_id).map_err(Error::from)?,
|
||||
@@ -724,6 +727,7 @@ fn sqlite_row_to_keyset_info(row: SqliteRow) -> Result<MintKeySetInfo, Error> {
|
||||
valid_from: row_valid_from as u64,
|
||||
valid_to: row_valid_to.map(|v| v as u64),
|
||||
derivation_path: DerivationPath::from_str(&row_derivation_path).map_err(Error::from)?,
|
||||
derivation_path_index: row_derivation_path_index.map(|d| d as u32),
|
||||
max_order: row_max_order,
|
||||
input_fee_ppk: row_keyset_ppk.unwrap_or(0) as u64,
|
||||
})
|
||||
|
||||
@@ -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"),
|
||||
])
|
||||
}
|
||||
|
||||
@@ -325,6 +325,19 @@ pub enum CurrencyUnit {
|
||||
Eur,
|
||||
}
|
||||
|
||||
#[cfg(feature = "mint")]
|
||||
impl CurrencyUnit {
|
||||
/// Derivation index mint will use for unit
|
||||
pub fn derivation_index(&self) -> u32 {
|
||||
match self {
|
||||
Self::Sat => 0,
|
||||
Self::Msat => 1,
|
||||
Self::Usd => 2,
|
||||
Self::Eur => 3,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for CurrencyUnit {
|
||||
type Err = Error;
|
||||
fn from_str(value: &str) -> Result<Self, Self::Err> {
|
||||
|
||||
Reference in New Issue
Block a user