Skip to content

Commit

Permalink
feat: v1 keys
Browse files Browse the repository at this point in the history
  • Loading branch information
ngutech21 committed Nov 30, 2023
1 parent 3d24abc commit 583bff6
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 26 deletions.
44 changes: 38 additions & 6 deletions moksha-core/src/keyset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
//!
//! The module also defines a `generate_hash` function for generating a random hash, and several helper functions for deriving keys and keyset IDs.
use hex::ToHex;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

Expand Down Expand Up @@ -40,6 +41,17 @@ pub struct MintKeyset {
}

impl MintKeyset {
pub fn legacy_new(seed: String, derivation_path: String) -> MintKeyset {
let priv_keys = derive_keys(&seed, &derivation_path);
let pub_keys = derive_pubkeys(&priv_keys);
MintKeyset {
private_keys: priv_keys,
keyset_id: legacy_derive_keyset_id(&pub_keys),
public_keys: pub_keys,
mint_pubkey: derive_pubkey(&seed).expect("invalid seed"),
}
}

pub fn new(seed: String, derivation_path: String) -> MintKeyset {
let priv_keys = derive_keys(&seed, &derivation_path);
let pub_keys = derive_pubkeys(&priv_keys);
Expand All @@ -66,7 +78,7 @@ impl Keysets {
&self,
mint_keys: &HashMap<u64, PublicKey>,
) -> Result<String, MokshaCoreError> {
let computed_id = derive_keyset_id(mint_keys);
let computed_id = legacy_derive_keyset_id(mint_keys);
if self.keysets.contains(&computed_id) {
Ok(computed_id)
} else {
Expand Down Expand Up @@ -120,7 +132,7 @@ pub fn derive_pubkeys(keys: &HashMap<u64, SecretKey>) -> HashMap<u64, PublicKey>
/// # Returns
///
/// A string representing the derived keyset ID.
pub fn derive_keyset_id(keys: &HashMap<u64, PublicKey>) -> String {
pub fn legacy_derive_keyset_id(keys: &HashMap<u64, PublicKey>) -> String {
let pubkeys_concat = keys
.iter()
.sorted_by(|(amt_a, _), (amt_b, _)| amt_a.cmp(amt_b))
Expand All @@ -130,7 +142,15 @@ pub fn derive_keyset_id(keys: &HashMap<u64, PublicKey>) -> String {
general_purpose::STANDARD.encode(hashed_pubkeys)[..12].to_string()
}

/// Derives a public key from a given seed.
fn derive_keyset_id(keys: &HashMap<u64, PublicKey>) -> String {
let pubkeys = keys
.iter()
.sorted_by(|(amt_a, _), (amt_b, _)| amt_a.cmp(amt_b))
.map(|(_, pubkey)| pubkey)
.join("");
let hashed_pubkeys: String = sha256::Hash::hash(pubkeys.as_bytes()).encode_hex();
format!("00{}", &hashed_pubkeys[..14])
}
///
/// # Arguments
///
Expand Down Expand Up @@ -180,20 +200,32 @@ mod tests {
assert!(keys.len() == 64);

let pub_keys = super::derive_pubkeys(&keys);
let id = super::derive_keyset_id(&pub_keys);
let id = super::legacy_derive_keyset_id(&pub_keys);
assert_eq!("JHV8eUnoAln/", id);
assert!(id.len() == 12);
Ok(())
}

#[test]
fn test_derive_keys_master_v1() -> anyhow::Result<()> {
let keys = super::derive_keys("supersecretprivatekey", "");
assert!(keys.len() == 64);

let pub_keys = super::derive_pubkeys(&keys);
let id = super::derive_keyset_id(&pub_keys);
//assert_eq!("00d31cecf59d18c0", id); // FIXME
assert!(id.len() == 16);
Ok(())
}

// uses values from cashu test_mint.py
#[test]
fn test_derive_keys_cashu_py() -> anyhow::Result<()> {
let keys = super::derive_keys("TEST_PRIVATE_KEY", "0/0/0/0");
assert!(keys.len() == 64);

let pub_keys = super::derive_pubkeys(&keys);
let id = super::derive_keyset_id(&pub_keys);
let id = super::legacy_derive_keyset_id(&pub_keys);
assert_eq!("1cCNIAZ2X/w1", id);
assert!(id.len() == 12);
Ok(())
Expand All @@ -216,7 +248,7 @@ mod tests {
),
);

let keyset_id = super::derive_keyset_id(&pubs);
let keyset_id = super::legacy_derive_keyset_id(&pubs);

assert!(keyset_id.len() == 12);
assert_eq!(keyset_id, "cNbjM0O6V/Kl");
Expand Down
27 changes: 27 additions & 0 deletions moksha-core/src/primitives.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//! This module contains all the request and response objects that are used for interacting between the Mint and Wallet in Cashu.
//! All of these structs are serializable and deserializable using serde.
use std::{collections::HashMap, fmt::Display};

use secp256k1::PublicKey;
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
Expand Down Expand Up @@ -93,6 +95,31 @@ pub struct Parameter {
pub peg_out_only: bool,
}

#[derive(serde::Deserialize, Serialize, Debug, PartialEq, Eq, Default)]
pub struct KeysResponse {
pub keysets: Vec<KeyResponse>,
}

#[derive(serde::Deserialize, Serialize, Debug, PartialEq, Eq)]
pub struct KeyResponse {
pub id: String,
pub unit: CurrencyUnit,
pub keys: HashMap<u64, PublicKey>,
}

#[derive(serde::Deserialize, Serialize, Debug, PartialEq, Eq)]
pub enum CurrencyUnit {
Sat,
}

impl Display for CurrencyUnit {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CurrencyUnit::Sat => write!(f, "sat"),
}
}
}

#[cfg(test)]
mod tests {
use crate::{
Expand Down
2 changes: 1 addition & 1 deletion moksha-mint/src/mint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ impl Mint {
lightning,
lightning_type,
lightning_fee_config,
keyset: MintKeyset::new(secret, derivation_path),
keyset: MintKeyset::legacy_new(secret, derivation_path),
db,
dhke: Dhke::new(),
mint_info,
Expand Down
61 changes: 44 additions & 17 deletions moksha-mint/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ use moksha_core::keyset::Keysets;
use crate::mint::Mint;
use crate::model::{GetMintQuery, PostMintQuery};
use moksha_core::primitives::{
CheckFeesRequest, CheckFeesResponse, MintInfoResponse, PaymentRequest, PostMeltRequest,
PostMeltResponse, PostMintRequest, PostMintResponse, PostSplitRequest, PostSplitResponse,
CheckFeesRequest, CheckFeesResponse, CurrencyUnit, KeyResponse, KeysResponse, MintInfoResponse,
PaymentRequest, PostMeltRequest, PostMeltResponse, PostMintRequest, PostMintResponse,
PostSplitRequest, PostSplitResponse,
};
use secp256k1::PublicKey;

Expand Down Expand Up @@ -69,17 +70,29 @@ pub async fn run_server(
}

fn app(mint: Mint, serve_wallet_path: Option<PathBuf>, prefix: Option<String>) -> Router {
let legacy_routes = Router::new()
.route("/keys", get(get_legacy_keys))
.route("/keysets", get(get_legacy_keysets))
.route("/mint", get(get_mint).post(post_legacy_mint))
.route("/checkfees", post(post_legacy_check_fees))
.route("/melt", post(post_legacy_melt))
.route("/split", post(post_legacy_split))
.route("/info", get(get_legacy_info));

let routes = Router::new()
.route("/keys", get(get_keys))
.route("/keysets", get(get_keysets))
.route("/mint", get(get_mint).post(post_mint))
.route("/checkfees", post(post_check_fees))
.route("/melt", post(post_melt))
.route("/split", post(post_split))
.route("/info", get(get_info));
.route("/v1/keys", get(get_keys))
.route("/v1/keysets", get(get_legacy_keysets))
.route("/v1/mint", get(get_mint).post(post_legacy_mint))
.route("/v1/checkfees", post(post_legacy_check_fees))
.route("/v1/melt", post(post_legacy_melt))
.route("/v1/split", post(post_legacy_split))
.route("/v1/info", get(get_legacy_info));

let prefix = prefix.unwrap_or_else(|| "".to_owned());

let router = Router::new()
.nest(&prefix.unwrap_or_else(|| "".to_owned()), routes)
.nest(&prefix, legacy_routes)
.nest(&prefix, routes)
.with_state(mint)
.layer(TraceLayer::new_for_http());

Expand Down Expand Up @@ -126,7 +139,7 @@ async fn add_response_headers(
Ok(res)
}

async fn post_split(
async fn post_legacy_split(
State(mint): State<Mint>,
Json(split_request): Json<PostSplitRequest>,
) -> Result<Json<PostSplitResponse>, MokshaMintError> {
Expand All @@ -137,7 +150,7 @@ async fn post_split(
Ok(Json(response))
}

async fn post_melt(
async fn post_legacy_melt(
State(mint): State<Mint>,
Json(melt_request): Json<PostMeltRequest>,
) -> Result<Json<PostMeltResponse>, MokshaMintError> {
Expand All @@ -152,7 +165,7 @@ async fn post_melt(
}))
}

async fn post_check_fees(
async fn post_legacy_check_fees(
State(mint): State<Mint>,
Json(_check_fees): Json<CheckFeesRequest>,
) -> Result<Json<CheckFeesResponse>, MokshaMintError> {
Expand All @@ -167,7 +180,9 @@ async fn post_check_fees(
}))
}

async fn get_info(State(mint): State<Mint>) -> Result<Json<MintInfoResponse>, MokshaMintError> {
async fn get_legacy_info(
State(mint): State<Mint>,
) -> Result<Json<MintInfoResponse>, MokshaMintError> {
let mint_info = MintInfoResponse {
name: mint.mint_info.name,
pubkey: mint.keyset.mint_pubkey,
Expand Down Expand Up @@ -202,7 +217,7 @@ async fn get_mint(
Ok(Json(PaymentRequest { pr, hash }))
}

async fn post_mint(
async fn post_legacy_mint(
State(mint): State<Mint>,
Query(mint_query): Query<PostMintQuery>,
Json(blinded_messages): Json<PostMintRequest>,
Expand All @@ -218,16 +233,28 @@ async fn post_mint(
Ok(Json(PostMintResponse { promises }))
}

async fn get_keys(
async fn get_legacy_keys(
State(mint): State<Mint>,
) -> Result<Json<HashMap<u64, PublicKey>>, MokshaMintError> {
Ok(Json(mint.keyset.public_keys))
}

async fn get_keysets(State(mint): State<Mint>) -> Result<Json<Keysets>, MokshaMintError> {
async fn get_legacy_keysets(State(mint): State<Mint>) -> Result<Json<Keysets>, MokshaMintError> {
Ok(Json(Keysets::new(vec![mint.keyset.keyset_id])))
}

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

async fn get_keys(State(mint): State<Mint>) -> Result<Json<KeysResponse>, MokshaMintError> {
Ok(Json(KeysResponse {
keysets: vec![KeyResponse {
id: mint.keyset.keyset_id.clone(),
unit: CurrencyUnit::Sat,
keys: mint.keyset.public_keys.clone(),
}],
}))
}

#[cfg(test)]
mod tests {
use std::{collections::HashMap, sync::Arc};
Expand Down
2 changes: 1 addition & 1 deletion moksha-wallet/src/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -519,7 +519,7 @@ mod tests {
impl Default for MockKeys {
fn default() -> Self {
Self {
mint_keyset: MintKeyset::new("mysecret".to_string(), "".to_string()),
mint_keyset: MintKeyset::legacy_new("mysecret".to_string(), "".to_string()),
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion moksha-wallet/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ async fn test_pay_invoice_can_not_melt() -> anyhow::Result<()> {

let melt_response = read_fixture_as::<PostMeltResponse>("post_melt_response_not_paid.json")?;
let split_response = read_fixture_as::<PostSplitResponse>("post_split_response_24_40.json")?;
let mint_keyset = MintKeyset::new("mysecret".to_string(), "".to_string());
let mint_keyset = MintKeyset::legacy_new("mysecret".to_string(), "".to_string());
let keysets = Keysets::new(vec![mint_keyset.keyset_id]);

let mock_client = MockClient::with(
Expand Down

0 comments on commit 583bff6

Please sign in to comment.