feat: Migrate SAL to Cargo workspace
- Migrate individual modules to independent crates - Refactor dependencies for improved modularity - Update build system and testing infrastructure - Update documentation to reflect new structure
This commit is contained in:
@@ -141,6 +141,8 @@ cargo test crypto_tests
|
||||
cargo test rhai_integration_tests
|
||||
```
|
||||
|
||||
**Note**: The Rhai integration tests use global state and are automatically serialized using a test mutex to prevent interference between parallel test runs.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `chacha20poly1305`: Symmetric encryption
|
||||
|
@@ -1,12 +1,15 @@
|
||||
//! Utility functions for smart contract interactions.
|
||||
|
||||
use ethers::abi::{Abi, Token, ParamType};
|
||||
use ethers::abi::{Abi, ParamType, Token};
|
||||
use ethers::types::{Address, U256};
|
||||
use rhai::{Array, Dynamic};
|
||||
use std::str::FromStr;
|
||||
use rhai::{Dynamic, Array};
|
||||
|
||||
/// Convert Rhai Dynamic values to ethers Token types
|
||||
pub fn convert_rhai_to_token(value: &Dynamic, expected_type: Option<&ParamType>) -> Result<Token, String> {
|
||||
pub fn convert_rhai_to_token(
|
||||
value: &Dynamic,
|
||||
expected_type: Option<&ParamType>,
|
||||
) -> Result<Token, String> {
|
||||
match value {
|
||||
// Handle integers
|
||||
v if v.is_int() => {
|
||||
@@ -18,25 +21,23 @@ pub fn convert_rhai_to_token(value: &Dynamic, expected_type: Option<&ParamType>)
|
||||
// Convert to I256 - in a real implementation, we would handle this properly
|
||||
// For now, we'll just use U256 for both types
|
||||
Ok(Token::Uint(U256::from(i as u64)))
|
||||
},
|
||||
_ => Err(format!("Expected {}, got integer", param_type))
|
||||
}
|
||||
_ => Err(format!("Expected {}, got integer", param_type)),
|
||||
}
|
||||
} else {
|
||||
// Default to Uint256 if no type info
|
||||
Ok(Token::Uint(U256::from(i as u64)))
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
// Handle strings and addresses
|
||||
v if v.is_string() => {
|
||||
let s = v.to_string();
|
||||
if let Some(param_type) = expected_type {
|
||||
match param_type {
|
||||
ParamType::Address => {
|
||||
match Address::from_str(&s) {
|
||||
Ok(addr) => Ok(Token::Address(addr)),
|
||||
Err(e) => Err(format!("Invalid address format: {}", e))
|
||||
}
|
||||
ParamType::Address => match Address::from_str(&s) {
|
||||
Ok(addr) => Ok(Token::Address(addr)),
|
||||
Err(e) => Err(format!("Invalid address format: {}", e)),
|
||||
},
|
||||
ParamType::String => Ok(Token::String(s)),
|
||||
ParamType::Bytes => {
|
||||
@@ -44,13 +45,13 @@ pub fn convert_rhai_to_token(value: &Dynamic, expected_type: Option<&ParamType>)
|
||||
if s.starts_with("0x") {
|
||||
match ethers::utils::hex::decode(&s[2..]) {
|
||||
Ok(bytes) => Ok(Token::Bytes(bytes)),
|
||||
Err(e) => Err(format!("Invalid hex string: {}", e))
|
||||
Err(e) => Err(format!("Invalid hex string: {}", e)),
|
||||
}
|
||||
} else {
|
||||
Ok(Token::Bytes(s.as_bytes().to_vec()))
|
||||
}
|
||||
},
|
||||
_ => Err(format!("Expected {}, got string", param_type))
|
||||
}
|
||||
_ => Err(format!("Expected {}, got string", param_type)),
|
||||
}
|
||||
} else {
|
||||
// Try to detect type from string format
|
||||
@@ -58,14 +59,14 @@ pub fn convert_rhai_to_token(value: &Dynamic, expected_type: Option<&ParamType>)
|
||||
// Likely an address
|
||||
match Address::from_str(&s) {
|
||||
Ok(addr) => Ok(Token::Address(addr)),
|
||||
Err(_) => Ok(Token::String(s))
|
||||
Err(_) => Ok(Token::String(s)),
|
||||
}
|
||||
} else {
|
||||
Ok(Token::String(s))
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
// Handle booleans
|
||||
v if v.is_bool() => {
|
||||
let b = v.as_bool().unwrap();
|
||||
@@ -78,8 +79,8 @@ pub fn convert_rhai_to_token(value: &Dynamic, expected_type: Option<&ParamType>)
|
||||
} else {
|
||||
Ok(Token::Bool(b))
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
// Handle arrays
|
||||
v if v.is_array() => {
|
||||
let arr = v.clone().into_array().unwrap();
|
||||
@@ -88,47 +89,50 @@ pub fn convert_rhai_to_token(value: &Dynamic, expected_type: Option<&ParamType>)
|
||||
for item in arr.iter() {
|
||||
match convert_rhai_to_token(item, Some(inner_type)) {
|
||||
Ok(token) => tokens.push(token),
|
||||
Err(e) => return Err(e)
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
Ok(Token::Array(tokens))
|
||||
} else {
|
||||
Err("Array type mismatch or no type information available".to_string())
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
// Handle other types or return error
|
||||
_ => Err(format!("Unsupported Rhai type: {:?}", value))
|
||||
_ => Err(format!("Unsupported Rhai type: {:?}", value)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Validate and convert arguments based on function ABI
|
||||
pub fn prepare_function_arguments(
|
||||
abi: &Abi,
|
||||
function_name: &str,
|
||||
args: &Array
|
||||
abi: &Abi,
|
||||
function_name: &str,
|
||||
args: &Array,
|
||||
) -> Result<Vec<Token>, String> {
|
||||
// Get the function from the ABI
|
||||
let function = abi.function(function_name)
|
||||
let function = abi
|
||||
.function(function_name)
|
||||
.map_err(|e| format!("Function not found in ABI: {}", e))?;
|
||||
|
||||
|
||||
// Check if number of arguments matches
|
||||
if function.inputs.len() != args.len() {
|
||||
return Err(format!(
|
||||
"Wrong number of arguments for function '{}': expected {}, got {}",
|
||||
function_name, function.inputs.len(), args.len()
|
||||
"Wrong number of arguments for function '{}': expected {}, got {}",
|
||||
function_name,
|
||||
function.inputs.len(),
|
||||
args.len()
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
// Convert each argument according to the expected type
|
||||
let mut tokens = Vec::new();
|
||||
for (i, (param, arg)) in function.inputs.iter().zip(args.iter()).enumerate() {
|
||||
match convert_rhai_to_token(arg, Some(¶m.kind)) {
|
||||
Ok(token) => tokens.push(token),
|
||||
Err(e) => return Err(format!("Error converting argument {}: {}", i, e))
|
||||
Err(e) => return Err(format!("Error converting argument {}: {}", i, e)),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Ok(tokens)
|
||||
}
|
||||
|
||||
@@ -137,12 +141,12 @@ pub fn convert_token_to_rhai(tokens: &[Token]) -> Dynamic {
|
||||
if tokens.is_empty() {
|
||||
return Dynamic::UNIT;
|
||||
}
|
||||
|
||||
|
||||
// If there's only one return value, return it directly
|
||||
if tokens.len() == 1 {
|
||||
return token_to_dynamic(&tokens[0]);
|
||||
}
|
||||
|
||||
|
||||
// If there are multiple return values, return them as an array
|
||||
let mut array = Array::new();
|
||||
for token in tokens {
|
||||
@@ -166,14 +170,14 @@ pub fn token_to_dynamic(token: &Token) -> Dynamic {
|
||||
rhai_arr.push(token_to_dynamic(item));
|
||||
}
|
||||
Dynamic::from(rhai_arr)
|
||||
},
|
||||
}
|
||||
Token::Tuple(tuple) => {
|
||||
let mut rhai_arr = Array::new();
|
||||
for item in tuple {
|
||||
rhai_arr.push(token_to_dynamic(item));
|
||||
}
|
||||
Dynamic::from(rhai_arr)
|
||||
},
|
||||
}
|
||||
// Handle other token types
|
||||
_ => {
|
||||
log::warn!("Unsupported token type: {:?}", token);
|
||||
|
@@ -11,74 +11,49 @@
|
||||
//! - `storage.rs`: Wallet storage functionality
|
||||
//! - `contract.rs`: Smart contract interaction functionality
|
||||
|
||||
mod wallet;
|
||||
mod provider;
|
||||
mod transaction;
|
||||
mod storage;
|
||||
mod contract;
|
||||
pub mod contract_utils;
|
||||
pub mod networks;
|
||||
mod provider;
|
||||
mod storage;
|
||||
mod transaction;
|
||||
mod wallet;
|
||||
// Re-export public types and functions
|
||||
pub use wallet::EthereumWallet;
|
||||
pub use networks::NetworkConfig;
|
||||
pub use wallet::EthereumWallet;
|
||||
|
||||
// Re-export wallet creation functions
|
||||
pub use storage::{
|
||||
create_ethereum_wallet_for_network,
|
||||
create_peaq_wallet,
|
||||
create_agung_wallet,
|
||||
create_ethereum_wallet_from_name_for_network,
|
||||
create_ethereum_wallet_from_name,
|
||||
create_ethereum_wallet_from_private_key_for_network,
|
||||
create_ethereum_wallet_from_private_key,
|
||||
create_agung_wallet, create_ethereum_wallet_for_network, create_ethereum_wallet_from_name,
|
||||
create_ethereum_wallet_from_name_for_network, create_ethereum_wallet_from_private_key,
|
||||
create_ethereum_wallet_from_private_key_for_network, create_peaq_wallet,
|
||||
};
|
||||
|
||||
// Re-export wallet management functions
|
||||
pub use storage::{
|
||||
get_current_ethereum_wallet_for_network,
|
||||
get_current_peaq_wallet,
|
||||
get_current_agung_wallet,
|
||||
clear_ethereum_wallets,
|
||||
clear_ethereum_wallets_for_network,
|
||||
clear_ethereum_wallets, clear_ethereum_wallets_for_network, get_current_agung_wallet,
|
||||
get_current_ethereum_wallet_for_network, get_current_peaq_wallet,
|
||||
};
|
||||
|
||||
// Re-export provider functions
|
||||
pub use provider::{
|
||||
create_provider,
|
||||
create_gnosis_provider,
|
||||
create_peaq_provider,
|
||||
create_agung_provider,
|
||||
create_agung_provider, create_gnosis_provider, create_peaq_provider, create_provider,
|
||||
};
|
||||
|
||||
// Re-export transaction functions
|
||||
pub use transaction::{
|
||||
get_balance,
|
||||
send_eth,
|
||||
format_balance,
|
||||
};
|
||||
pub use transaction::{format_balance, get_balance, send_eth};
|
||||
|
||||
// Re-export network registry functions
|
||||
pub use networks::{
|
||||
get_network_by_name,
|
||||
get_proper_network_name,
|
||||
list_network_names,
|
||||
get_all_networks,
|
||||
names,
|
||||
get_all_networks, get_network_by_name, get_proper_network_name, list_network_names, names,
|
||||
};
|
||||
|
||||
// Re-export contract functions
|
||||
pub use contract::{
|
||||
Contract,
|
||||
load_abi_from_json,
|
||||
call_read_function,
|
||||
call_write_function,
|
||||
estimate_gas,
|
||||
call_read_function, call_write_function, estimate_gas, load_abi_from_json, Contract,
|
||||
};
|
||||
|
||||
// Re-export contract utility functions
|
||||
pub use contract_utils::{
|
||||
convert_rhai_to_token,
|
||||
prepare_function_arguments,
|
||||
convert_token_to_rhai,
|
||||
token_to_dynamic,
|
||||
convert_rhai_to_token, convert_token_to_rhai, prepare_function_arguments, token_to_dynamic,
|
||||
};
|
||||
|
@@ -3,9 +3,9 @@
|
||||
//! This module provides a centralized registry of Ethereum networks and utilities
|
||||
//! to work with them.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::OnceLock;
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
/// Configuration for an EVM-compatible network
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
@@ -288,6 +288,17 @@ fn select_keyspace(name: &str) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
// Before switching, save the current keyspace state to registry
|
||||
if let Ok(current_space) = keyspace::get_current_space() {
|
||||
if let Ok(mut registry) = KEYSPACE_REGISTRY.lock() {
|
||||
// Find the password for the current space
|
||||
if let Some((_, password)) = registry.get(¤t_space.name).cloned() {
|
||||
// Update the registry with the current state
|
||||
registry.insert(current_space.name.clone(), (current_space, password));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try to get from registry first (for testing)
|
||||
if let Ok(registry) = KEYSPACE_REGISTRY.lock() {
|
||||
if let Some((space, _password)) = registry.get(name) {
|
||||
@@ -357,6 +368,14 @@ fn rhai_list_keypairs() -> Vec<String> {
|
||||
}
|
||||
}
|
||||
|
||||
fn rhai_count_keyspaces() -> i64 {
|
||||
rhai_list_keyspaces_actual().len() as i64
|
||||
}
|
||||
|
||||
fn rhai_count_keypairs() -> i64 {
|
||||
rhai_list_keypairs().len() as i64
|
||||
}
|
||||
|
||||
fn rhai_select_keypair(name: &str) -> bool {
|
||||
match keyspace::session_manager::select_keypair(name) {
|
||||
Ok(_) => true,
|
||||
@@ -377,7 +396,19 @@ fn rhai_clear_session() {
|
||||
|
||||
fn rhai_create_keypair(name: &str) -> bool {
|
||||
match keyspace::session_manager::create_keypair(name) {
|
||||
Ok(_) => true,
|
||||
Ok(_) => {
|
||||
// Update the registry with the current state after creating keypair
|
||||
if let Ok(current_space) = keyspace::get_current_space() {
|
||||
if let Ok(mut registry) = KEYSPACE_REGISTRY.lock() {
|
||||
// Find the password for the current space
|
||||
if let Some((_, password)) = registry.get(¤t_space.name).cloned() {
|
||||
// Update the registry with the current state
|
||||
registry.insert(current_space.name.clone(), (current_space, password));
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Error creating keypair '{}': {}", name, e);
|
||||
false
|
||||
@@ -998,6 +1029,8 @@ pub fn register_crypto_module(engine: &mut Engine) -> Result<(), Box<EvalAltResu
|
||||
engine.register_fn("select_keyspace", select_keyspace);
|
||||
engine.register_fn("list_keyspaces", rhai_list_keyspaces_actual);
|
||||
engine.register_fn("list_keypairs", rhai_list_keypairs);
|
||||
engine.register_fn("count_keyspaces", rhai_count_keyspaces);
|
||||
engine.register_fn("count_keypairs", rhai_count_keypairs);
|
||||
engine.register_fn("select_keypair", rhai_select_keypair);
|
||||
engine.register_fn("clear_session", rhai_clear_session);
|
||||
engine.register_fn("create_keypair", rhai_create_keypair);
|
||||
|
@@ -6,10 +6,8 @@ pub mod implementation;
|
||||
|
||||
// Re-export public types and functions
|
||||
pub use implementation::{
|
||||
generate_symmetric_key, derive_key_from_password,
|
||||
encrypt_symmetric, decrypt_symmetric,
|
||||
encrypt_with_key, decrypt_with_key,
|
||||
encrypt_key_space, decrypt_key_space,
|
||||
serialize_encrypted_space, deserialize_encrypted_space,
|
||||
EncryptedKeySpace, EncryptedKeySpaceMetadata
|
||||
decrypt_key_space, decrypt_symmetric, decrypt_with_key, derive_key_from_password,
|
||||
deserialize_encrypted_space, encrypt_key_space, encrypt_symmetric, encrypt_with_key,
|
||||
generate_symmetric_key, serialize_encrypted_space, EncryptedKeySpace,
|
||||
EncryptedKeySpaceMetadata,
|
||||
};
|
||||
|
@@ -1,5 +1,12 @@
|
||||
use rhai::{Engine, EvalAltResult};
|
||||
use sal_vault::rhai::*;
|
||||
use std::sync::Mutex;
|
||||
|
||||
// NOTE: These tests use global state (SESSION and KEYSPACE_REGISTRY) and are automatically
|
||||
// serialized using a global mutex to prevent test interference during parallel execution.
|
||||
|
||||
// Global test mutex to ensure tests run sequentially
|
||||
static TEST_MUTEX: Mutex<()> = Mutex::new(());
|
||||
|
||||
#[cfg(test)]
|
||||
mod rhai_integration_tests {
|
||||
@@ -13,6 +20,7 @@ mod rhai_integration_tests {
|
||||
|
||||
#[test]
|
||||
fn test_rhai_module_registration() {
|
||||
let _guard = TEST_MUTEX.lock().unwrap();
|
||||
let engine = create_test_engine();
|
||||
|
||||
// Test that the functions are registered by checking if they exist
|
||||
@@ -32,6 +40,7 @@ mod rhai_integration_tests {
|
||||
|
||||
#[test]
|
||||
fn test_symmetric_encryption_functions() {
|
||||
let _guard = TEST_MUTEX.lock().unwrap();
|
||||
let engine = create_test_engine();
|
||||
|
||||
let script = r#"
|
||||
@@ -52,6 +61,7 @@ mod rhai_integration_tests {
|
||||
|
||||
#[test]
|
||||
fn test_keyspace_functions() {
|
||||
let _guard = TEST_MUTEX.lock().unwrap();
|
||||
let engine = create_test_engine();
|
||||
|
||||
let script = r#"
|
||||
@@ -78,6 +88,7 @@ mod rhai_integration_tests {
|
||||
|
||||
#[test]
|
||||
fn test_keypair_functions() {
|
||||
let _guard = TEST_MUTEX.lock().unwrap();
|
||||
let engine = create_test_engine();
|
||||
|
||||
let script = r#"
|
||||
@@ -116,6 +127,7 @@ mod rhai_integration_tests {
|
||||
|
||||
#[test]
|
||||
fn test_signing_functions() {
|
||||
let _guard = TEST_MUTEX.lock().unwrap();
|
||||
let engine = create_test_engine();
|
||||
|
||||
let script = r#"
|
||||
@@ -157,6 +169,7 @@ mod rhai_integration_tests {
|
||||
|
||||
#[test]
|
||||
fn test_session_management() {
|
||||
let _guard = TEST_MUTEX.lock().unwrap();
|
||||
let engine = create_test_engine();
|
||||
|
||||
let script = r#"
|
||||
@@ -169,7 +182,8 @@ mod rhai_integration_tests {
|
||||
|
||||
// Test listing keyspaces
|
||||
let spaces = list_keyspaces();
|
||||
if spaces.len() < 2 {
|
||||
let space_count = count_keyspaces();
|
||||
if space_count < 2 {
|
||||
throw "Should have at least 2 keyspaces";
|
||||
}
|
||||
|
||||
@@ -182,7 +196,8 @@ mod rhai_integration_tests {
|
||||
|
||||
// Test listing keypairs in current space
|
||||
let keypairs = list_keypairs();
|
||||
if keypairs.len() != 1 {
|
||||
let keypair_count = count_keypairs();
|
||||
if keypair_count != 1 {
|
||||
throw "Should have exactly 1 keypair in space2";
|
||||
}
|
||||
|
||||
@@ -199,6 +214,7 @@ mod rhai_integration_tests {
|
||||
|
||||
#[test]
|
||||
fn test_error_handling() {
|
||||
let _guard = TEST_MUTEX.lock().unwrap();
|
||||
let engine = create_test_engine();
|
||||
|
||||
let script = r#"
|
||||
|
Reference in New Issue
Block a user