Skip to content

Commit

Permalink
Refactor wallet management and add wallet selection command
Browse files Browse the repository at this point in the history
  • Loading branch information
jamesatomc committed Jan 12, 2025
1 parent 592357c commit 3c86acc
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 69 deletions.
37 changes: 36 additions & 1 deletion crates/command/src/keytool_cli/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::io;
use colored::Colorize;
use key::{generate_karix_address, list_wallet_files, load_wallet, save_wallet, };
use key::{generate_karix_address, list_wallet_files, load_wallet, save_wallet, set_selected_wallet, };

use k2::blockchain::{load_blockchain, BALANCES};
use std::process::exit;
Expand All @@ -14,6 +14,7 @@ struct CommandInfo {
const COMMANDS: &[CommandInfo] = &[
CommandInfo { name: "generate", description: "Generate new address" },
CommandInfo { name: "balance", description: "Check balance" },
CommandInfo { name: "select", description: "Select wallet" },
CommandInfo { name: "wallet", description: "Load existing wallet" },
CommandInfo { name: "send", description: "Send coins" },
CommandInfo { name: "list", description: "List wallet files" },
Expand Down Expand Up @@ -88,6 +89,40 @@ pub fn handle_keytool_command() -> Option<String> {
println!("Balance for {}: {}", public_address.green(), balance.to_string().green());
return None; // Return None to indicate no address to be used further
},

"select" => {
match list_wallet_files() {
Ok(wallets) => {
if wallets.is_empty() {
println!("{}", "No wallets found!".red());
return None;
}

println!("\nAvailable wallets:");
for (i, (wallet, _)) in wallets.iter().enumerate() {
println!("{}. {}", i + 1, wallet.trim_end_matches(".toml"));
}

println!("\nEnter wallet number to select:");
let mut input = String::new();
let _ = io::stdin().read_line(&mut input);

if let Ok(index) = input.trim().parse::<usize>() {
if index > 0 && index <= wallets.len() {
let selected = wallets[index - 1].0.trim_end_matches(".toml");
if let Err(e) = set_selected_wallet(selected) {
println!("Error setting wallet: {}", e);
} else {
println!("Selected wallet: {}", selected.green());
}
}
}
},
Err(e) => println!("Error listing wallets: {}", e),
}
None
},

"wallet" => { // String comparison for "wallet"
println!("Enter public address to load:");
let mut public_address = String::new();
Expand Down
12 changes: 0 additions & 12 deletions crates/core/k2/src/blockchain/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ use bincode;
use consensus_pos::Blake3Algorithm;
use dirs;
use rocksdb::*;
use thiserror::Error;
use std::collections::{HashMap, VecDeque};
use std::fs;
use std::path::PathBuf;
Expand Down Expand Up @@ -87,17 +86,6 @@ pub fn load_blockchain() {
*balances.entry(tx.sender.clone()).or_insert(0) -= tx.amount;
*balances.entry(tx.receiver.clone()).or_insert(0) += tx.amount;
}

if !block.data.is_empty() {
if let Some(modules_mutex) = MOVE_MODULES.as_ref() {
if let Ok(mut modules) = modules_mutex.lock() {
modules.insert(block.hash.clone(), block.data.clone());
}
} else {
// Handle the case where MOVE_MODULES is None
eprintln!("MOVE_MODULES is not initialized");
}
}
}

BALANCES = Some(Mutex::new(balances));
Expand Down
49 changes: 22 additions & 27 deletions crates/core/k2/src/simulation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,19 @@ pub static mut TRANSACTION_SENDER: Option<Sender<Transaction>> = None;
pub static mut TRANSACTION_RECEIVER: Option<Receiver<Transaction>> = None;

pub fn run_blockchain(running: Arc<Mutex<bool>>, miner_address: String) {
let max_tokens = 11_000_000; // Maximum token supply
let max_tokens = 11_000_000; // Maximum token supply
let mut tokens_per_block = 25; // Initial block reward
let halving_interval = 210_000; // Halve the block reward every 210,000 blocks
let block_size = 2_250_000; // 2.25 MB in bytes



// Assume there's a global variable for pending transactions
static PENDING_TRANSACTIONS: Mutex<Vec<Transaction>> = Mutex::new(Vec::new());

unsafe {
// Initialize the channel within the function
let (sender, receiver) = unbounded();
TRANSACTION_SENDER = Some(sender);
TRANSACTION_RECEIVER = Some(receiver);
TRANSACTION_RECEIVER = Some(receiver);

if BLOCKCHAIN.is_empty() {
let genesis_data = vec![0; block_size];
Expand All @@ -45,14 +43,14 @@ pub fn run_blockchain(running: Arc<Mutex<bool>>, miner_address: String) {
tokens_per_block,
genesis_transactions,
miner_address.clone(),
hasher
hasher,
));
TOTAL_TOKENS += tokens_per_block;
BALANCES.as_mut().unwrap().lock().unwrap().entry(miner_address.clone()).and_modify(|balance| *balance += tokens_per_block).or_insert(tokens_per_block);
info!("Genesis block created with hash: {}", BLOCKCHAIN.back().unwrap().hash);
}

loop {
loop {
// Receive transactions from the channel
if let Ok(transaction) = TRANSACTION_RECEIVER.as_ref().unwrap().try_recv() {
info!("Received new transaction: {:?}", transaction);
Expand All @@ -70,7 +68,7 @@ pub fn run_blockchain(running: Arc<Mutex<bool>>, miner_address: String) {
let miner_reward = if TOTAL_TOKENS < max_tokens {
tokens_per_block
} else {
0
0
};

let prev_block = BLOCKCHAIN.back().unwrap();
Expand All @@ -97,21 +95,21 @@ pub fn run_blockchain(running: Arc<Mutex<bool>>, miner_address: String) {
.as_secs(),
signature: None, // Add an empty signature or a valid one if available
tx_type: TransactionType::Transfer, // Add default transfer type
data: vec![], // Add empty data
data: vec![], // Add empty data
coin_type: None, // Add coin type if available
});
info!("No transactions found. Created a zero-fee transaction for the miner.");
}

let hasher = Blake3Algorithm;
let new_block = Block::new(
prev_block.index + 1,
new_data,
prev_block.hash.clone(),
prev_block.index + 1,
new_data,
prev_block.hash.clone(),
miner_reward, // Use calculated miner_reward
transactions.clone(),
miner_address.clone(),
hasher
transactions.clone(),
miner_address.clone(),
hasher,
);

if !new_block.verify(prev_block) {
Expand All @@ -136,14 +134,14 @@ pub fn run_blockchain(running: Arc<Mutex<bool>>, miner_address: String) {

// Update TOTAL_TOKENS only if it's less than the max supply
if TOTAL_TOKENS < max_tokens {
TOTAL_TOKENS += tokens_per_block;
TOTAL_TOKENS += tokens_per_block;
}

// Save blockchain every time a new block is created
save_blockchain();


println!("{} {} | block={} | hash={:}... | prev={:}... | miner={} | reward={}",
println!(
"{} {} | block={} | hash={:}... | prev={:}... | miner={} | reward={}",
"[INFO]".green(),
Local::now().format("%Y-%m-%d %H:%M:%S"),
BLOCKCHAIN.len().to_string().blue(),
Expand All @@ -152,16 +150,18 @@ pub fn run_blockchain(running: Arc<Mutex<bool>>, miner_address: String) {
format!("{}t", transaction_fees).cyan(),
format!("{}t", tokens_per_block).cyan()
);

if BLOCKCHAIN.len() % halving_interval == 0 && TOTAL_TOKENS < max_tokens {
tokens_per_block /= 2;
println!("{} Block reward halved to {}",
println!(
"{} Block reward halved to {}",
"[HALV]".red(),
format!("{} tokens", tokens_per_block).red()
);
}

println!("{} blocks={} supply={}",

println!(
"{} blocks={} supply={}",
"[STAT]".magenta(),
BLOCKCHAIN.len().to_string().blue(),
TOTAL_TOKENS.to_string().magenta()
Expand All @@ -171,9 +171,4 @@ pub fn run_blockchain(running: Arc<Mutex<bool>>, miner_address: String) {
thread::sleep(Duration::from_millis(550));
}
}




}

}
45 changes: 23 additions & 22 deletions crates/core/k2/src/transaction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::gas::{
use move_core_types::language_storage::TypeTag;
use serde::{Serialize, Deserialize};
use secp256k1::{Secp256k1, Message, SecretKey};
use secp256k1::ecdsa::Signature;
use sha2::{Sha256, Digest};

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
Expand Down Expand Up @@ -58,7 +59,7 @@ impl Transaction {
sender,
receiver: String::new(),
amount: 0,
gas_cost: 0.0,
gas_cost: 0.0,
timestamp: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
Expand All @@ -75,23 +76,6 @@ impl Transaction {
}
}

pub fn apply_transfer(&self, balances: &mut std::collections::HashMap<String, u64>) -> Result<(), String> {
if let TransactionType::Transfer = self.tx_type {
let sender_balance = balances.get(&self.sender).cloned().unwrap_or(0);
if sender_balance < self.amount {
return Err("Insufficient funds".to_string());
}
balances.insert(self.sender.clone(), sender_balance - self.amount);

let receiver_balance = balances.get(&self.receiver).cloned().unwrap_or(0);
balances.insert(self.receiver.clone(), receiver_balance + self.amount);

Ok(())
} else {
Err("Not a transfer transaction".to_string())
}
}

pub fn new_move_deploy(sender: String, module_bytes: Vec<u8>) -> Self {
Self {
sender,
Expand All @@ -118,14 +102,14 @@ impl Transaction {
let tx_hash = self.hash();
let message = Message::from_digest_slice(&tx_hash)
.map_err(|e| format!("Message creation error: {}", e))?;

let secret_key = SecretKey::from_slice(private_key)
.map_err(|e| format!("Invalid private key: {}", e))?;

let signature = secp.sign_ecdsa(&message, &secret_key);
let signature_hex = hex::encode(signature.serialize_compact());
self.signature = Some(signature_hex.clone());

Ok(signature_hex)
}

Expand All @@ -143,9 +127,26 @@ impl Transaction {
let mut hash = [0u8; 32];
hash.copy_from_slice(&result);
hash
}
}

// Add verification functionality
pub fn verify_signature(&self, secp: &Secp256k1<secp256k1::All>, public_key: &[u8]) -> Result<bool, String> {
let tx_hash = self.hash();
let message = Message::from_digest_slice(&tx_hash)
.map_err(|e| format!("Message creation error: {}", e))?;

let public_key = secp256k1::PublicKey::from_slice(public_key)
.map_err(|e| format!("Invalid public key: {}", e))?;

let signature = Signature::from_compact(
&hex::decode(self.signature.as_ref().unwrap())
.map_err(|e| format!("Invalid signature: {}", e))?
)
.map_err(|e| format!("Invalid signature: {}", e))?;

Ok(secp.verify_ecdsa(&message, &signature, &public_key).is_ok())
}

pub fn apply_coin_transfer(&self, balances: &mut std::collections::HashMap<(String, Option<String>), u64>) -> Result<(), String> {
if let TransactionType::Transfer = self.tx_type {
let coin = self.coin_type.clone();
Expand Down
2 changes: 1 addition & 1 deletion crates/core/wallet/key/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ lazy_static.workspace = true
consensus-pos.workspace = true

toml.workspace = true

log.workspace = true
serde.workspace = true
chacha20poly1305.workspace = true
thiserror.workspace = true
Expand Down
39 changes: 33 additions & 6 deletions crates/core/wallet/key/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
use std::{collections::HashMap, fs, io::Write, path::PathBuf, str::FromStr as _, sync::Mutex};
use std::{collections::HashMap, fs, io::{self, Write}, path::PathBuf, str::FromStr as _, sync::Mutex};
use serde::{Deserialize, Serialize};
use bip39::Mnemonic;

use log::{debug, error};
use secp256k1::{Secp256k1, Message, SecretKey};
use rand::rngs::OsRng;
use hex;


// Import Mutex and HashMap from std::sync
use k2::{blockchain::{get_kari_dir, save_blockchain, BALANCES}, gas::TRANSACTION_GAS_COST, transaction::Transaction};
use k2::{blockchain::{get_kari_dir, save_blockchain, BALANCES}, config::{load_config, save_config}, gas::TRANSACTION_GAS_COST, transaction::Transaction};
use serde_json::json;

pub fn check_wallet_exists() -> bool {
match list_wallet_files() {
Expand Down Expand Up @@ -42,22 +43,48 @@ pub fn save_wallet(address: &str, private_key: &str, seed_phrase: &str) {
println!("Wallet saved to {:?}", wallet_file);
}

/// Set the selected wallet address in the configuration
pub fn set_selected_wallet(wallet_address: &str) -> io::Result<()> {
// Load existing config
let mut config = load_config()?;

// Update miner_address in config
if let Some(obj) = config.as_object_mut() {
obj.insert("miner_address".to_string(), json!(wallet_address));
}

// Save updated config
save_config(&config)
}

pub fn load_wallet(address: &str) -> Option<Wallet> {
// Validate input
// Input validation with logging
if address.trim().is_empty() {
debug!("Attempted to load wallet with empty address");
return None;
}

let kari_dir = get_kari_dir();
let wallet_file: PathBuf = kari_dir.join("wallets").join(format!("{}.toml", address));

debug!("Attempting to load wallet from: {}", wallet_file.display());

if wallet_file.exists() {
// Handle potential IO and parsing errors gracefully
// Handle potential IO and parsing errors with logging
fs::read_to_string(&wallet_file)
.map_err(|e| {
error!("Failed to read wallet file: {}", e);
e
})
.ok()
.and_then(|data| toml::from_str(&data).ok())
.and_then(|data| {
toml::from_str(&data).map_err(|e| {
error!("Failed to parse wallet TOML: {}", e);
e
}).ok()
})
} else {
debug!("Wallet file not found: {}", wallet_file.display());
None
}
}
Expand Down

0 comments on commit 3c86acc

Please sign in to comment.