diff --git a/moksha-mint/src/mint.rs b/moksha-mint/src/mint.rs index 10a0729c..6f102c29 100644 --- a/moksha-mint/src/mint.rs +++ b/moksha-mint/src/mint.rs @@ -89,18 +89,15 @@ impl Mint { pub fn create_blinded_signatures( &self, blinded_messages: &[BlindedMessage], + keyset: &MintKeyset, // FIXME refactor keyset management ) -> Result, MokshaMintError> { let promises = blinded_messages .iter() .map(|blinded_msg| { - let private_key = self - .keyset_legacy - .private_keys - .get(&blinded_msg.amount) - .unwrap(); // FIXME unwrap + let private_key = keyset.private_keys.get(&blinded_msg.amount).unwrap(); // FIXME unwrap let blinded_sig = self.dhke.step2_bob(blinded_msg.b_, private_key).unwrap(); // FIXME unwrap BlindedSignature { - id: Some(self.keyset_legacy.keyset_id.clone()), + id: Some(keyset.keyset_id.clone()), amount: blinded_msg.amount, c_: blinded_sig, } @@ -124,6 +121,7 @@ impl Mint { &self, key: String, outputs: &[BlindedMessage], + keyset: &MintKeyset, ) -> Result, MokshaMintError> { let invoice = self.db.get_pending_invoice(key.clone())?; @@ -137,7 +135,7 @@ impl Mint { } self.db.remove_pending_invoice(key)?; - self.create_blinded_signatures(outputs) + self.create_blinded_signatures(outputs, keyset) } fn has_duplicate_pubkeys(outputs: &[BlindedMessage]) -> bool { @@ -149,7 +147,8 @@ impl Mint { &self, proofs: &Proofs, blinded_messages: &[BlindedMessage], - ) -> Result { + keyset: &MintKeyset, + ) -> Result, MokshaMintError> { self.check_used_proofs(proofs)?; if Self::has_duplicate_pubkeys(blinded_messages) { @@ -158,7 +157,7 @@ impl Mint { let sum_proofs = proofs.total_amount(); - let promises = self.create_blinded_signatures(blinded_messages)?; + let promises = self.create_blinded_signatures(blinded_messages, keyset)?; let amount_promises = promises.total_amount(); if sum_proofs != amount_promises { return Err(MokshaMintError::SwapAmountMismatch(format!( @@ -167,7 +166,7 @@ impl Mint { } self.db.add_used_proofs(proofs)?; - Ok(PostSwapResponse::with_promises(promises)) + Ok(promises) } pub async fn melt( @@ -175,6 +174,7 @@ impl Mint { payment_request: String, proofs: &Proofs, blinded_messages: &[BlindedMessage], + keyset: &MintKeyset, ) -> Result<(bool, String, Vec), MokshaMintError> { let invoice = self .lightning @@ -206,7 +206,7 @@ impl Mint { let _remaining_amount = (amount_msat - (proofs_amount / 1000)) * 1000; // FIXME check if output amount matches remaining_amount - let output = self.create_blinded_signatures(blinded_messages)?; + let output = self.create_blinded_signatures(blinded_messages, keyset)?; Ok((true, result.payment_hash, output)) } @@ -321,7 +321,7 @@ mod tests { use crate::{database::MockDatabase, error::MokshaMintError}; use moksha_core::blind::{BlindedMessage, TotalAmount}; use moksha_core::dhke; - use moksha_core::primitives::PostSwapRequest; + use moksha_core::primitives::PostSplitRequest; use moksha_core::proof::Proofs; use moksha_core::token::TokenV3; use std::str::FromStr; @@ -346,7 +346,7 @@ mod tests { ), }]; - let result = mint.create_blinded_signatures(&blinded_messages)?; + let result = mint.create_blinded_signatures(&blinded_messages, &mint.keyset_legacy)?; assert_eq!(1, result.len()); assert_eq!(8, result[0].amount); @@ -366,7 +366,9 @@ mod tests { let mint = create_mint_from_mocks(Some(create_mock_mint()), Some(lightning)); let outputs = vec![]; - let result = mint.mint_tokens("somehash".to_string(), &outputs).await?; + let result = mint + .mint_tokens("somehash".to_string(), &outputs, &mint.keyset_legacy) + .await?; assert!(result.is_empty()); Ok(()) } @@ -378,7 +380,9 @@ mod tests { let mint = create_mint_from_mocks(Some(create_mock_mint()), Some(lightning)); let outputs = create_blinded_msgs_from_fixture("blinded_messages_40.json".to_string())?; - let result = mint.mint_tokens("somehash".to_string(), &outputs).await?; + let result = mint + .mint_tokens("somehash".to_string(), &outputs, &mint.keyset_legacy) + .await?; assert_eq!(40, result.total_amount()); Ok(()) } @@ -389,9 +393,11 @@ mod tests { let mint = create_mint_from_mocks(Some(create_mock_db_get_used_proofs()), None); let proofs = Proofs::empty(); - let result = mint.swap(&proofs, &blinded_messages).await?; + let result = mint + .swap(&proofs, &blinded_messages, &mint.keyset_legacy) + .await?; - assert!(result.promises.is_empty()); + assert!(result.is_empty()); Ok(()) } @@ -400,11 +406,13 @@ mod tests { let mint = create_mint_from_mocks(Some(create_mock_db_get_used_proofs()), None); let request = create_request_from_fixture("post_split_request_64_20.json".to_string())?; - let result = mint.swap(&request.proofs, &request.outputs).await?; - assert_eq!(result.promises.total_amount(), 64); + let result = mint + .swap(&request.proofs, &request.outputs, &mint.keyset_legacy) + .await?; + assert_eq!(result.total_amount(), 64); - let prv_lst = result.promises.get(result.promises.len() - 2).unwrap(); - let lst = result.promises.last().unwrap(); + let prv_lst = result.get(result.len() - 2).unwrap(); + let lst = result.last().unwrap(); assert_eq!(prv_lst.amount, 4); assert_eq!(lst.amount, 16); @@ -417,7 +425,9 @@ mod tests { let request = create_request_from_fixture("post_split_request_duplicate_key.json".to_string())?; - let result = mint.swap(&request.proofs, &request.outputs).await; + let result = mint + .swap(&request.proofs, &request.outputs, &mint.keyset_legacy) + .await; assert!(result.is_err()); Ok(()) } @@ -456,7 +466,9 @@ mod tests { let invoice = "some invoice".to_string(); let change = create_blinded_msgs_from_fixture("blinded_messages_40.json".to_string())?; - let (paid, _payment_hash, change) = mint.melt(invoice, &tokens.proofs(), &change).await?; + let (paid, _payment_hash, change) = mint + .melt(invoice, &tokens.proofs(), &change, &mint.keyset_legacy) + .await?; assert!(paid); assert!(change.total_amount() == 40); @@ -470,10 +482,10 @@ mod tests { Ok(raw_token.trim().to_string().try_into()?) } - fn create_request_from_fixture(fixture: String) -> Result { + fn create_request_from_fixture(fixture: String) -> Result { let base_dir = std::env::var("CARGO_MANIFEST_DIR")?; let raw_token = std::fs::read_to_string(format!("{base_dir}/src/fixtures/{fixture}"))?; - Ok(serde_json::from_str::(&raw_token)?) + Ok(serde_json::from_str::(&raw_token)?) } fn create_blinded_msgs_from_fixture( diff --git a/moksha-mint/src/server.rs b/moksha-mint/src/server.rs index 51f9a032..aaeea349 100644 --- a/moksha-mint/src/server.rs +++ b/moksha-mint/src/server.rs @@ -20,7 +20,8 @@ use moksha_core::primitives::{ PaymentRequest, PostMeltBolt11Request, PostMeltBolt11Response, PostMeltQuoteBolt11Request, PostMeltQuoteBolt11Response, PostMeltRequest, PostMeltResponse, PostMintBolt11Request, PostMintBolt11Response, PostMintQuoteBolt11Request, PostMintQuoteBolt11Response, - PostMintRequest, PostMintResponse, PostSwapRequest, PostSwapResponse, + PostMintRequest, PostMintResponse, PostSplitRequest, PostSplitResponse, PostSwapRequest, + PostSwapResponse, }; use secp256k1::PublicKey; @@ -79,11 +80,12 @@ fn app(mint: Mint, serve_wallet_path: Option, prefix: Option) - .route("/mint", get(get_legacy_mint).post(post_legacy_mint)) .route("/checkfees", post(post_legacy_check_fees)) .route("/melt", post(post_legacy_melt)) - .route("/split", post(post_swap)) + .route("/split", post(post_legacy_split)) .route("/info", get(get_legacy_info)); let routes = Router::new() .route("/v1/keys", get(get_keys)) + .route("/v1/keys/:id", get(get_keys_by_id)) .route("/v1/keysets", get(get_keysets)) .route("/v1/mint/quote/bolt11", post(post_mint_quote_bolt11)) .route("/v1/mint/quote/bolt11/:quote", get(get_mint_quote_bolt11)) @@ -145,15 +147,19 @@ async fn add_response_headers( Ok(res) } -async fn post_swap( +async fn post_legacy_split( State(mint): State, - Json(swap_request): Json, -) -> Result, MokshaMintError> { + Json(swap_request): Json, +) -> Result, MokshaMintError> { let response = mint - .swap(&swap_request.proofs, &swap_request.outputs) + .swap( + &swap_request.proofs, + &swap_request.outputs, + &mint.keyset_legacy, + ) .await?; - Ok(Json(response)) + Ok(Json(PostSplitResponse::with_promises(response))) } async fn post_legacy_melt( @@ -161,7 +167,12 @@ async fn post_legacy_melt( Json(melt_request): Json, ) -> Result, MokshaMintError> { let (paid, preimage, change) = mint - .melt(melt_request.pr, &melt_request.proofs, &melt_request.outputs) + .melt( + melt_request.pr, + &melt_request.proofs, + &melt_request.outputs, + &mint.keyset_legacy, + ) .await?; Ok(Json(PostMeltResponse { @@ -237,7 +248,11 @@ async fn post_legacy_mint( ); let promises = mint - .mint_tokens(mint_query.hash, &blinded_messages.outputs) + .mint_tokens( + mint_query.hash, + &blinded_messages.outputs, + &mint.keyset_legacy, + ) .await?; Ok(Json(PostMintResponse { promises })) } @@ -254,6 +269,19 @@ async fn get_legacy_keysets(State(mint): State) -> Result, M // ###################################################################################################### +async fn post_swap( + State(mint): State, + Json(swap_request): Json, +) -> Result, MokshaMintError> { + let response = mint + .swap(&swap_request.inputs, &swap_request.outputs, &mint.keyset) + .await?; + + Ok(Json(PostSwapResponse { + signatures: response, + })) +} + async fn get_keys(State(mint): State) -> Result, MokshaMintError> { Ok(Json(KeysResponse { keysets: vec![KeyResponse { @@ -264,6 +292,19 @@ async fn get_keys(State(mint): State) -> Result, Moksha })) } +async fn get_keys_by_id( + Path(_id): Path, + State(mint): State, +) -> Result, MokshaMintError> { + Ok(Json(KeysResponse { + keysets: vec![KeyResponse { + id: mint.keyset.keyset_id.clone(), + unit: CurrencyUnit::Sat, + keys: mint.keyset.public_keys.clone(), + }], + })) +} + async fn get_keysets(State(mint): State) -> Result, MokshaMintError> { Ok(Json(V1Keysets::new( mint.keyset.keyset_id, @@ -309,7 +350,9 @@ async fn post_mint_bolt11( match quote { Quote::Bolt11Mint { .. } => { - let signatures = mint.mint_tokens(request.quote, &request.outputs).await?; + let signatures = mint + .mint_tokens(request.quote, &request.outputs, &mint.keyset) + .await?; Ok(Json(PostMintBolt11Response { signatures })) } _ => Err(crate::error::MokshaMintError::InvalidQuote( @@ -329,7 +372,8 @@ async fn post_melt_quote_bolt11( let amount = invoice .amount_milli_satoshis() .ok_or_else(|| crate::error::MokshaMintError::InvalidAmount)?; - let fee_reserve = mint.fee_reserve(amount); + let fee_reserve = mint.fee_reserve(amount) / 1_000; // FIXME check if this is correct + info!("fee_reserve: {}", fee_reserve); let amount_sat = amount / 1_000; // Store quote in db @@ -361,7 +405,7 @@ async fn post_melt_bolt11( match quote { Quote::Bolt11Melt { .. } => { let (paid, preimage, change) = mint - .melt(melt_request.quote, &melt_request.inputs, &[]) + .melt(melt_request.quote, &melt_request.inputs, &[], &mint.keyset) .await?; Ok(Json(PostMeltBolt11Response { diff --git a/moksha-wallet/src/client/mod.rs b/moksha-wallet/src/client/mod.rs index 0b1ffd18..be114e4b 100644 --- a/moksha-wallet/src/client/mod.rs +++ b/moksha-wallet/src/client/mod.rs @@ -5,7 +5,7 @@ use moksha_core::{ blind::BlindedMessage, keyset::Keysets, primitives::{ - CheckFeesResponse, PaymentRequest, PostMeltResponse, PostMintResponse, PostSwapResponse, + CheckFeesResponse, PaymentRequest, PostMeltResponse, PostMintResponse, PostSplitResponse, }, proof::Proofs, }; @@ -24,7 +24,7 @@ pub trait Client { mint_url: &Url, proofs: Proofs, output: Vec, - ) -> Result; + ) -> Result; async fn post_mint_payment_request( &self, diff --git a/moksha-wallet/src/client/reqwest.rs b/moksha-wallet/src/client/reqwest.rs index 73f40a73..be9ecbdf 100644 --- a/moksha-wallet/src/client/reqwest.rs +++ b/moksha-wallet/src/client/reqwest.rs @@ -6,7 +6,8 @@ use moksha_core::{ keyset::Keysets, primitives::{ CashuErrorResponse, CheckFeesRequest, CheckFeesResponse, PaymentRequest, PostMeltRequest, - PostMeltResponse, PostMintRequest, PostMintResponse, PostSwapRequest, PostSwapResponse, + PostMeltResponse, PostMintRequest, PostMintResponse, PostSplitRequest, PostSplitResponse, + PostSwapResponse, }, proof::Proofs, }; @@ -44,8 +45,8 @@ impl Client for HttpClient { mint_url: &Url, proofs: Proofs, outputs: Vec, - ) -> Result { - let body = serde_json::to_string(&PostSwapRequest { proofs, outputs })?; + ) -> Result { + let body = serde_json::to_string(&PostSplitRequest { proofs, outputs })?; let resp = self .request_client @@ -55,7 +56,7 @@ impl Client for HttpClient { .send() .await?; - extract_response_data::(resp).await + extract_response_data::(resp).await } async fn post_melt_tokens( diff --git a/moksha-wallet/src/wallet.rs b/moksha-wallet/src/wallet.rs index f3ac78e3..19ef6732 100644 --- a/moksha-wallet/src/wallet.rs +++ b/moksha-wallet/src/wallet.rs @@ -451,7 +451,8 @@ mod tests { use moksha_core::fixture::{read_fixture, read_fixture_as}; use moksha_core::keyset::{Keysets, MintKeyset}; use moksha_core::primitives::{ - CheckFeesResponse, PaymentRequest, PostMeltResponse, PostMintResponse, PostSwapResponse, + CheckFeesResponse, PaymentRequest, PostMeltResponse, PostMintResponse, PostSplitResponse, + PostSwapResponse, }; use moksha_core::proof::Proofs; use moksha_core::token::{Token, TokenV3}; @@ -526,14 +527,14 @@ mod tests { #[derive(Clone, Default)] struct MockClient { - split_response: PostSwapResponse, + split_response: PostSplitResponse, post_mint_response: PostMintResponse, post_melt_response: PostMeltResponse, keyset: MockKeys, } impl MockClient { - fn with_split_response(split_response: PostSwapResponse) -> Self { + fn with_split_response(split_response: PostSplitResponse) -> Self { Self { split_response, ..Default::default() @@ -550,7 +551,7 @@ mod tests { fn with_melt_response(post_melt_response: PostMeltResponse) -> Self { Self { post_melt_response, - split_response: PostSwapResponse::with_promises(vec![]), + split_response: PostSplitResponse::with_promises(vec![]), ..Default::default() } } @@ -563,7 +564,7 @@ mod tests { _mint_url: &Url, _proofs: Proofs, _output: Vec, - ) -> Result { + ) -> Result { Ok(self.split_response.clone()) } @@ -642,7 +643,8 @@ mod tests { #[tokio::test] async fn test_split() -> anyhow::Result<()> { - let split_response = read_fixture_as::("post_split_response_24_40.json")?; + let split_response = + read_fixture_as::("post_split_response_24_40.json")?; let client = MockClient::with_split_response(split_response); let localstore = MockLocalStore::default(); diff --git a/moksha-wallet/tests/tests.rs b/moksha-wallet/tests/tests.rs index d3676213..ea00b4ad 100644 --- a/moksha-wallet/tests/tests.rs +++ b/moksha-wallet/tests/tests.rs @@ -5,7 +5,7 @@ use moksha_core::blind::BlindedMessage; use moksha_core::fixture::{read_fixture, read_fixture_as}; use moksha_core::keyset::{Keysets, MintKeyset}; use moksha_core::primitives::{ - CheckFeesResponse, PaymentRequest, PostMeltResponse, PostMintResponse, PostSwapResponse, + CheckFeesResponse, PaymentRequest, PostMeltResponse, PostMintResponse, PostSplitResponse, }; use moksha_core::proof::Proofs; use moksha_core::token::TokenV3; @@ -18,7 +18,7 @@ use secp256k1::PublicKey; #[derive(Clone, Default)] struct MockClient { - split_response: PostSwapResponse, + split_response: PostSplitResponse, post_mint_response: PostMintResponse, post_melt_response: PostMeltResponse, mint_keys: HashMap, @@ -28,7 +28,7 @@ struct MockClient { impl MockClient { fn with( post_melt_response: PostMeltResponse, - post_split_response: PostSwapResponse, + post_split_response: PostSplitResponse, mint_keys: HashMap, keysets: Keysets, ) -> Self { @@ -49,7 +49,7 @@ impl Client for MockClient { _mint_url: &Url, _proofs: Proofs, _output: Vec, - ) -> Result { + ) -> Result { Ok(self.split_response.clone()) } @@ -121,7 +121,7 @@ async fn test_pay_invoice_can_not_melt() -> anyhow::Result<()> { assert_eq!(64, localstore.get_proofs().await?.total_amount()); let melt_response = read_fixture_as::("post_melt_response_not_paid.json")?; - let split_response = read_fixture_as::("post_split_response_24_40.json")?; + let split_response = read_fixture_as::("post_split_response_24_40.json")?; let mint_keyset = MintKeyset::legacy_new("mysecret", ""); let keysets = Keysets::new(vec![mint_keyset.keyset_id]);