From 5c1c0630a6428bad9842d5333b9deb30747ad4fb Mon Sep 17 00:00:00 2001 From: Revertron Date: Tue, 20 Apr 2021 20:54:45 +0200 Subject: [PATCH] Improved signing blocks operations. --- .github/workflows/rust_build_and_test.yml | 2 +- src/blockchain/chain.rs | 30 +++++++------- src/context.rs | 12 +++++- src/main.rs | 2 +- src/miner.rs | 48 ++++++++++++++--------- src/p2p/network.rs | 30 ++++++++------ src/web_ui.rs | 2 +- 7 files changed, 77 insertions(+), 49 deletions(-) diff --git a/.github/workflows/rust_build_and_test.yml b/.github/workflows/rust_build_and_test.yml index 2280e1f..db8626b 100644 --- a/.github/workflows/rust_build_and_test.yml +++ b/.github/workflows/rust_build_and_test.yml @@ -27,4 +27,4 @@ jobs: - name: Build run: cargo build --verbose - name: Run tests - run: cargo test --all --verbose + run: cargo test --all --verbose diff --git a/src/blockchain/chain.rs b/src/blockchain/chain.rs index 29d35ac..51fcc66 100644 --- a/src/blockchain/chain.rs +++ b/src/blockchain/chain.rs @@ -93,7 +93,7 @@ impl Chain { } fn check_chain(&mut self) { - let height = self.height(); + let height = self.get_height(); info!("Local blockchain height is {}, starting full blockchain check...", height); for id in 1..=height { info!("Checking block {}", id); @@ -251,7 +251,7 @@ impl Chain { } pub fn update(&mut self, keystore: &Option) -> Option { - if self.height() < BLOCK_SIGNERS_START { + if self.get_height() < BLOCK_SIGNERS_START { trace!("Too early to start block signings"); return None; } @@ -259,7 +259,7 @@ impl Chain { trace!("We can't sign blocks without keys"); return None; } - if self.height() < self.max_height() { + if self.get_height() < self.max_height() { trace!("No signing while syncing"); return None; } @@ -269,7 +269,7 @@ impl Chain { Some(ref block) => { block.clone() } }; // TODO maybe make some config option to mine signing blocks above? - let sign_count = self.height() - block.index; + let sign_count = self.get_height() - block.index; if sign_count >= BLOCK_SIGNERS_MIN { trace!("Block {} has enough signing blocks", block.index); return None; @@ -283,7 +283,7 @@ impl Chain { let keystore = keystore.clone().unwrap().clone(); let signers: HashSet = self.get_block_signers(&block).into_iter().collect(); if signers.contains(&keystore.get_public()) { - for index in block.index..=self.height() { + for index in block.index..=self.get_height() { let b = self.get_block(index).unwrap(); if b.pub_key == keystore.get_public() { info!("We already mined signing block for block {}", block.index); @@ -304,7 +304,7 @@ impl Chain { pub fn update_sign_block_for_mining(&self, mut block: Block) -> Option { if let Some(full_block) = &self.last_full_block { - let sign_count = self.height() - full_block.index; + let sign_count = self.get_height() - full_block.index; if sign_count >= BLOCK_SIGNERS_MIN { return None; } @@ -319,7 +319,7 @@ impl Chain { pub fn is_waiting_signers(&self) -> bool { if let Some(full_block) = &self.last_full_block { - let sign_count = self.height() - full_block.index; + let sign_count = self.get_height() - full_block.index; if sign_count < BLOCK_SIGNERS_MIN { return true; } @@ -668,7 +668,7 @@ impl Chain { self.last_block.clone() } - pub fn height(&self) -> u64 { + pub fn get_height(&self) -> u64 { match self.last_block { None => { 0u64 } Some(ref block) => { @@ -686,12 +686,12 @@ impl Chain { pub fn next_allowed_full_block(&self) -> u64 { match self.last_full_block { - None => { self.height() + 1 } + None => { self.get_height() + 1 } Some(ref block) => { if block.index < BLOCK_SIGNERS_START { - self.height() + 1 + self.get_height() + 1 } else { - max(block.index + BLOCK_SIGNERS_MIN, self.height() + 1) + max(block.index + BLOCK_SIGNERS_MIN, self.get_height() + 1) } } } @@ -716,7 +716,7 @@ impl Chain { warn!("Ignoring block from the future:\n{:?}", &block); return Bad; } - if block.index > self.height() + 1 { + if block.index > self.get_height() + 1 { info!("Ignoring future block:\n{:?}", &block); return Future; } @@ -857,10 +857,10 @@ impl Chain { /// Checks if this block is a good signature block fn is_good_sign_block(&self, block: &Block) -> bool { if let Some(full_block) = &self.last_full_block { - let sign_count = self.height() - full_block.index; + let sign_count = self.get_height() - full_block.index; if sign_count < BLOCK_SIGNERS_MIN { // Last full block is not locked enough - if block.transaction.is_some() { + if block.index > full_block.index && block.transaction.is_some() { warn!("Not enough signing blocks over full {} block!", full_block.index); return false; } else { @@ -954,7 +954,7 @@ impl Chain { /// block - last full block pub fn get_block_signers(&self, block: &Block) -> Vec { let mut result = Vec::new(); - if block.index < BLOCK_SIGNERS_START || self.height() < block.index { + if block.index < BLOCK_SIGNERS_START || self.get_height() < block.index { return result; } let mut set = HashSet::new(); diff --git a/src/context.rs b/src/context.rs index 3a81a98..3ac1f63 100644 --- a/src/context.rs +++ b/src/context.rs @@ -2,6 +2,7 @@ use crate::{Chain, Bus, Keystore, Settings, ExternalZones}; use crate::event::Event; #[allow(unused_imports)] use log::{trace, debug, info, warn, error}; +use crate::miner::MinerState; pub struct Context { pub app_version: String, @@ -10,12 +11,21 @@ pub struct Context { pub chain: Chain, pub x_zones: ExternalZones, pub bus: Bus, + pub miner_state: MinerState, } impl Context { /// Creating an essential context to work with 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() } + Context { + app_version, + settings, + keystore, + chain, + x_zones: ExternalZones::new(), + bus: Bus::new(), + miner_state: MinerState { mining: false, full: false } + } } /// Load keystore and return Context diff --git a/src/main.rs b/src/main.rs index 1957c85..92f0b96 100644 --- a/src/main.rs +++ b/src/main.rs @@ -107,7 +107,7 @@ fn main() { let keystore = Keystore::from_file(&settings.key_file, ""); let chain: Chain = Chain::new(&settings); if opt_matches.opt_present("b") { - for i in 1..(chain.height() + 1) { + for i in 1..(chain.get_height() + 1) { if let Some(block) = chain.get_block(i) { info!(target: LOG_TARGET_MAIN, "{:?}", &block); } diff --git a/src/miner.rs b/src/miner.rs index 7f0b18f..22f3c7f 100644 --- a/src/miner.rs +++ b/src/miner.rs @@ -17,6 +17,19 @@ use crate::event::Event; use blakeout::Blakeout; use std::ops::Deref; +#[derive(Clone)] +pub struct MineJob { + start: i64, + block: Block, + keystore: Keystore +} + +#[derive(Clone, Debug)] +pub struct MinerState { + pub mining: bool, + pub full: bool +} + pub struct Miner { context: Arc>, jobs: Arc>>, @@ -142,24 +155,28 @@ impl Miner { } } } else { - job.block.index = context.lock().unwrap().chain.height() + 1; + job.block.index = context.lock().unwrap().chain.get_height() + 1; job.block.prev_block_hash = match context.lock().unwrap().chain.last_block() { None => { Bytes::default() } Some(block) => { block.hash } }; } - context.lock().unwrap().bus.post(Event::MinerStarted); - let thread_spawn_interval = Duration::from_millis(10); - let live_threads = Arc::new(AtomicU32::new(0u32)); - let lower = context.lock().unwrap().settings.mining.lower; + let (lower, threads) = { + let mut context = context.lock().unwrap(); + context.bus.post(Event::MinerStarted); + context.miner_state.mining = true; + context.miner_state.full = job.block.transaction.is_some(); + (context.settings.mining.lower, context.settings.mining.threads) + }; let cpus = num_cpus::get(); - let threads = context.lock().unwrap().settings.mining.threads; let threads = match threads { 0 => cpus, _ => threads }; debug!("Starting {} threads for mining", threads); + let thread_spawn_interval = Duration::from_millis(100); + let live_threads = Arc::new(AtomicU32::new(0u32)); for cpu in 0..threads { let context = Arc::clone(&context); let job = job.clone(); @@ -178,6 +195,7 @@ impl Miner { // If this is the last thread, but mining was not stopped by another thread if count == 1 { let mut context = context.lock().unwrap(); + context.miner_state.mining = false; context.bus.post(Event::MinerStopped { success: false, full }); } }, @@ -198,6 +216,7 @@ impl Miner { context.chain.add_block(block); success = true; } + context.miner_state.mining = false; context.bus.post(Event::MinerStopped { success, full }); mining.store(false, Ordering::SeqCst); }, @@ -208,13 +227,6 @@ impl Miner { } } -#[derive(Clone)] -pub struct MineJob { - start: i64, - block: Block, - keystore: Keystore -} - fn find_hash(context: Arc>, mut block: Block, running: Arc, thread: usize) -> Option { let difficulty = block.difficulty; let full = block.transaction.is_some(); @@ -226,7 +238,7 @@ fn find_hash(context: Arc>, mut block: Block, running: Arc>, mut block: Block, running: Arc>, mut block: Block, running: Arc 5000 { let speed = (nonce - prev_nonce) / (elapsed as u64 / 1000); //debug!("Mining speed {} H/s, max difficulty {}", speed, max_diff); - if let Ok(mut context) = context.lock() { + if let Ok(mut context) = context.try_lock() { context.bus.post(Event::MinerStats { thread, speed, max_diff, aim_diff: difficulty }) } time = Instant::now(); @@ -276,8 +288,8 @@ fn find_hash(context: Arc>, mut block: Block, running: Arc 1 { - if let Ok(context) = context.lock() { - if context.chain.height() >= block.index { + if let Ok(context) = context.try_lock() { + if context.chain.get_height() >= block.index { if !full { info!("Blockchain changed while mining signing block, dropping work"); return None; diff --git a/src/p2p/network.rs b/src/p2p/network.rs index e077d43..f718ce4 100644 --- a/src/p2p/network.rs +++ b/src/p2p/network.rs @@ -66,6 +66,8 @@ impl Network { let mut log_timer = Instant::now(); let mut bootstrap_timer = Instant::now(); let mut last_events_time = Instant::now(); + let mut sent_mining_event_index = 0u64; + let mut sent_mining_event_time = Instant::now(); loop { if peers.get_peers_count() == 0 && bootstrap_timer.elapsed().as_secs() > 60 { // Starting peer connections to bootstrap nodes @@ -136,7 +138,7 @@ impl Network { if !handle_connection_event(Arc::clone(&context), &mut peers, &poll.registry(), &event) { let _ = peers.close_peer(poll.registry(), &token); let mut context = context.lock().unwrap(); - let blocks_count = context.chain.height(); + let blocks_count = context.chain.get_height(); context.bus.post(crate::event::Event::NetworkStatus { nodes: peers.get_peers_active_count(), blocks: blocks_count }); } } @@ -151,7 +153,7 @@ impl Network { // Send pings to idle peers let (height, hash) = { let mut context = context.lock().unwrap(); - let height = context.chain.height(); + let height = context.chain.get_height(); let nodes = peers.get_peers_active_count(); let banned = peers.get_peers_banned_count(); if nodes > 0 { @@ -164,10 +166,14 @@ impl Network { warn!("Last network events time {} seconds ago", elapsed); } log_timer = Instant::now(); - let keystore = context.keystore.clone(); - if let Some(event) = context.chain.update(&keystore) { - context.bus.post(event); - trace!("Posted an event to mine signing block"); + if sent_mining_event_index < height || sent_mining_event_time.elapsed().as_secs() >= 600 { + let keystore = context.keystore.clone(); + if let Some(event) = context.chain.update(&keystore) { + context.bus.post(event); + trace!("Posted an event to mine signing block"); + sent_mining_event_index = height; + sent_mining_event_time = Instant::now(); + } } } (height, context.chain.last_hash()) @@ -300,7 +306,7 @@ fn handle_connection_event(context: Arc>, peers: &mut Peers, regi if from.elapsed().as_secs() >= 30 { let data: String = { let c = context.lock().unwrap(); - let message = Message::ping(c.chain.height(), c.chain.last_hash()); + let message = Message::ping(c.chain.get_height(), c.chain.last_hash()); serde_json::to_string(&message).unwrap() }; send_message(peer.get_stream(), &data.into_bytes()).unwrap_or_else(|e| warn!("Error sending ping {}", e)); @@ -376,7 +382,7 @@ fn handle_message(context: Arc>, message: Message, peers: &mut Pe let (my_height, my_hash, my_origin, my_version) = { let context = context.lock().unwrap(); // TODO cache it somewhere - (context.chain.height(), context.chain.last_hash(), &context.settings.origin.clone(), CHAIN_VERSION) + (context.chain.get_height(), context.chain.last_hash(), &context.settings.origin.clone(), CHAIN_VERSION) }; let answer = match message { Message::Hand { app_version, origin, version, public, rand} => { @@ -502,7 +508,7 @@ fn process_new_block(context: Arc>, peers: &mut Peers, token: &To match context.chain.check_new_block(&block) { BlockQuality::Good => { context.chain.add_block(block); - let my_height = context.chain.height(); + let my_height = context.chain.get_height(); context.bus.post(crate::event::Event::BlockchainChanged { index: my_height }); // If it was the last block to sync if my_height == max_height { @@ -517,7 +523,7 @@ fn process_new_block(context: Arc>, peers: &mut Peers, token: &To BlockQuality::Bad => { // TODO save bad public keys to banned table debug!("Ignoring bad block from {}:\n{:?}", peer.get_addr(), &block); - let height = context.chain.height(); + let height = context.chain.get_height(); context.chain.update_max_height(height); context.bus.post(crate::event::Event::SyncFinished); return State::Banned; @@ -527,10 +533,10 @@ fn process_new_block(context: Arc>, peers: &mut Peers, token: &To let last_block = context.chain.last_block().unwrap(); if block.is_better_than(&last_block) { context.chain.replace_block(block.index, block).expect("Error replacing block with fork"); - let index = context.chain.height(); + let index = context.chain.get_height(); context.bus.post(crate::event::Event::BlockchainChanged { index }); } - let height = context.chain.height(); + let height = context.chain.get_height(); context.chain.update_max_height(height); context.bus.post(crate::event::Event::SyncFinished); } diff --git a/src/web_ui.rs b/src/web_ui.rs index 9cebc7f..3e6f60f 100644 --- a/src/web_ui.rs +++ b/src/web_ui.rs @@ -317,7 +317,7 @@ fn action_loaded(context: &Arc>, web_view: &mut WebView<()>) { let hash = keystore.get_hash().to_string(); c.bus.post(Event::KeyLoaded { path, public, hash }); } - let index = c.chain.height(); + let index = c.chain.get_height(); c.bus.post(Event::BlockchainChanged { index }); event_info(web_view, "Application loaded"); }