From f5949e6ec0578dbae22724dfc1b8a83911a5930b Mon Sep 17 00:00:00 2001 From: Revertron Date: Tue, 23 Mar 2021 18:55:11 +0100 Subject: [PATCH] Reworked handling appropriate (mined) keys absence. Now that info handled in UI as well. It won't allow users without keys to mine domains or zones. --- alfis.toml | 4 +- src/context.rs | 10 ++--- src/dns/server.rs | 16 +++++++- src/event.rs | 5 ++- src/keys.rs | 8 +++- src/main.rs | 19 ++++------ src/miner.rs | 51 +++++++++++++++----------- src/p2p/network.rs | 45 ++++++++++++++++++----- src/web_ui.rs | 83 +++++++++++++++++++++++++++--------------- src/webview/index.html | 26 ++++++------- src/webview/scripts.js | 14 +++++++ 11 files changed, 184 insertions(+), 97 deletions(-) diff --git a/alfis.toml b/alfis.toml index c0d96db..e043276 100644 --- a/alfis.toml +++ b/alfis.toml @@ -1,8 +1,8 @@ # Settings origin = "00000102C2F9BFD2803284D93327F089D60FC72A06F19AF2384567F2646B8348" -key_file = "default.key" +key_file = "default1.key" listen = "[::]:4244" -public = false +public = true # Bootstrap nodes #peers = ["test-ip4.alfis.name:4244", "test-ip6.alfis.name:4244"] diff --git a/src/context.rs b/src/context.rs index debb24e..3a81a98 100644 --- a/src/context.rs +++ b/src/context.rs @@ -6,7 +6,7 @@ use log::{trace, debug, info, warn, error}; pub struct Context { pub app_version: String, pub settings: Settings, - pub keystore: Keystore, + pub keystore: Option, pub chain: Chain, pub x_zones: ExternalZones, pub bus: Bus, @@ -14,7 +14,7 @@ pub struct Context { impl Context { /// Creating an essential context to work with - pub fn new(app_version: String, settings: Settings, keystore: Keystore, chain: Chain) -> Context { + pub fn new(app_version: String, settings: Settings, keystore: Option, chain: Chain) -> Context { Context { app_version, settings, keystore, chain, x_zones: ExternalZones::new(), bus: Bus::new() } } @@ -26,17 +26,17 @@ impl Context { warn!("Error loading keystore '{}'!", filename); }, Some(keystore) => { - self.keystore = keystore; + self.keystore = Some(keystore); }, } self } - pub fn get_keystore(&self) -> Keystore { + pub fn get_keystore(&self) -> Option { self.keystore.clone() } - pub fn set_keystore(&mut self, keystore: Keystore) { + pub fn set_keystore(&mut self, keystore: Option) { self.keystore = keystore; } diff --git a/src/dns/server.rs b/src/dns/server.rs index c076c74..eb18590 100644 --- a/src/dns/server.rs +++ b/src/dns/server.rs @@ -244,8 +244,14 @@ impl DnsServer for DnsUdpServer { let mut req_buffer = BytePacketBuffer::new(); let (_, src) = match socket.recv_from(&mut req_buffer.buf) { Ok(x) => x, - Err(e) => { - debug!("Failed to read from UDP socket: {:?}", e); + Err(err) => { + if let Some(code) = err.raw_os_error() { + if code == 10004 { + debug!("UDP service loop has finished"); + break; + } + } + debug!("Failed to read from UDP socket: {:?}", err); continue; } }; @@ -347,6 +353,12 @@ impl DnsServer for DnsTcpServer { let stream = match wrap_stream { Ok(stream) => stream, Err(err) => { + if let Some(code) = err.raw_os_error() { + if code == 10004 { + debug!("TCP service loop has finished"); + break; + } + } warn!("Failed to accept TCP connection: {:?}", err); continue; } diff --git a/src/event.rs b/src/event.rs index fde9772..658ba48 100644 --- a/src/event.rs +++ b/src/event.rs @@ -1,4 +1,4 @@ -use crate::Bytes; +use crate::{Bytes, Keystore}; #[derive(Clone, PartialEq, Debug)] pub enum Event { @@ -12,7 +12,8 @@ pub enum Event { NewBlockReceived, BlockchainChanged { index: u64 }, ActionStopMining, - ActionMineLocker { index: u64, hash: Bytes }, + ActionMineLocker { index: u64, hash: Bytes, keystore: Box }, + ActionQuit, NetworkStatus { nodes: usize, blocks: u64 }, Syncing { have: u64, height: u64 }, SyncFinished, diff --git a/src/keys.rs b/src/keys.rs index e9876b6..895069f 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -128,6 +128,12 @@ impl Clone for Keystore { } } +impl PartialEq for Keystore { + fn eq(&self, other: &Self) -> bool { + self.keypair.to_bytes().eq(&other.keypair.to_bytes()) + } +} + /// 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 { @@ -155,7 +161,7 @@ pub fn create_key(context: Arc>) { let hash = keystore.get_hash().to_string(); info!("Key mined successfully: {:?}, hash: {}", &keystore.get_public(), &hash); context.bus.post(Event::KeyCreated { path: keystore.get_path().to_owned(), public: keystore.get_public().to_string(), hash }); - context.set_keystore(keystore); + context.set_keystore(Some(keystore)); } } let miners = miners_count.fetch_sub(1, atomic::Ordering::SeqCst) - 1; diff --git a/src/main.rs b/src/main.rs index cd64977..8a6ea79 100644 --- a/src/main.rs +++ b/src/main.rs @@ -80,13 +80,7 @@ fn main() { let settings = Settings::load(&config_name); info!(target: LOG_TARGET_MAIN, "Loaded settings: {:?}", &settings); - let keystore: Keystore = match Keystore::from_file(&settings.key_file, "") { - None => { - warn!(target: LOG_TARGET_MAIN, "Generated temporary keystore. Please, generate full-privileged keys."); - Keystore::new() - } - Some(keystore) => { keystore } - }; + let keystore = Keystore::from_file(&settings.key_file, ""); let chain: Chain = Chain::new(&settings); if opt_matches.opt_present("l") { for i in 1..(chain.height() + 1) { @@ -102,7 +96,8 @@ fn main() { Some(block) => { trace!(target: LOG_TARGET_MAIN, "Loaded DB with origin {:?}", &block.hash); } } let settings_copy = settings.clone(); - let context: Arc> = Arc::new(Mutex::new(Context::new(env!("CARGO_PKG_VERSION").to_owned(), settings, keystore, chain))); + let context = Context::new(env!("CARGO_PKG_VERSION").to_owned(), settings, keystore, chain); + let context: Arc> = Arc::new(Mutex::new(context)); dns_utils::start_dns_server(&context, &settings_copy); let mut miner_obj = Miner::new(Arc::clone(&context)); @@ -136,9 +131,11 @@ fn create_genesis_if_needed(context: &Arc>, miner: &Arc>, - blocks: Arc>>, + jobs: Arc>>, running: Arc, mining: Arc, cond_var: Arc @@ -28,15 +29,15 @@ impl Miner { pub fn new(context: Arc>) -> Self { Miner { context, - blocks: Arc::new(Mutex::new(Vec::new())), + jobs: Arc::new(Mutex::new(Vec::new())), running: Arc::new(AtomicBool::new(false)), mining: Arc::new(AtomicBool::new(false)), cond_var: Arc::new(Condvar::new()) } } - pub fn add_block(&mut self, block: Block) { - self.blocks.lock().unwrap().push(block); + pub fn add_block(&mut self, block: Block, keystore: Keystore) { + self.jobs.lock().unwrap().push(MineJob { block, keystore }); self.cond_var.notify_one(); } @@ -48,7 +49,7 @@ impl Miner { pub fn start_mining_thread(&mut self) { let context = Arc::clone(&self.context); - let blocks = self.blocks.clone(); + let blocks = self.jobs.clone(); let running = self.running.clone(); let mining = self.mining.clone(); let cond_var = self.cond_var.clone(); @@ -73,7 +74,7 @@ impl Miner { } }); let mining = self.mining.clone(); - let blocks = self.blocks.clone(); + let blocks = self.jobs.clone(); let cond_var = self.cond_var.clone(); self.context.lock().unwrap().bus.register(move |_uuid, e| { match e { @@ -82,11 +83,11 @@ impl Miner { Event::ActionStopMining => { mining.store(false, Ordering::SeqCst); } - Event::ActionMineLocker { index, hash } => { + Event::ActionMineLocker { index, hash, keystore } => { if !mining.load(Ordering::SeqCst) { let mut block = Block::new(None, Bytes::default(), hash, LOCKER_DIFFICULTY); block.index = index; - blocks.lock().unwrap().push(block); + blocks.lock().unwrap().push(MineJob { block, keystore: keystore.deref().clone() }); cond_var.notify_all(); info!("Added a locker block to mine"); } @@ -101,16 +102,16 @@ impl Miner { self.running.load(Ordering::Relaxed) } - fn mine_internal(context: Arc>, mut block: Block, mining: Arc) { + fn mine_internal(context: Arc>, mut job: MineJob, mining: Arc) { // Clear signature and hash just in case - block.signature = Bytes::default(); - block.hash = Bytes::default(); - block.version = CHAIN_VERSION; + job.block.signature = Bytes::default(); + job.block.hash = Bytes::default(); + job.block.version = CHAIN_VERSION; // If this block needs to be a locker - if block.index > 0 && !block.prev_block_hash.is_empty() { + if job.block.index > 0 && !job.block.prev_block_hash.is_empty() { info!("Mining locker block"); - block.pub_key = context.lock().unwrap().keystore.get_public(); - if !check_public_key_strength(&block.pub_key, KEYSTORE_DIFFICULTY) { + job.block.pub_key = job.keystore.get_public(); + if !check_public_key_strength(&job.block.pub_key, KEYSTORE_DIFFICULTY) { warn!("Can not mine block with weak public key!"); context.lock().unwrap().bus.post(Event::MinerStopped); mining.store(false, Ordering::SeqCst); @@ -121,7 +122,7 @@ impl Miner { Some(last_block) => { info!("Last block found"); // If we were doing something else and got new block before we could mine this block - if last_block.index > block.index || last_block.hash != block.prev_block_hash { + if last_block.index > job.block.index || last_block.hash != job.block.prev_block_hash { warn!("We missed block to lock"); context.lock().unwrap().bus.post(Event::MinerStopped); mining.store(false, Ordering::SeqCst); @@ -130,8 +131,8 @@ impl Miner { } } } else { - block.index = context.lock().unwrap().chain.height() + 1; - block.prev_block_hash = match context.lock().unwrap().chain.last_block() { + job.block.index = context.lock().unwrap().chain.height() + 1; + job.block.prev_block_hash = match context.lock().unwrap().chain.last_block() { None => { Bytes::default() } Some(block) => { block.hash } }; @@ -144,12 +145,12 @@ impl Miner { debug!("Starting {} threads for mining", cpus); for _cpu in 0..cpus { let context = Arc::clone(&context); - let block = block.clone(); + let job = job.clone(); let mining = Arc::clone(&mining); let live_threads = Arc::clone(&live_threads); thread::spawn(move || { live_threads.fetch_add(1, Ordering::SeqCst); - match find_hash(Arc::clone(&context), block, Arc::clone(&mining)) { + match find_hash(Arc::clone(&context), job.block, Arc::clone(&mining)) { None => { debug!("Mining was cancelled"); let count = live_threads.fetch_sub(1, Ordering::SeqCst); @@ -162,7 +163,7 @@ impl Miner { Some(mut block) => { let index = block.index; let mut context = context.lock().unwrap(); - block.signature = Bytes::from_bytes(&context.keystore.sign(&block.as_bytes())); + block.signature = Bytes::from_bytes(&job.keystore.sign(&block.as_bytes())); if context.chain.check_new_block(&block) != BlockQuality::Good { warn!("Error adding mined block!"); if index == 0 { @@ -184,6 +185,12 @@ impl Miner { } } +#[derive(Clone)] +pub struct MineJob { + block: Block, + keystore: Keystore +} + fn find_hash(context: Arc>, mut block: Block, running: Arc) -> Option { let difficulty = block.difficulty as usize; let full = block.transaction.is_some(); diff --git a/src/p2p/network.rs b/src/p2p/network.rs index 8312977..30b27e4 100644 --- a/src/p2p/network.rs +++ b/src/p2p/network.rs @@ -18,6 +18,7 @@ use std::collections::HashSet; use crate::{Context, Block, p2p::Message, p2p::State, p2p::Peer, p2p::Peers, Bytes}; use crate::blockchain::enums::BlockQuality; use crate::commons::CHAIN_VERSION; +use std::sync::atomic::{AtomicBool, Ordering}; const SERVER: Token = Token(0); const POLL_TIMEOUT: Option = Some(Duration::from_millis(3000)); @@ -40,6 +41,9 @@ impl Network { (c.settings.listen.clone(), c.settings.peers.clone()) }; + let running = Arc::new(AtomicBool::new(true)); + subscribe_to_bus(&mut self.context, Arc::clone(&running)); + // Starting server socket let addr = listen_addr.parse().expect("Error parsing listen address"); let mut server = TcpListener::bind(addr).expect("Can't bind to address"); @@ -62,6 +66,9 @@ impl Network { loop { // Poll Mio for events, blocking until we get an event. poll.poll(&mut events, POLL_TIMEOUT).expect("Error polling sockets"); + if !running.load(Ordering::SeqCst) { + break; + } // Process each event. for event in events.iter() { @@ -125,11 +132,26 @@ impl Network { peers.send_pings(poll.registry(), height, hash); peers.connect_new_peers(poll.registry(), &mut unique_token); } + info!("Network loop finished"); }); Ok(()) } } +fn subscribe_to_bus(context: &mut Arc>, running: Arc) { + use crate::event::Event; + context.lock().unwrap().bus.register(move |_uuid, e| { + match e { + Event::ActionQuit => { + running.store(false, Ordering::SeqCst); + return false; + } + _ => {} + } + true + }); +} + fn handle_connection_event(context: Arc>, peers: &mut Peers, registry: &Registry, event: &Event) -> bool { if event.is_error() || (event.is_read_closed() && event.is_write_closed()) { return false; @@ -415,16 +437,19 @@ fn handle_message(context: Arc>, message: Message, peers: &mut Pe fn mine_locker_block(context: Arc>) { let mut context = context.lock().unwrap(); if let Some(block) = context.chain.last_block() { - if block.index < context.chain.max_height() { - info!("No locker mining while syncing"); - return; - } - let lockers: HashSet = context.chain.get_block_lockers(&block).into_iter().collect(); - if lockers.contains(&context.keystore.get_public()) { - info!("We have an honor to mine locker block!"); - context.bus.post(crate::event::Event::ActionMineLocker { index: block.index + 1, hash: block.hash }); - } else if !lockers.is_empty() { - info!("Locker block must be mined by other nodes"); + if let Some(keystore) = &context.keystore { + if block.index < context.chain.max_height() { + info!("No locker mining while syncing"); + return; + } + let lockers: HashSet = context.chain.get_block_lockers(&block).into_iter().collect(); + if lockers.contains(&keystore.get_public()) { + info!("We have an honor to mine locker block!"); + let keystore = Box::new(keystore.clone()); + context.bus.post(crate::event::Event::ActionMineLocker { index: block.index + 1, hash: block.hash, keystore }); + } else if !lockers.is_empty() { + info!("Locker block must be mined by other nodes"); + } } } } diff --git a/src/web_ui.rs b/src/web_ui.rs index 2a122c5..694bf2f 100644 --- a/src/web_ui.rs +++ b/src/web_ui.rs @@ -61,12 +61,13 @@ pub fn run_interface(context: Arc>, miner: Arc>) { .build() .expect("Error building GUI"); - run_interface_loop(&mut interface); + let mut context = Arc::clone(&context); + run_interface_loop(&mut context, &mut interface); interface.exit(); } /// Indefinitely loops through WebView steps -fn run_interface_loop(interface: &mut WebView<()>) { +fn run_interface_loop(context: &mut Arc>, interface: &mut WebView<()>) { // We use this ugly loop to lower CPU usage a lot. // If we use .run() or only .step() in a loop without sleeps it will try // to support 60FPS and uses more CPU than it should. @@ -76,6 +77,8 @@ fn run_interface_loop(interface: &mut WebView<()>) { match interface.step() { None => { info!("Interface closed, exiting"); + context.lock().unwrap().bus.post(Event::ActionQuit); + thread::sleep(Duration::from_millis(100)); break; } Some(result) => { @@ -101,8 +104,10 @@ fn action_check_zone(context: &Arc>, web_view: &mut WebView<()>, web_view.eval("zoneAvailable(false)").expect("Error evaluating!"); } else { let c = context.lock().unwrap(); - let available = c.get_chain().is_domain_available(&name, &c.get_keystore()); - web_view.eval(&format!("zoneAvailable({})", available)).expect("Error evaluating!"); + if let Some(keystore) = c.get_keystore() { + let available = c.get_chain().is_domain_available(&name, &keystore); + web_view.eval(&format!("zoneAvailable({})", available)).expect("Error evaluating!"); + } } } @@ -114,24 +119,31 @@ fn action_check_record(web_view: &mut WebView<()>, data: String) { } fn action_check_domain(context: &Arc>, web_view: &mut WebView<()>, name: String) { - let name = name.to_lowercase(); let c = context.lock().unwrap(); - let available = c.get_chain().is_domain_available(&name, &c.get_keystore()); - web_view.eval(&format!("domainAvailable({})", available)).expect("Error evaluating!"); + if let Some(keystore) = c.get_keystore() { + let name = name.to_lowercase(); + let available = c.get_chain().is_domain_available(&name, &keystore); + web_view.eval(&format!("domainAvailable({})", available)).expect("Error evaluating!"); + } } fn action_save_key(context: &Arc>) { + if context.lock().unwrap().get_keystore().is_none() { + return; + } let result = tfd::save_file_dialog_with_filter("Save keys file", "", &["*.key"], "Key files (*.key)"); match result { None => {} Some(new_path) => { let mut context = context.lock().unwrap(); let path = new_path.clone(); - let public = context.keystore.get_public().to_string(); - let hash = context.keystore.get_hash().to_string(); - context.keystore.save(&new_path, ""); - info!("Key file saved to {}", &path); - context.bus.post(Event::KeySaved { path, public, hash }); + if let Some(mut keystore) = context.get_keystore() { + let public = keystore.get_public().to_string(); + let hash = keystore.get_hash().to_string(); + keystore.save(&new_path, ""); + info!("Key file saved to {}", &path); + context.bus.post(Event::KeySaved { path, public, hash }); + } } } } @@ -152,7 +164,7 @@ fn action_load_key(context: &Arc>, _web_view: &mut WebView<()>) { let public = keystore.get_public().to_string(); let hash = keystore.get_hash().to_string(); c.bus.post(Event::KeyLoaded { path, public, hash }); - c.set_keystore(keystore); + c.set_keystore(Some(keystore)); } } } @@ -213,23 +225,32 @@ fn action_loaded(context: &Arc>, web_view: &mut WebView<()>) { }; if !eval.is_empty() { - //debug!("Evaluating {}", &eval); handle.dispatch(move |web_view| { web_view.eval(&eval.replace("\\", "\\\\")) }).expect("Error dispatching!"); } true }); - let eval = format!("keystoreChanged('{}', '{}', '{}');", c.keystore.get_path(), &c.keystore.get_public().to_string(), &c.keystore.get_hash().to_string()); - debug!("Evaluating {}", &eval); - web_view.eval(&eval.replace("\\", "\\\\")).expect("Error evaluating!"); + if let Some(keystore) = c.get_keystore() { + let eval = format!("keystoreChanged('{}', '{}', '{}');", + keystore.get_path(), + &keystore.get_public().to_string(), + &keystore.get_hash().to_string()); + //debug!("Evaluating {}", &eval); + web_view.eval(&eval.replace("\\", "\\\\")).expect("Error evaluating!"); + } } fn action_create_domain(context: Arc>, miner: Arc>, web_view: &mut WebView<()>, name: String, records: &String) { debug!("Creating domain with records: {}", records); let c = Arc::clone(&context); let context = context.lock().unwrap(); - let pub_key = context.keystore.get_public(); + if context.get_keystore().is_none() { + show_warning(web_view, "You don't have keys loaded!\nLoad or mine the keys and try again."); + return; + } + let keystore = context.get_keystore().unwrap(); + let pub_key = keystore.get_public(); match context.chain.can_mine_domain(&name, &records, &pub_key) { MineResult::Fine => { let zone = get_domain_zone(&name); @@ -237,7 +258,6 @@ fn action_create_domain(context: Arc>, miner: Arc>, if let Ok(records) = serde_json::from_str::>(&records) { let data = DomainData::new(zone.clone(), records); let data = serde_json::to_string(&data).unwrap(); - let keystore = context.keystore.clone(); std::mem::drop(context); create_domain(c, miner, &name, &data, difficulty, &keystore); let _ = web_view.eval("domainMiningStarted()"); @@ -271,18 +291,23 @@ fn action_create_zone(context: Arc>, miner: Arc>, we let context = context.lock().unwrap(); (context.get_keystore(), context.chain.get_domain_transaction(&name)) }; - match transaction { - None => { - create_domain(Arc::clone(&context), miner.clone(), &name, &data, ZONE_DIFFICULTY, &keystore); - } - Some(transaction) => { - if transaction.pub_key == keystore.get_public() { + if let Some(keystore) = keystore { + match transaction { + None => { create_domain(Arc::clone(&context), miner.clone(), &name, &data, ZONE_DIFFICULTY, &keystore); - } else { - warn!("Tried to mine not owned domain!"); - show_warning(web_view, "You cannot change domain that you don't own!"); + } + Some(transaction) => { + if transaction.pub_key == keystore.get_public() { + create_domain(Arc::clone(&context), miner.clone(), &name, &data, ZONE_DIFFICULTY, &keystore); + } else { + warn!("Tried to mine not owned domain!"); + show_warning(web_view, "You cannot change domain that you don't own!"); + } } } + } else { + warn!("Can not mine without keys!"); + show_warning(web_view, "You don't have keys loaded!\nLoad or mine the keys and try again."); } } @@ -303,7 +328,7 @@ fn create_domain(context: Arc>, miner: Arc>, name: & //let tags_vector: Vec = tags.into().trim().split(",").map(|s| s.trim()).map(String::from).collect(); let transaction = Transaction::from_str(name, "dns".to_owned(), data.to_owned(), keystore.get_public().clone()); let block = Block::new(Some(transaction), keystore.get_public(), Bytes::default(), difficulty); - miner.lock().unwrap().add_block(block); + miner.lock().unwrap().add_block(block, keystore.clone()); } #[derive(Deserialize)] diff --git a/src/webview/index.html b/src/webview/index.html index 125b06f..d1b1a70 100644 --- a/src/webview/index.html +++ b/src/webview/index.html @@ -154,7 +154,7 @@ @@ -198,7 +198,7 @@
- +
@@ -210,10 +210,10 @@
- +
- +
@@ -225,7 +225,7 @@
- +
@@ -233,7 +233,7 @@
- +
@@ -241,7 +241,7 @@
- +
diff --git a/src/webview/scripts.js b/src/webview/scripts.js index afb40ad..507e549 100644 --- a/src/webview/scripts.js +++ b/src/webview/scripts.js @@ -189,12 +189,15 @@ function onDomainChange(element) { function domainAvailable(available) { input = document.getElementById("new_domain"); button = document.getElementById("new_domain_button"); + button2 = document.getElementById("add_record_button"); if (available) { input.className = "input"; button.disabled = false + button2.disabled = false } else { input.className = "input is-danger"; button.disabled = true + button2.disabled = true } } @@ -309,4 +312,15 @@ function keystoreChanged(path, pub_key, hash) { key_public_key.innerHTML = pub_key; var key_public_hash = document.getElementById("key_public_hash"); key_public_hash.innerHTML = hash; + var save_key = document.getElementById("save_key"); + save_key.disabled = false; + + var new_domain = document.getElementById("new_domain"); + new_domain.disabled = false; + var new_domain_tags = document.getElementById("new_domain_tags"); + new_domain_tags.disabled = false; + var new_zone = document.getElementById("new_zone"); + new_zone.disabled = false; + var new_zone_difficulty = document.getElementById("new_zone_difficulty"); + new_zone_difficulty.disabled = false; } \ No newline at end of file