From 752bb5019eeffa862a5ffeb02471987ec94b1ded Mon Sep 17 00:00:00 2001 From: Will Pfleger Date: Tue, 23 Jun 2026 21:16:38 -0400 Subject: [PATCH] refactor(desktop): move DM peer crypto off the main thread MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The nip44_encrypt_to_peer / nip44_decrypt_from_peer commands ran the CPU-bound NIP-44 encrypt/decrypt synchronously while holding the keys lock on the main thread. #1222 already moved the equivalent *_self commands off-thread via async + spawn_blocking; these DM commands predate that change and were left on the asymmetric path. Mirror the *_self template so the hottest DM paths (encrypt-on-send, decrypt-on-render) no longer block the main thread. Callers are unchanged — both already await through invokeTauri. Co-authored-by: Will Pfleger Signed-off-by: Will Pfleger --- desktop/src-tauri/src/commands/identity.rs | 26 +++++++++++++++------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/desktop/src-tauri/src/commands/identity.rs b/desktop/src-tauri/src/commands/identity.rs index eb853835e..f4dd796e5 100644 --- a/desktop/src-tauri/src/commands/identity.rs +++ b/desktop/src-tauri/src/commands/identity.rs @@ -274,16 +274,21 @@ pub async fn nip44_decrypt_from_self( /// Rust backend — the frontend only sends plaintext + peer pubkey and gets the /// ciphertext back to embed in the kind:9 it signs. Used for DM encrypt-on-send. #[tauri::command] -pub fn nip44_encrypt_to_peer( +pub async fn nip44_encrypt_to_peer( peer_pubkey: String, plaintext: String, state: State<'_, AppState>, ) -> Result { let peer = PublicKey::from_hex(peer_pubkey.trim()).map_err(|e| format!("invalid peer pubkey: {e}"))?; - let keys = state.keys.lock().map_err(|e| e.to_string())?; - nip44::encrypt(keys.secret_key(), &peer, &plaintext, nip44::Version::V2) - .map_err(|e| format!("nip44 encrypt failed: {e}")) + let keys = state.keys.lock().map_err(|e| e.to_string())?.clone(); + + tauri::async_runtime::spawn_blocking(move || { + nip44::encrypt(keys.secret_key(), &peer, &plaintext, nip44::Version::V2) + .map_err(|e| format!("nip44 encrypt failed: {e}")) + }) + .await + .map_err(|e| format!("spawn_blocking failed: {e}"))? } /// NIP-44 v2 decrypt DM `ciphertext` from a peer. The peer pubkey is the other @@ -292,14 +297,19 @@ pub fn nip44_encrypt_to_peer( /// key decrypts both). Used for DM decrypt-on-render; on failure the frontend /// shows the mixed-version placeholder rather than blank or garbled content. #[tauri::command] -pub fn nip44_decrypt_from_peer( +pub async fn nip44_decrypt_from_peer( peer_pubkey: String, ciphertext: String, state: State<'_, AppState>, ) -> Result { let peer = PublicKey::from_hex(peer_pubkey.trim()).map_err(|e| format!("invalid peer pubkey: {e}"))?; - let keys = state.keys.lock().map_err(|e| e.to_string())?; - nip44::decrypt(keys.secret_key(), &peer, &ciphertext) - .map_err(|e| format!("nip44 decrypt failed: {e}")) + let keys = state.keys.lock().map_err(|e| e.to_string())?.clone(); + + tauri::async_runtime::spawn_blocking(move || { + nip44::decrypt(keys.secret_key(), &peer, &ciphertext) + .map_err(|e| format!("nip44 decrypt failed: {e}")) + }) + .await + .map_err(|e| format!("spawn_blocking failed: {e}"))? }