Skip to content

Commit

Permalink
feat: use derivation path by unit
Browse files Browse the repository at this point in the history
  • Loading branch information
thesimplekid committed Jul 11, 2024
1 parent 17263b0 commit 6d3200c
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 43 deletions.
8 changes: 5 additions & 3 deletions crates/cdk-mintd/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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?;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE keyset ADD derivation_path_index INTEGER;
8 changes: 6 additions & 2 deletions crates/cdk-sqlite/src/mint/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand All @@ -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)?;
Expand Down Expand Up @@ -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)?,
Expand All @@ -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,
})
Expand Down
145 changes: 107 additions & 38 deletions crates/cdk/src/mint/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -46,61 +46,116 @@ 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);
}
}
}

let mint_url = UncheckedUrl::from(mint_url);

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,
})
}
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand All @@ -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"),
])
}
13 changes: 13 additions & 0 deletions crates/cdk/src/nuts/nut00/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down

0 comments on commit 6d3200c

Please sign in to comment.