diff --git a/Cargo.toml b/Cargo.toml index ff779fe..1df70dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,8 +14,12 @@ getopts = "0.2.21" log = "0.4.14" simple_logger = "1.11.0" toml = "0.5.8" -rust-crypto = "^0.2" -blakeout = "0.1.0" +digest = "0.9.0" +sha2 = "0.9.3" +ed25519-dalek = "1.0.1" +signature = "1.3.0" +#rust-crypto = "^0.2" +blakeout = "0.3.0" num_cpus = "1.13.0" byteorder = "1.4.3" serde = { version = "1.0.123", features = ["derive"] } @@ -24,6 +28,8 @@ num-bigint = "0.4.0" num-traits = "0.2.14" chrono = { version = "0.4.13", features = ["serde"] } rand = "0.8.3" +# For ed25519-dalek +rand-old = { package = "rand", version = "0.7.0" } sqlite = "0.25.3" uuid = { version = "0.8.2", features = ["serde", "v4"] } mio = { version = "0.7.10", features = ["os-poll", "net"] } diff --git a/alfis.toml b/alfis.toml index b41e298..6d47f4e 100644 --- a/alfis.toml +++ b/alfis.toml @@ -5,13 +5,14 @@ listen = "[::]:4244" public = false # Bootstrap nodes -peers = ["test-ip4.alfis.name:4244", "test-ip6.alfis.name:4244"] +#peers = ["test-ip4.alfis.name:4244", "test-ip6.alfis.name:4244"] +peers = ["[300:1251::1]:4244"] # DNS server options [dns] listen = "127.0.0.1:5300" threads = 20 #AdGuard DNS servers to filter ads and trackers -forwarders = ["94.140.14.14:53", "94.140.15.15:53"] -#forwarders = ["[301:2522::53]:53", "[301:2923::53]:53"] +#forwarders = ["94.140.14.14:53", "94.140.15.15:53"] +forwarders = ["[301:2522::53]:53", "[303:8b1a::53]:53"] #forwarders = ["[301:2522::53]:53", "[301:2923::53]:53", "94.140.14.14:53", "94.140.15.15:53"] diff --git a/src/blockchain/hash_utils.rs b/src/blockchain/hash_utils.rs index 3918e44..ed751a4 100644 --- a/src/blockchain/hash_utils.rs +++ b/src/blockchain/hash_utils.rs @@ -1,18 +1,9 @@ use blakeout::Blakeout; -use crypto::digest::Digest; -use crypto::sha2::Sha256; use num_bigint::BigUint; use num_traits::One; use crate::{Block, Bytes, Keystore}; - -/// Creates needed hasher by current blockchain version -pub(crate) fn get_hasher_for_version(version: u32) -> Box { - match version { - 2 => Box::new(Blakeout::default()), - _ => Box::new(Sha256::new()) - } -} +use sha2::{Sha256, Digest}; /// Checks block's hash and returns true on valid hash or false otherwise pub fn check_block_hash(block: &Block) -> bool { @@ -20,21 +11,14 @@ pub fn check_block_hash(block: &Block) -> bool { copy.hash = Bytes::default(); copy.signature = Bytes::default(); let data = serde_json::to_string(©).unwrap(); - let mut hasher = get_hasher_for_version(block.version); - hash_data(&mut *hasher, data.as_bytes()) == block.hash + blakeout_data(data.as_bytes()) == block.hash } /// Hashes data by given hasher -pub fn hash_data(digest: &mut dyn Digest, data: &[u8]) -> Bytes { - let mut buf = match digest.output_bytes() { - 32 => Bytes::zero32(), - 64 => Bytes::zero64(), - _ => panic!("Supplied wrong digest!") - }; - - digest.input(data); - digest.result(buf.as_mut_slice()); - buf +pub fn blakeout_data(data: &[u8]) -> Bytes { + let mut digest = Blakeout::default(); + digest.update(data); + Bytes::from_bytes(digest.result()) } /// Checks block's signature, returns true if the signature is valid, false otherwise @@ -47,14 +31,12 @@ pub fn check_block_signature(block: &Block) -> bool { /// Hashes some identity (domain in case of DNS). If you give it a public key, it will hash with it as well. /// Giving public key is needed to create a confirmation field in [Transaction] pub fn hash_identity(identity: &str, key: Option<&Bytes>) -> Bytes { - let mut buf: [u8; 32] = [0; 32]; - let mut digest = Sha256::new(); - digest.input_str(identity); + let mut digest = Sha256::default(); + digest.update(identity.as_bytes()); if let Some(key) = key { - digest.input(key.as_slice()); + digest.update(key.as_slice()); } - digest.result(&mut buf); - Bytes::from_bytes(&buf) + Bytes::from_bytes(&digest.finalize()[..]) } /// There is no default PartialEq implementation for arrays > 32 in size diff --git a/src/commons/constants.rs b/src/commons/constants.rs index 365e99c..748cb42 100644 --- a/src/commons/constants.rs +++ b/src/commons/constants.rs @@ -1,9 +1,9 @@ -pub const CHAIN_VERSION: u32 = 1; +pub const CHAIN_VERSION: u32 = 2; -pub const ZONE_DIFFICULTY: u32 = 28; +pub const ZONE_DIFFICULTY: u32 = 26; pub const BLOCK_DIFFICULTY: u32 = 24; pub const LOCKER_DIFFICULTY: u32 = 18; -pub const KEYSTORE_DIFFICULTY: usize = 25; +pub const KEYSTORE_DIFFICULTY: usize = 23; pub const LOCKER_BLOCK_START: u64 = 12; pub const LOCKER_BLOCK_LOCKERS: u64 = 6; diff --git a/src/keys.rs b/src/keys.rs index 027ae62..8f8de2d 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -1,4 +1,5 @@ -extern crate crypto; +extern crate rand; +extern crate ed25519_dalek; extern crate serde; extern crate serde_json; @@ -10,11 +11,9 @@ use std::path::Path; use std::sync::{Arc, atomic, Mutex}; use std::sync::atomic::{AtomicBool, AtomicUsize}; -use crypto::ed25519::{keypair, signature, verify}; +use ed25519_dalek::Keypair; #[allow(unused_imports)] use log::{debug, error, info, trace, warn}; -use rand::{Rng, RngCore, thread_rng}; -use serde::{Deserialize, Serialize}; use crate::blockchain::hash_utils::*; use crate::Context; @@ -22,40 +21,51 @@ use crate::event::Event; use crate::commons::KEYSTORE_DIFFICULTY; use crate::bytes::Bytes; use blakeout::Blakeout; -use self::crypto::digest::Digest; use std::time::Instant; use std::cell::RefCell; +use self::ed25519_dalek::{Signer, PublicKey, Verifier, SecretKey}; +use self::ed25519_dalek::ed25519::signature::Signature; +use rand_old::{CryptoRng, RngCore}; +use rand_old::rngs::OsRng; -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Debug)] pub struct Keystore { - private_key: Bytes, - public_key: Bytes, - #[serde(skip)] + keypair: Keypair, hash: RefCell, - #[serde(skip)] path: String, - #[serde(skip)] - seed: Vec, } impl Keystore { pub fn new() -> Self { - let mut buf = [0u8; 32]; - let mut rng = thread_rng(); - rng.fill(&mut buf); - let (private, public) = keypair(&buf); - Keystore { private_key: Bytes::from_bytes(&private), public_key: Bytes::from_bytes(&public), hash: RefCell::new(Bytes::default()), path: String::new(), seed: Vec::from(&buf[..]) } + let mut csprng = OsRng::default(); + let keypair = ed25519_dalek::Keypair::generate(&mut csprng); + Keystore { keypair, hash: RefCell::new(Bytes::default()), path: String::new() } + } + + pub fn from_random(csprng: &mut R) -> Self where R: CryptoRng + RngCore { + let keypair = ed25519_dalek::Keypair::generate(csprng); + Keystore { keypair, hash: RefCell::new(Bytes::default()), path: String::new() } } pub fn from_bytes(seed: &[u8]) -> Self { - let (private, public) = keypair(&seed); - Keystore { private_key: Bytes::from_bytes(&private), public_key: Bytes::from_bytes(&public), hash: RefCell::new(Bytes::default()), path: String::new(), seed: Vec::from(seed) } + let keypair = Keypair::from_bytes(seed).expect("Error creating keypair from bytes!"); + Keystore { keypair, hash: RefCell::new(Bytes::default()), path: String::new() } + } + + pub fn from_random_bytes(key: &[u8]) -> Self { + let secret = SecretKey::from_bytes(&key).unwrap(); + let public = PublicKey::from(&secret); + let keypair = Keypair { secret, public }; + Keystore { keypair, hash: RefCell::new(Bytes::default()), path: String::new() } } pub fn from_file(filename: &str, _password: &str) -> Option { let path = Path::new(filename); match fs::read(&path) { Ok(key) => { + if key.len() == 32 { + return Some(Keystore::from_random_bytes(key.as_slice())); + } let mut keystore = Self::from_bytes(key.as_slice()); keystore.path = path.to_str().unwrap().to_owned(); Some(keystore) @@ -66,12 +76,12 @@ impl Keystore { } } - //TODO Implement error conditions pub fn save(&mut self, filename: &str, _password: &str) { match File::create(Path::new(filename)) { Ok(mut f) => { //TODO implement key encryption - f.write_all(&self.seed).expect("Error saving keystore"); + let bytes = self.keypair.to_bytes(); + f.write_all(&bytes).expect("Error saving keystore"); self.path = filename.to_owned(); } Err(_) => { error!("Error saving key file!"); } @@ -79,11 +89,11 @@ impl Keystore { } pub fn get_public(&self) -> Bytes { - self.public_key.clone() + Bytes::from_bytes(&self.keypair.public.to_bytes()) } pub fn get_private(&self) -> Bytes { - self.private_key.clone() + Bytes::from_bytes(&self.keypair.secret.to_bytes()) } pub fn get_path(&self) -> &str { @@ -92,24 +102,36 @@ impl Keystore { pub fn get_hash(&self) -> Bytes { if self.hash.borrow().is_empty() { - self.hash.replace(hash_data(&mut Blakeout::default(), &self.public_key)); + self.hash.replace(blakeout_data(&self.get_public())); } self.hash.borrow().clone() } pub fn sign(&self, message: &[u8]) -> [u8; 64] { - signature(message, &self.private_key) + self.keypair.sign(message).to_bytes() } pub fn check(message: &[u8], public_key: &[u8], signature: &[u8]) -> bool { - verify(message, public_key, signature) + let key = PublicKey::from_bytes(public_key).expect("Wrong public key!"); + let signature = Signature::from_bytes(signature).unwrap(); + match key.verify(message, &signature) { + Ok(_) => { true } + Err(_) => { false } + } + } +} + +impl Clone for Keystore { + fn clone(&self) -> Self { + let keypair = Keypair::from_bytes(&self.keypair.to_bytes()).unwrap(); + Self { keypair, hash: RefCell::new(Bytes::default()), path: self.path.clone() } } } /// Checks if some public key is "strong" enough to mine domains /// TODO Optimize by caching Blakeout somewhere pub fn check_public_key_strength(key: &Bytes, strength: usize) -> bool { - let bytes = hash_data(&mut Blakeout::default(), &key); + let bytes = blakeout_data(&key); hash_is_good(&bytes, strength) } @@ -154,18 +176,18 @@ pub fn create_key(context: Arc>) { } fn generate_key(difficulty: usize, mining: Arc) -> Option { + use self::rand::RngCore; let mut rng = rand::thread_rng(); - let mut buf = [0u8; 32]; - let mut digest = Blakeout::default(); let mut time = Instant::now(); let mut count = 0u128; + let mut digest = Blakeout::default(); + let mut buf = [0u8; 32]; loop { rng.fill_bytes(&mut buf); - let keystore = Keystore::from_bytes(&buf); + let keystore = Keystore::from_random_bytes(&buf); digest.reset(); - digest.input(&keystore.public_key); - digest.result(&mut buf); - if hash_is_good(&buf, difficulty) { + digest.update(keystore.get_public().as_slice()); + if hash_is_good(digest.result(), difficulty) { info!("Generated keypair with public key: {:?} and hash {:?}", &keystore.get_public(), &keystore.get_hash()); return Some(keystore); } diff --git a/src/miner.rs b/src/miner.rs index 34e9695..02a3e84 100644 --- a/src/miner.rs +++ b/src/miner.rs @@ -4,7 +4,6 @@ use std::thread; use std::time::Duration; use chrono::Utc; -use crypto::digest::Digest; #[allow(unused_imports)] use log::{debug, error, info, trace, warn}; use num_cpus; @@ -15,6 +14,7 @@ use crate::blockchain::enums::BlockQuality; use crate::blockchain::hash_utils::*; use crate::keys::check_public_key_strength; use crate::event::Event; +use blakeout::Blakeout; pub struct Miner { context: Arc>, @@ -149,8 +149,7 @@ impl Miner { let live_threads = Arc::clone(&live_threads); thread::spawn(move || { live_threads.fetch_add(1, Ordering::SeqCst); - let mut hasher = get_hasher_for_version(block.version); - match find_hash(Arc::clone(&context), &mut *hasher, block, Arc::clone(&mining)) { + match find_hash(Arc::clone(&context), block, Arc::clone(&mining)) { None => { debug!("Mining was cancelled"); let count = live_threads.fetch_sub(1, Ordering::SeqCst); @@ -185,10 +184,10 @@ impl Miner { } } -fn find_hash(context: Arc>, digest: &mut dyn Digest, mut block: Block, running: Arc) -> Option { - let mut buf: [u8; 32] = [0; 32]; +fn find_hash(context: Arc>, mut block: Block, running: Arc) -> Option { let difficulty = block.difficulty as usize; let full = block.transaction.is_some(); + let mut digest = Blakeout::new(); loop { block.random = rand::random(); let next_allowed_block = { @@ -215,10 +214,9 @@ fn find_hash(context: Arc>, digest: &mut dyn Digest, mut block: B block.nonce = nonce; digest.reset(); - digest.input(&block.as_bytes()); - digest.result(&mut buf); - if hash_is_good(&buf, difficulty) { - block.hash = Bytes::from_bytes(&buf); + digest.update(&block.as_bytes()); + if hash_is_good(digest.result(), difficulty) { + block.hash = Bytes::from_bytes(digest.result()); return Some(block); }