Skip to content

Commit

Permalink
chore: use deterministic secrets for mint and split
Browse files Browse the repository at this point in the history
  • Loading branch information
ngutech21 committed Mar 29, 2024
1 parent 2e22627 commit 8ca03ec
Show file tree
Hide file tree
Showing 5 changed files with 173 additions and 34 deletions.
1 change: 1 addition & 0 deletions moksha-core/src/dhke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ impl Dhke {
.ok_or(MokshaCoreError::NoValidPointFound)
}

// FIXME: use SecretKey instead of &[u8] for blinding factor
pub fn step1_alice(
&self,
secret_msg: impl Into<String>,
Expand Down
29 changes: 27 additions & 2 deletions moksha-wallet/src/localstore/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,33 @@ pub trait LocalStore {
) -> Result<(), MokshaWalletError>;
async fn get_proofs(&self, tx: &mut RexieTransaction) -> Result<Proofs, MokshaWalletError>;

async fn get_keysets(&self) -> Result<Vec<WalletKeyset>, MokshaWalletError>;
async fn add_keyset(&self, keyset: &WalletKeyset) -> Result<(), MokshaWalletError>;
async fn get_keysets(
&self,
_tx: &mut RexieTransaction,
) -> Result<Vec<WalletKeyset>, MokshaWalletError>;

async fn upsert_keyset(
&self,
_tx: &mut RexieTransaction,
keyset: &WalletKeyset,
) -> Result<(), MokshaWalletError>;

async fn update_keyset_last_index(
&self,
_tx: &mut RexieTransaction,
keyset: &WalletKeyset,
) -> Result<(), MokshaWalletError>;

async fn add_seed(
&self,
_tx: &mut RexieTransaction,
seed_words: &str,
) -> Result<(), MokshaWalletError>;

async fn get_seed(
&self,
_tx: &mut RexieTransaction,
) -> Result<Option<String>, MokshaWalletError>;
}

#[cfg(test)]
Expand Down
40 changes: 31 additions & 9 deletions moksha-wallet/src/localstore/rexie.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,19 +110,41 @@ impl LocalStore for RexieLocalStore {
Ok(())
}

