From b2cab5318842ace3c03de8c903bae9e59ca0f6f6 Mon Sep 17 00:00:00 2001 From: Sosthene Date: Wed, 14 Aug 2024 09:45:19 +0200 Subject: [PATCH] Break down parse_network_msg into parse_new_tx, parse_cipher --- src/api.rs | 166 +++++++++++++++++++++++------------------------------ 1 file changed, 72 insertions(+), 94 deletions(-) diff --git a/src/api.rs b/src/api.rs index 874f146..257ff83 100644 --- a/src/api.rs +++ b/src/api.rs @@ -474,7 +474,7 @@ pub fn reset_device() -> ApiResult<()> { Ok(()) } -fn handle_recover_transaction( +fn handle_transaction( updated: HashMap, tx: &Transaction, sp_wallet: &mut SpWallet, @@ -666,7 +666,7 @@ fn process_transaction( tx_hex: String, blockheight: u32, tweak_data_hex: String, -) -> anyhow::Result { +) -> anyhow::Result> { let tx = deserialize::(&Vec::from_hex(&tx_hex)?)?; let tweak_data = PublicKey::from_str(&tweak_data_hex)?; @@ -676,105 +676,83 @@ fn process_transaction( let updated = wallet.update_wallet_with_transaction(&tx, blockheight, tweak_data)?; if updated.len() > 0 { - let updated_msg = handle_recover_transaction(updated, &tx, wallet, tweak_data)?; - return Ok(updated_msg); + let updated_msg = handle_transaction(updated, &tx, wallet, tweak_data)?; + return Ok(Some(updated_msg)); } - Err(anyhow::Error::msg("No output found")) -} - -fn process_new_tx_error(msg: NewTxMessage) -> anyhow::Result { - // how do we match this error with the cached message? - unimplemented!(); + Ok(None) } #[wasm_bindgen] -pub fn parse_network_msg(raw: String, fee_rate: u32) -> ApiResult { - if let Ok(ank_msg) = serde_json::from_str::(&raw) { - match ank_msg.flag { - AnkFlag::NewTx => { - let tx_message = serde_json::from_str::(&ank_msg.content)?; - if let Some(ref error) = tx_message.error { - // Transaction failed to broadcast - // we can retry later or check the availability of our spent output, depending on the actual error - // we should probably look up the cached message and record the error - log::error!("{}", error); - // let updated = process_new_tx_error(tx_message)?; - let updated = CachedMessage::new(); - return Ok(updated); - } - if tx_message.tweak_data.is_none() { - return Err(ApiError { - message: "Missing tweak_data".to_owned(), - }); - } - let network_msg = - process_transaction(tx_message.transaction, 0, tx_message.tweak_data.unwrap())?; - return Ok(network_msg); - } - AnkFlag::Faucet => { - let faucet_msg = serde_json::from_str::(&ank_msg.content)?; - if let Some(error) = faucet_msg.error { - debug!("Faucet msg returned with an error: {}", error); - } - unimplemented!(); - } - AnkFlag::Cipher => { - // let's try to decrypt with keys we found in transactions but haven't used yet - let mut messages = lock_messages()?; - let cipher = Vec::from_hex(&ank_msg.content.trim_matches('\"'))?; - if let Some(message) = messages.iter_mut().find(|m| match m.status { - CachedMessageStatus::TxWaitingCipher | CachedMessageStatus::Trusted => { - m.try_decrypt_cipher(cipher.clone()).is_ok() - } - _ => return false, - }) { - let plain = message.try_decrypt_cipher(cipher).unwrap(); - debug!("Found message {}", String::from_utf8(plain.clone())?); - if message.status == CachedMessageStatus::TxWaitingCipher { - let cipher_msg: CipherMessage = serde_json::from_slice(&plain)?; - // does the retrieved message match with the commited hash? - let hash = create_commitment(serde_json::to_string(&cipher_msg)?); - if Some(hash) != message.commitment { - return Err(ApiError { - message: "Message doesn't match commitment".to_owned(), - }); - } - message.sender = Some(cipher_msg.sender); - message.ciphertext = None; - if cipher_msg.message.starts_with("PAIRING") { - // we don't follow the classic confirmation pattern - // set the status to sth else - // if we agree, we must send another notification to remote - // the notification output here will be spent as our first session - message.status = CachedMessageStatus::Pairing; - } else if cipher_msg.message.starts_with("LOGIN") { - message.status = CachedMessageStatus::Login; - } else { - message.status = CachedMessageStatus::ReceivedMustConfirm; - } - message.plaintext.push(cipher_msg.message); - } else { - // We're receiving a message for some action already engaged - // Let's update the message by pushing what we just found out - message.plaintext.push(String::from_utf8(plain)?); - } - return Ok(message.clone()); - } else { - // let's keep it in case we receive the transaction later - let mut new_msg = CachedMessage::new(); - new_msg.status = CachedMessageStatus::CipherWaitingTx; - new_msg.ciphertext = Some(ank_msg.content); - messages.push(new_msg.clone()); - return Ok(new_msg); - } - } - _ => unimplemented!(), +pub fn parse_new_tx(new_tx_msg: String, block_height: u32, fee_rate: u32) -> ApiResult> { + let new_tx: NewTxMessage = serde_json::from_str(&new_tx_msg)?; + + if let Some(error) = new_tx.error { + return Err(ApiError { + message: format!("NewTx returned with an error: {}", error), + }); + } + + if new_tx.tweak_data.is_none() { + return Err(ApiError { + message: "Missing tweak_data".to_owned(), + }); + } + + let msg = + process_transaction(new_tx.transaction, block_height, new_tx.tweak_data.unwrap())?; + + Ok(msg) +} + +#[wasm_bindgen] +pub fn parse_cipher(cipher_msg: String, fee_rate: u32) -> ApiResult { + // let's try to decrypt with keys we found in transactions but haven't used yet + let mut messages = lock_messages()?; + let cipher = Vec::from_hex(&cipher_msg.trim_matches('\"'))?; + if let Some(message) = messages.iter_mut().find(|m| match m.status { + CachedMessageStatus::TxWaitingCipher | CachedMessageStatus::Trusted => { + m.try_decrypt_cipher(cipher.clone()).is_ok() } + _ => return false, + }) { + let plain = message.try_decrypt_cipher(cipher).unwrap(); + // debug!("Found message {}", String::from_utf8(plain.clone())?); + if message.status == CachedMessageStatus::TxWaitingCipher { + let cipher_msg: CipherMessage = serde_json::from_slice(&plain)?; + // does the retrieved message match with the commited hash? + let hash = create_commitment(serde_json::to_string(&cipher_msg)?); + if Some(hash) != message.commitment { + return Err(ApiError { + message: "Message doesn't match commitment".to_owned(), + }); + } + message.sender = Some(cipher_msg.sender); + message.ciphertext = None; + if cipher_msg.message.starts_with("PAIRING") { + // we don't follow the classic confirmation pattern + // if we agree, we must send another notification to remote + // the notification output here will be spent as our first session + message.status = CachedMessageStatus::Pairing; + } else if cipher_msg.message.starts_with("LOGIN") { + message.status = CachedMessageStatus::Login; + } else { + message.status = CachedMessageStatus::ReceivedMustConfirm; + } + message.plaintext.push(cipher_msg.message); + } else { + // We're receiving a message for some action already engaged + // Let's update the message by pushing what we just found out + message.plaintext.push(String::from_utf8(plain)?); + } + return Ok(message.clone()); } else { - Err(ApiError { - message: format!("Can't parse message as a valid 4nk message: {}", raw), - }) + // let's keep it in case we receive the transaction later + let mut new_msg = CachedMessage::new(); + new_msg.status = CachedMessageStatus::CipherWaitingTx; + new_msg.ciphertext = Some(cipher_msg); + messages.push(new_msg.clone()); + return Ok(new_msg); } }