From 19f46d6edb8c93accd34b4c40666d42aa6cf2905 Mon Sep 17 00:00:00 2001 From: Sameh Abouelsaad Date: Fri, 16 May 2025 03:42:48 +0300 Subject: [PATCH] fix: fix tests and add Makefile --- Makefile | 23 +++++++++ README.md | 76 +++++++++++++++++++++++++---- evm_client/Cargo.toml | 2 + evm_client/tests/balance.rs | 44 ++++++++++++++++- evm_client/tests/wasm.rs | 33 +++++++++++++ vault/tests/wasm_session_manager.rs | 3 +- 6 files changed, 168 insertions(+), 13 deletions(-) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..342ee66 --- /dev/null +++ b/Makefile @@ -0,0 +1,23 @@ +# Makefile to run all browser (WASM) tests for kvstore, vault, and evm_client + +BROWSER ?= firefox + +.PHONY: test-browser-all test-browser-kvstore test-browser-vault test-browser-evm-client + +test-browser-all: test-browser-kvstore test-browser-vault test-browser-evm-client + +# Run browser tests for kvstore + +test-browser-kvstore: + cd kvstore && wasm-pack test --headless --$(BROWSER) + +# Run browser tests for vault + +test-browser-vault: + cd vault && wasm-pack test --headless --$(BROWSER) + +# Run browser tests for evm_client + +test-browser-evm-client: + cd evm_client && wasm-pack test --headless --$(BROWSER) + diff --git a/README.md b/README.md index 740162a..b39a980 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,80 @@ # Modular Rust System: Key-Value Store, Vault, and EVM Client -This repository implements a modular, async, and cross-platform cryptographic stack in Rust. It is designed for use in both native (desktop/server) and WASM (browser) environments, supporting secure storage, cryptographic operations, and EVM (Ethereum) client functionality. +A modular, async, and cross-platform cryptographic stack in Rust. Built for both native (desktop/server) and WASM (browser) environments, this system provides secure storage, cryptographic operations, and Ethereum (EVM) client functionality—all with a focus on extensibility, testability, and scripting. ## Crate Overview -- **kvstore/**: Async key-value store trait and implementations (native: `sled`, WASM: IndexedDB). -- **vault/**: Cryptographic vault for managing encrypted keyspaces and key operations. Uses `kvstore` for persistence. -- **evm_client/**: EVM RPC client, integrates with `vault` for signing and secure key management. -- **cli_app/**: (Planned) Command-line interface for scripting and automation. -- **web_app/**: (Planned) WASM web app exposing the same APIs to JavaScript or browser scripting. +- **kvstore/**: Async key-value store trait and implementations (native: `sled`, WASM: IndexedDB) +- **vault/**: Cryptographic vault for encrypted keyspaces, key management, and signing; uses `kvstore` for persistence +- **evm_client/**: Async EVM RPC client, integrates with `vault` for secure signing; supports trait-based signers and modular providers +- **cli_app/** _(planned)_: Command-line interface for scripting, automation, and Rhai scripting +- **web_app/** _(planned)_: WASM web app exposing APIs to JavaScript/browser scripting +- **wasm/** _(planned)_: WebAssembly module for browser/extension integration +- **browser_extension/** _(planned)_: Browser extension for secure scripting and automation +- **rhai scripting** _(planned)_: Unified scripting API for both CLI and browser (see [`docs/rhai_architecture_plan.md`](docs/rhai_architecture_plan.md)) + +## Directory Structure +> **Note:** Some directories are planned for future extensibility and scripting, and may not exist yet in the current workspace. + +``` +. +├── kvstore/ # Key-value store trait and backends +├── vault/ # Cryptographic vault (shared core) +├── evm_client/ # EVM RPC client (shared core) +├── cli_app/ # Command-line tool for Rhai scripts (planned) +├── web_app/ # WASM web app exposing APIs (planned) +├── wasm/ # WebAssembly module for browser/extension (planned) +├── browser_extension/ # Extension source (planned) +├── docs/ # Architecture & usage docs +└── README.md +``` ## Architecture Highlights -- **Async everywhere:** All APIs are async and runtime-agnostic. -- **Conditional backends:** Uses Cargo features and `cfg` to select the appropriate backend for each environment. -- **Secure by design:** Vault encrypts all key material at rest and leverages modern cryptography. -- **Tested natively and in browser:** WASM and native backends are both covered by tests. +- **Modular and async:** All APIs are async and runtime-agnostic (works with both native and WASM targets) +- **Conditional backends:** Uses Cargo features and `cfg` for platform-specific storage/networking +- **Secure by design:** Vault encrypts all key material at rest using modern cryptography +- **Extensible:** Trait-based APIs for signers and providers, ready for scripting and new integrations +- **Tested everywhere:** Native and browser (WASM) backends are covered by automated tests and a unified Makefile ## Building and Testing ### Prerequisites +- Rust (latest stable recommended) +- wasm-pack (for browser tests) +- Firefox or Chrome (for browser testing) + +### Native Build & Test +```sh +cargo build +cargo test +``` + +### Browser (WASM) Tests +Run all browser tests for all modules: +```sh +make test-browser-all +``` +Or run for a specific module: +```sh +make test-browser-kvstore +make test-browser-vault +make test-browser-evm-client +``` +Set `BROWSER=chrome` to use Chrome instead of Firefox. + +## Scripting & Extensibility +- **Rhai scripting**: The architecture is ready for ergonomic scripting via Rhai, both in CLI and browser (see [`docs/rhai_architecture_plan.md`](docs/rhai_architecture_plan.md)) +- **Browser extension**: Planned support for browser extension scripting and secure key usage + +## Documentation +- [Architecture Overview](docs/architecture.md) +- [EVM Client Plan](docs/evm_client_architecture_plan.md) +- [Rhai Scripting Plan](docs/rhai_architecture_plan.md) + +--- + +For questions, contributions, or more details, see the architecture docs or open an issue! + - Rust (latest stable recommended) - For WASM: `wasm-pack`, Firefox or Chrome (for browser tests) diff --git a/evm_client/Cargo.toml b/evm_client/Cargo.toml index 4b75de5..98171d1 100644 --- a/evm_client/Cargo.toml +++ b/evm_client/Cargo.toml @@ -25,6 +25,8 @@ k256 = { version = "0.13", features = ["ecdsa"] } [dev-dependencies] wasm-bindgen-test = "0.3" web-sys = { version = "0.3", features = ["console"] } +tempfile = "3" +kvstore = { path = "../kvstore" } [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] tokio = { version = "1", features = ["macros", "rt-multi-thread"] } diff --git a/evm_client/tests/balance.rs b/evm_client/tests/balance.rs index b14d0bd..cff5ce2 100644 --- a/evm_client/tests/balance.rs +++ b/evm_client/tests/balance.rs @@ -1,7 +1,47 @@ use evm_client::provider::Transaction; use evm_client::provider::{parse_signature_rs_v, get_balance}; -// All native (non-WASM) balance and signature tests are in this file. -use ethers_core::types::{U256, Address, Bytes}; +use ethers_core::types::{Address, U256}; + +#[cfg(not(target_arch = "wasm32"))] +mod native_tests { + use super::*; + use vault::{SessionManager, KeyType}; + use tempfile::TempDir; + use kvstore::native::NativeStore; + use alloy_primitives::keccak256; + + #[tokio::test] + async fn test_vault_sessionmanager_balance_for_new_keypair() { + let tmp_dir = TempDir::new().expect("create temp dir"); + let store = NativeStore::open(tmp_dir.path().to_str().unwrap()).expect("Failed to open native store"); + let mut vault = vault::Vault::new(store.clone()); + let keyspace = "testspace"; + let password = b"testpass"; + // 1. Create keyspace + vault.create_keyspace(keyspace, password, None).await.expect("create keyspace"); + // 2. Add secp256k1 keypair + let key_id = vault.add_keypair(keyspace, password, Some(KeyType::Secp256k1), None).await.expect("add keypair"); + // 3. Create SessionManager and unlock keyspace + let mut session = SessionManager::new(vault); + session.unlock_keyspace(keyspace, password).await.expect("unlock keyspace"); + session.select_keyspace(keyspace).expect("select keyspace"); + session.select_keypair(&key_id).expect("select keypair"); + let kp = session.current_keypair().expect("current keypair"); + // 4. Derive Ethereum address from public key (same as in evm_client) + let pubkey = &kp.public_key; + // Remove leading 0x04 if present (uncompressed SEC1) + let pubkey = if pubkey.len() == 65 && pubkey[0] == 0x04 { &pubkey[1..] } else { pubkey.as_slice() }; + let hash = keccak256(pubkey); + let address = Address::from_slice(&hash[12..]); + // 5. Query balance + let url = "https://ethereum.blockpi.network/v1/rpc/public"; + let balance = get_balance(url, address).await.expect("Failed to get balance"); + assert_eq!(balance, ethers_core::types::U256::zero(), "New keypair should have zero balance"); + } +} + + +use ethers_core::types::Bytes; #[test] fn test_rlp_encode_unsigned() { diff --git a/evm_client/tests/wasm.rs b/evm_client/tests/wasm.rs index 7f89c18..5434e27 100644 --- a/evm_client/tests/wasm.rs +++ b/evm_client/tests/wasm.rs @@ -34,6 +34,39 @@ pub async fn test_get_balance_real_address_wasm_unique() { assert!(balance > U256::zero(), "Vitalik's balance should be greater than zero"); } +#[wasm_bindgen_test(async)] +pub async fn test_vault_sessionmanager_balance_for_new_keypair_wasm() { + use vault::{SessionManager, KeyType}; + use ethers_core::types::Address; + use evm_client::provider::get_balance; + use alloy_primitives::keccak256; + web_sys::console::log_1(&"WASM vault-session balance test running!".into()); + let store = kvstore::wasm::WasmStore::open("test-db").await.expect("open"); + let mut vault = vault::Vault::new(store); + let keyspace = "testspace-wasm"; + let password = b"testpass"; + // 1. Create keyspace + vault.create_keyspace(keyspace, password, None).await.expect("create keyspace"); + // 2. Add secp256k1 keypair + let key_id = vault.add_keypair(keyspace, password, Some(KeyType::Secp256k1), None).await.expect("add keypair"); + // 3. Create SessionManager and unlock keyspace + let mut session = SessionManager::new(vault); + session.unlock_keyspace(keyspace, password).await.expect("unlock keyspace"); + session.select_keyspace(keyspace).expect("select keyspace"); + session.select_keypair(&key_id).expect("select keypair"); + let kp = session.current_keypair().expect("current keypair"); + // 4. Derive Ethereum address from public key + let pubkey = &kp.public_key; + let pubkey = if pubkey.len() == 65 && pubkey[0] == 0x04 { &pubkey[1..] } else { pubkey.as_slice() }; + let hash = keccak256(pubkey); + let address = Address::from_slice(&hash[12..]); + // 5. Query balance + let url = "https://ethereum.blockpi.network/v1/rpc/public"; + let balance = get_balance(url, address).await.expect("Failed to get balance"); + web_sys::console::log_1(&format!("Balance: {balance:?}").into()); + assert_eq!(balance, ethers_core::types::U256::zero(), "New keypair should have zero balance"); +} + #[wasm_bindgen_test] fn test_parse_signature_rs_v() { let mut sig = [0u8; 65]; diff --git a/vault/tests/wasm_session_manager.rs b/vault/tests/wasm_session_manager.rs index 6f070dc..12b5642 100644 --- a/vault/tests/wasm_session_manager.rs +++ b/vault/tests/wasm_session_manager.rs @@ -1,6 +1,7 @@ //! WASM integration test for SessionManager using kvstore::WasmStore -use vault::Vault; + +use vault::{Vault, KeyType, KeyMetadata, SessionManager}; #[cfg(target_arch = "wasm32")] use kvstore::WasmStore; use wasm_bindgen_test::*;