async fn get_keysets(&self) -> std::result::Result<Vec<WalletKeyset>, MokshaWalletError> {
// FIXME todo implement
Ok(vec![WalletKeyset {
id: "id".to_string(),
mint_url: "mint_url".to_string(),
}])
async fn get_keysets(
&self,
_tx: &mut RexieTransaction,
) -> std::result::Result<Vec<WalletKeyset>, MokshaWalletError> {
todo!()
}

async fn add_keyset(
async fn upsert_keyset(
&self,
_tx: &mut RexieTransaction,
_keyset: &WalletKeyset,
) -> std::result::Result<(), MokshaWalletError> {
// FIXME todo implement
Ok(())
todo!()
}

async fn update_keyset_last_index(
&self,
_tx: &mut RexieTransaction,
_keyset: &WalletKeyset,
) -> std::result::Result<(), MokshaWalletError> {
todo!()
}

async fn add_seed(
&self,
_tx: &mut RexieTransaction,
_seed_words: &str,
) -> std::result::Result<(), MokshaWalletError> {
todo!()
}

async fn get_seed(
&self,
_tx: &mut RexieTransaction,
) -> std::result::Result<Option<String>, MokshaWalletError> {
todo!()
}
}
23 changes: 23 additions & 0 deletions moksha-wallet/src/secret.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ pub struct DeterministicSecret {
pub seed: Seed,
}

impl Clone for DeterministicSecret {
fn clone(&self) -> Self {
Self {
seed: Seed::new(*self.seed.as_bytes()),
}
}
}

impl DeterministicSecret {
pub fn from_seed_words(seed_words: &str) -> Result<Self, MokshaWalletError> {
let mnemonic = Mnemonic::from_str(seed_words)?;
Expand Down Expand Up @@ -55,6 +63,21 @@ impl DeterministicSecret {
Ok(hex::encode(key))
}

pub fn derive_range(
&self,
keyset_id: u32,
start: u32,
length: u32,
) -> Result<Vec<(String, SecretKey)>, MokshaWalletError> {
Ok((start..start + length)
.map(|i| {
let key = self.derive_secret(keyset_id, i).unwrap();
let blinding_factor = self.derive_blinding_factor(keyset_id, i).unwrap();
(key, blinding_factor)
})
.collect::<Vec<(String, SecretKey)>>())
}

pub fn derive_blinding_factor(
&self,
keyset_id: u32,
Expand Down
114 changes: 91 additions & 23 deletions moksha-wallet/src/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use crate::{
error::MokshaWalletError,
http::CrossPlatformHttpClient,
localstore::{LocalStore, WalletKeyset},
secret::DeterministicSecret,
secret::{self, DeterministicSecret},
};
use lightning_invoice::Bolt11Invoice as LNInvoice;
use std::str::FromStr;
Expand All @@ -37,6 +37,7 @@ where
dhke: Dhke,
localstore: L,
mint_url: Url,
secret: DeterministicSecret,
}

pub struct WalletBuilder<L, C: CashuClient = CrossPlatformHttpClient>
Expand Down Expand Up @@ -111,6 +112,7 @@ where
let wallet_keyset = WalletKeyset::new(&m.id, &mint_url, &m.unit, 0, public_keys, true);
localstore.upsert_keyset(&mut tx, &wallet_keyset).await?;
}
let seed = localstore.get_seed(&mut tx).await?.expect("seed not found");
tx.commit().await?;

// FIXME store all keysets
Expand All @@ -134,6 +136,7 @@ where
key_response.clone(),
localstore,
mint_url,
DeterministicSecret::from_seed_words(&seed)?,
))
}
}
Expand All @@ -159,6 +162,7 @@ where
key_response: KeyResponse,
localstore: L,
mint_url: Url,
secret: DeterministicSecret,
) -> Self {
Self {
client,
Expand All @@ -167,6 +171,7 @@ where
dhke: Dhke::new(),
localstore,
mint_url,
secret,
}
}

Expand Down Expand Up @@ -426,20 +431,55 @@ where
Ok(melt_response)
}

async fn create_secrets(
&self,
amount: u32,
) -> Result<Vec<(String, SecretKey)>, MokshaWalletError> {
let keyset_id = self.keyset_id.clone().id;
let keyset_id_int = secret::convert_hex_to_int(&keyset_id).unwrap(); // FIXME

let mut tx = self.localstore.begin_tx().await?;
let all_keysets = self.localstore.get_keysets(&mut tx).await?;
let keyset = all_keysets
.iter()
.find(|k| k.keyset_id == keyset_id)
.expect("keyset not found");

let start_index = (keyset.last_index + 1) as u32;
let secret_range = self
.secret
.derive_range(keyset_id_int, start_index, amount)?;

self.localstore
.update_keyset_last_index(
&mut tx,
&WalletKeyset {
last_index: (start_index + amount) as u64,
..keyset.clone()
},
)
.await?;
Ok(secret_range)
}

