From 27e5a6df3e907224e2a80d65ab0559bf77fff0fd Mon Sep 17 00:00:00 2001 From: Sameh Abouelsaad Date: Sun, 11 May 2025 22:37:02 +0300 Subject: [PATCH] feat: Improve error handling and simplify async operations --- src/hero_vault/ethereum/transaction.rs | 4 +- src/rhai/hero_vault.rs | 228 ++++++++----------------- 2 files changed, 76 insertions(+), 156 deletions(-) diff --git a/src/hero_vault/ethereum/transaction.rs b/src/hero_vault/ethereum/transaction.rs index 2dc7477..b7b355e 100644 --- a/src/hero_vault/ethereum/transaction.rs +++ b/src/hero_vault/ethereum/transaction.rs @@ -73,7 +73,7 @@ pub async fn send_eth( let tx = TransactionRequest::new() .to(to) .value(amount) - .gas(21000); + .gas(gas); // Send the transaction let pending_tx = client.send_transaction(tx, None) @@ -115,7 +115,7 @@ pub async fn send_eth_with_provider( let tx = TransactionRequest::new() .to(to) .value(amount) - .gas(21000); + .gas(gas); // Send the transaction let pending_tx = client.send_transaction(tx, None) diff --git a/src/rhai/hero_vault.rs b/src/rhai/hero_vault.rs index 836d0ff..46754d3 100644 --- a/src/rhai/hero_vault.rs +++ b/src/rhai/hero_vault.rs @@ -3,8 +3,6 @@ use rhai::{Engine, Dynamic, EvalAltResult}; use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64}; use std::fs; -use std::path::PathBuf; -use std::collections::HashMap; use std::sync::Mutex; use once_cell::sync::Lazy; use tokio::runtime::Runtime; @@ -12,7 +10,7 @@ use ethers::types::{Address, U256}; use std::str::FromStr; use crate::hero_vault::{keypair, symmetric, ethereum, kvs}; -use crate::hero_vault::kvs::{KVStore, DefaultStore}; +use crate::hero_vault::kvs::DefaultStore; use crate::hero_vault::ethereum::prepare_function_arguments; // Global Tokio runtime for blocking async operations @@ -20,6 +18,16 @@ static RUNTIME: Lazy> = Lazy::new(|| { Mutex::new(Runtime::new().expect("Failed to create Tokio runtime")) }); +// Helper function to run async operations and handle errors consistently +fn run_async(future: F) -> Result +where + F: std::future::Future>, + E: std::fmt::Display, +{ + let rt = RUNTIME.lock().map_err(|e| format!("Failed to acquire runtime lock: {}", e))?; + rt.block_on(async { future.await.map_err(|e| e.to_string()) }) +} + // Get a platform-specific DefaultStore implementation for Rhai bindings #[cfg(not(target_arch = "wasm32"))] fn get_key_store() -> DefaultStore { @@ -42,16 +50,16 @@ fn get_key_store() -> DefaultStore { fn get_key_store() -> DefaultStore { use once_cell::sync::Lazy; static STORE: Lazy = Lazy::new(|| { - // In WASM we have to run this on the main thread the first time - // This works because the cache in IndexedDbStore handles subsequent synchronous operations - let rt = RUNTIME.lock().unwrap(); - rt.block_on(async { - match kvs::open_default_store("rhai-vault", None).await { - Ok(store) => store, - Err(_) => kvs::create_default_store("rhai-vault", false, None).await - .expect("Failed to create store") + match run_async(async { + kvs::open_default_store("rhai-vault", None).await + .or_else(|_| kvs::create_default_store("rhai-vault", false, None).await) + }) { + Ok(store) => store, + Err(e) => { + log::error!("Failed to create key store: {}", e); + panic!("Could not initialize key store: {}", e); } - }) + } }); STORE.clone() @@ -76,26 +84,13 @@ fn auto_save_key_space(password: &str) -> bool { // Export the current key space to a JSON string fn encrypt_key_space(password: &str) -> String { - match keypair::get_current_space() { - Ok(space) => { - match symmetric::encrypt_key_space(&space, password) { - Ok(encrypted_space) => { - match symmetric::serialize_encrypted_space(&encrypted_space) { - Ok(json) => json, - Err(e) => { - log::error!("Error serializing encrypted space: {}", e); - String::new() - } - } - }, - Err(e) => { - log::error!("Error encrypting key space: {}", e); - String::new() - } - } - }, + match keypair::get_current_space() + .and_then(|space| symmetric::encrypt_key_space(&space, password)) + .and_then(|encrypted_space| symmetric::serialize_encrypted_space(&encrypted_space)) + { + Ok(json) => json, Err(e) => { - log::error!("Error getting current space: {}", e); + log::error!("Error encrypting key space: {}", e); String::new() } } @@ -103,26 +98,13 @@ fn encrypt_key_space(password: &str) -> String { // Import a key space from a JSON string fn decrypt_key_space(encrypted: &str, password: &str) -> bool { - match symmetric::deserialize_encrypted_space(encrypted) { - Ok(encrypted_space) => { - match symmetric::decrypt_key_space(&encrypted_space, password) { - Ok(space) => { - match keypair::set_current_space(space) { - Ok(_) => true, - Err(e) => { - log::error!("Error setting current space: {}", e); - false - } - } - }, - Err(e) => { - log::error!("Error decrypting key space: {}", e); - false - } - } - }, + match symmetric::deserialize_encrypted_space(encrypted) + .and_then(|encrypted_space| symmetric::decrypt_key_space(&encrypted_space, password)) + .and_then(|space| keypair::set_current_space(space)) + { + Ok(_) => true, Err(e) => { - log::error!("Error parsing encrypted space: {}", e); + log::error!("Error decrypting key space: {}", e); false } } @@ -176,18 +158,14 @@ fn sign(message: &str) -> String { fn verify(message: &str, signature: &str) -> bool { let message_bytes = message.as_bytes(); - match BASE64.decode(signature) { - Ok(signature_bytes) => { - match keypair::keypair_verify(message_bytes, &signature_bytes) { - Ok(is_valid) => is_valid, - Err(e) => { - log::error!("Error verifying signature: {}", e); - false - } - } - }, + match BASE64.decode(signature) + .map_err(|e| e.to_string()) + .and_then(|sig_bytes| keypair::keypair_verify(message_bytes, &sig_bytes) + .map_err(|e| e.to_string())) + { + Ok(is_valid) => is_valid, Err(e) => { - log::error!("Error decoding signature: {}", e); + log::error!("Error verifying signature: {}", e); false } } @@ -195,58 +173,44 @@ fn verify(message: &str, signature: &str) -> bool { // Symmetric encryption fn generate_key() -> String { - let key = symmetric::generate_symmetric_key(); - BASE64.encode(key) + BASE64.encode(symmetric::generate_symmetric_key()) } fn encrypt(key: &str, message: &str) -> String { - match BASE64.decode(key) { - Ok(key_bytes) => { - let message_bytes = message.as_bytes(); - match symmetric::encrypt_symmetric(&key_bytes, message_bytes) { - Ok(ciphertext) => BASE64.encode(ciphertext), - Err(e) => { - log::error!("Error encrypting message: {}", e); - String::new() - } - } - }, + match BASE64.decode(key) + .map_err(|e| format!("Error decoding key: {}", e)) + .and_then(|key_bytes| { + symmetric::encrypt_symmetric(&key_bytes, message.as_bytes()) + .map_err(|e| format!("Error encrypting message: {}", e)) + }) + { + Ok(ciphertext) => BASE64.encode(ciphertext), Err(e) => { - log::error!("Error decoding key: {}", e); + log::error!("{}", e); String::new() } } } fn decrypt(key: &str, ciphertext: &str) -> String { - match BASE64.decode(key) { - Ok(key_bytes) => { - match BASE64.decode(ciphertext) { - Ok(ciphertext_bytes) => { - match symmetric::decrypt_symmetric(&key_bytes, &ciphertext_bytes) { - Ok(plaintext) => { - match String::from_utf8(plaintext) { - Ok(text) => text, - Err(e) => { - log::error!("Error converting plaintext to string: {}", e); - String::new() - } - } - }, - Err(e) => { - log::error!("Error decrypting ciphertext: {}", e); - String::new() - } - } - }, - Err(e) => { - log::error!("Error decoding ciphertext: {}", e); - String::new() - } - } - }, + match BASE64.decode(key) + .map_err(|e| format!("Error decoding key: {}", e)) + .and_then(|key_bytes| { + BASE64.decode(ciphertext) + .map_err(|e| format!("Error decoding ciphertext: {}", e)) + .and_then(|cipher_bytes| { + symmetric::decrypt_symmetric(&key_bytes, &cipher_bytes) + .map_err(|e| format!("Error decrypting ciphertext: {}", e)) + }) + }) + .and_then(|plaintext| { + String::from_utf8(plaintext) + .map_err(|e| format!("Error converting plaintext to string: {}", e)) + }) + { + Ok(text) => text, Err(e) => { - log::error!("Error decoding key: {}", e); + log::error!("{}", e); String::new() } } @@ -374,15 +338,6 @@ fn get_network_explorer_url(network_name: &str) -> String { // Get the balance of an address on a specific network fn get_balance(network_name: &str, address: &str) -> String { - // Get the runtime - let rt = match RUNTIME.lock() { - Ok(rt) => rt, - Err(e) => { - log::error!("Failed to acquire runtime lock: {}", e); - return String::new(); - } - }; - // Parse the address let addr = match Address::from_str(address) { Ok(addr) => addr, @@ -393,9 +348,7 @@ fn get_balance(network_name: &str, address: &str) -> String { }; // Execute the balance query in a blocking manner - match rt.block_on(async { - ethereum::get_balance(network_name, addr).await - }) { + match run_async(ethereum::get_balance(network_name, addr)) { Ok(balance) => { // Format the balance with the network's token symbol match ethereum::get_network_by_name(network_name) { @@ -412,15 +365,6 @@ fn get_balance(network_name: &str, address: &str) -> String { // Send ETH from one address to another fn send_eth(network_name: &str, to_address: &str, amount_str: &str) -> String { - // Get the runtime - let rt = match RUNTIME.lock() { - Ok(rt) => rt, - Err(e) => { - log::error!("Failed to acquire runtime lock: {}", e); - return String::new(); - } - }; - // Parse the address let to_addr = match Address::from_str(to_address) { Ok(addr) => addr, @@ -449,9 +393,7 @@ fn send_eth(network_name: &str, to_address: &str, amount_str: &str) -> String { }; // Execute the transaction in a blocking manner - match rt.block_on(async { - ethereum::send_eth(&wallet, network_name, to_addr, amount).await - }) { + match run_async(ethereum::send_eth(&wallet, network_name, to_addr, amount)) { Ok(tx_hash) => format!("{:?}", tx_hash), Err(e) => { log::error!("Transaction failed: {}", e); @@ -538,15 +480,6 @@ fn call_contract_read(contract_json: &str, function_name: &str, args: rhai::Arra } }; - // Get the runtime - let rt = match RUNTIME.lock() { - Ok(rt) => rt, - Err(e) => { - log::error!("Failed to acquire runtime lock: {}", e); - return Dynamic::UNIT; - } - }; - // Create a provider let provider = match ethereum::create_provider(&contract.network.name) { Ok(p) => p, @@ -557,9 +490,7 @@ fn call_contract_read(contract_json: &str, function_name: &str, args: rhai::Arra }; // Execute the call in a blocking manner - match rt.block_on(async { - ethereum::call_read_function(&contract, &provider, function_name, tokens).await - }) { + match run_async(ethereum::call_read_function(&contract, &provider, function_name, tokens)) { Ok(result) => ethereum::token_to_dynamic(&result), Err(e) => { log::error!("Failed to call contract function: {}", e); @@ -593,15 +524,6 @@ fn call_contract_write(contract_json: &str, function_name: &str, args: rhai::Arr } }; - // Get the runtime - let rt = match RUNTIME.lock() { - Ok(rt) => rt, - Err(e) => { - log::error!("Failed to acquire runtime lock: {}", e); - return String::new(); - } - }; - // Get the wallet let wallet = match ethereum::get_current_ethereum_wallet() { Ok(w) => w, @@ -621,9 +543,7 @@ fn call_contract_write(contract_json: &str, function_name: &str, args: rhai::Arr }; // Execute the transaction in a blocking manner - match rt.block_on(async { - ethereum::call_write_function(&contract, &wallet, &provider, function_name, tokens).await - }) { + match run_async(ethereum::call_write_function(&contract, &wallet, &provider, function_name, tokens)) { Ok(tx_hash) => format!("{:?}", tx_hash), Err(e) => { // Log the error details for debugging @@ -657,20 +577,20 @@ fn get_agung_address() -> String { get_ethereum_address() } -fn create_wallet_for_network(network_name: &str) -> bool { +fn create_wallet_for_network(_network_name: &str) -> bool { create_ethereum_wallet() } -fn get_wallet_address_for_network(network_name: &str) -> String { +fn get_wallet_address_for_network(_network_name: &str) -> String { get_ethereum_address() } -fn clear_wallets_for_network(network_name: &str) -> bool { +fn clear_wallets_for_network(_network_name: &str) -> bool { ethereum::clear_ethereum_wallets(); true } -fn create_wallet_from_private_key_for_network(private_key: &str, network_name: &str) -> bool { +fn create_wallet_from_private_key_for_network(private_key: &str, _network_name: &str) -> bool { create_ethereum_wallet_from_private_key(private_key) }