pub async fn split_tokens(
&self,
tokens: &TokenV3,
splt_amount: Amount,
) -> Result<(TokenV3, TokenV3), MokshaWalletError> {
let total_token_amount = tokens.total_amount();
let first_amount: Amount = (total_token_amount - splt_amount.0).into();
let first_secrets = first_amount.split().create_secrets();
let first_secrets = self
.create_secrets(first_amount.split().len() as u32)
.await?;
let first_outputs = self.create_blinded_messages(first_amount, &first_secrets)?;

// ############################################################################

let second_amount = splt_amount.clone();
let second_secrets = second_amount.split().create_secrets();
let second_secrets = self
.create_secrets(second_amount.split().len() as u32)
.await?;
let second_outputs = self.create_blinded_messages(second_amount, &second_secrets)?;

let mut total_outputs = vec![];
Expand All @@ -463,6 +503,8 @@ where
let secrets = [first_secrets, second_secrets].concat();
let outputs = [first_outputs, second_outputs].concat();

let secrets = secrets.into_iter().map(|(s, _)| s).collect::<Vec<String>>();

let proofs = self
.create_proofs_from_blinded_signatures(split_result.signatures, secrets, outputs)?
.proofs();
Expand Down Expand Up @@ -530,23 +572,53 @@ where
quote_id: String,
) -> Result<TokenV3, MokshaWalletError> {
let split_amount = amount.split();
let secrets = split_amount.create_secrets();

// FIXME cleanup code
let keyset_id = self.keyset_id.clone().id;
let keyset_id_int = secret::convert_hex_to_int(&keyset_id).unwrap(); // FIXME

let mut tx = self.localstore.begin_tx().await?;
let all_keysets = self.localstore.get_keysets(&mut tx).await?;
let keyset = all_keysets
.iter()
.find(|k| k.keyset_id == keyset_id)
.expect("keyset not found");

let start_index = (keyset.last_index + 1) as u32;
let secret_range =
self.secret
.derive_range(keyset_id_int, start_index, split_amount.len() as u32)?;

self.localstore
.update_keyset_last_index(
&mut tx,
&WalletKeyset {
last_index: (start_index + split_amount.len() as u32) as u64,
..keyset.clone()
},
)
.await?;
tx.commit().await?;

let blinded_messages = split_amount
.into_iter()
.zip(secrets.clone())
.map(|(amount, secret)| {
let (b_, alice_secret_key) = self.dhke.step1_alice(secret, None).unwrap(); // FIXME
.zip(secret_range)
.map(|(amount, (secret, blinding_factor))| {
let (b_, alice_secret_key) = self
.dhke
.step1_alice(&secret, Some(&blinding_factor.secret_bytes()))
.unwrap(); // FIXME
(
BlindedMessage {
amount,
b_,
id: self.keyset_id.clone().id,
},
alice_secret_key,
secret,
)
})
.collect::<Vec<(BlindedMessage, SecretKey)>>();
.collect::<Vec<(BlindedMessage, SecretKey, String)>>();

let signatures = match payment_method {
PaymentMethod::Bolt11 => {
Expand All @@ -558,7 +630,7 @@ where
blinded_messages
.clone()
.into_iter()
.map(|(msg, _)| msg)
.map(|(msg, _, _)| msg)
.collect::<Vec<BlindedMessage>>(),
)
.await?;
Expand All @@ -573,7 +645,7 @@ where
blinded_messages
.clone()
.into_iter()
.map(|(msg, _)| msg)
.map(|(msg, _, _)| msg)
.collect::<Vec<BlindedMessage>>(),
)
.await?;
Expand All @@ -584,17 +656,10 @@ where
// step 3: unblind signatures
let current_keyset_id = self.keyset_id.clone().id; // FIXME

let private_keys = blinded_messages
.clone()
.into_iter()
.map(|(_, secret)| secret)
.collect::<Vec<SecretKey>>();

let proofs = signatures
.iter()
.zip(private_keys)
.zip(secrets)
.map(|((p, priv_key), secret)| {
.zip(blinded_messages)
.map(|(p, (_, priv_key, secret))| {
let key = self
.keyset
.keys
Expand All @@ -620,15 +685,18 @@ where
fn create_blinded_messages(
&self,
amount: Amount,
secrets: &[String],
secrets_factors: &Vec<(String, SecretKey)>,
) -> Result<Vec<(BlindedMessage, SecretKey)>, MokshaWalletError> {
let split_amount = amount.split();

Ok(split_amount
.into_iter()
.zip(secrets)
.map(|(amount, secret)| {
let (b_, alice_secret_key) = self.dhke.step1_alice(secret, None).unwrap(); // FIXME
.zip(secrets_factors)
.map(|(amount, (secret, blinding_factor))| {
let (b_, alice_secret_key) = self
.dhke
.step1_alice(secret, Some(&blinding_factor.secret_bytes()))
.unwrap(); // FIXME
(
BlindedMessage {
amount,
Expand Down

0 comments on commit 8ca03ec

Please sign in to comment.