2021-01-30 14:18:37 +01:00
|
|
|
use std::sync::{Arc, Condvar, Mutex};
|
2021-03-21 14:34:32 +01:00
|
|
|
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
|
2020-04-18 21:31:40 +02:00
|
|
|
use std::thread;
|
2021-01-14 18:34:43 +01:00
|
|
|
use std::time::Duration;
|
2021-01-30 14:18:37 +01:00
|
|
|
|
|
|
|
|
use chrono::Utc;
|
2021-02-21 21:56:56 +01:00
|
|
|
#[allow(unused_imports)]
|
2021-03-02 18:11:17 +01:00
|
|
|
use log::{debug, error, info, trace, warn};
|
|
|
|
|
use num_cpus;
|
2020-04-18 21:31:40 +02:00
|
|
|
|
2021-03-21 14:34:32 +01:00
|
|
|
use crate::{Block, Bytes, Context};
|
2021-03-21 01:31:33 +01:00
|
|
|
use crate::commons::{CHAIN_VERSION, LOCKER_DIFFICULTY, KEYSTORE_DIFFICULTY};
|
2021-03-10 22:21:50 +01:00
|
|
|
use crate::blockchain::enums::BlockQuality;
|
|
|
|
|
use crate::blockchain::hash_utils::*;
|
2021-03-12 01:36:54 +01:00
|
|
|
use crate::keys::check_public_key_strength;
|
2021-01-30 14:18:37 +01:00
|
|
|
use crate::event::Event;
|
2021-03-22 19:20:51 +01:00
|
|
|
use blakeout::Blakeout;
|
2021-01-30 14:18:37 +01:00
|
|
|
|
2020-04-18 21:31:40 +02:00
|
|
|
pub struct Miner {
|
|
|
|
|
context: Arc<Mutex<Context>>,
|
2021-03-02 18:11:17 +01:00
|
|
|
blocks: Arc<Mutex<Vec<Block>>>,
|
2020-04-18 21:31:40 +02:00
|
|
|
running: Arc<AtomicBool>,
|
2021-01-14 18:34:43 +01:00
|
|
|
mining: Arc<AtomicBool>,
|
|
|
|
|
cond_var: Arc<Condvar>
|
2020-04-18 21:31:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Miner {
|
|
|
|
|
pub fn new(context: Arc<Mutex<Context>>) -> Self {
|
|
|
|
|
Miner {
|
|
|
|
|
context: context.clone(),
|
2021-03-02 18:11:17 +01:00
|
|
|
blocks: Arc::new(Mutex::new(Vec::new())),
|
2020-04-18 21:31:40 +02:00
|
|
|
running: Arc::new(AtomicBool::new(false)),
|
2021-01-14 18:34:43 +01:00
|
|
|
mining: Arc::new(AtomicBool::new(false)),
|
|
|
|
|
cond_var: Arc::new(Condvar::new())
|
2020-04-18 21:31:40 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-02 18:11:17 +01:00
|
|
|
pub fn add_block(&mut self, block: Block) {
|
|
|
|
|
self.blocks.lock().unwrap().push(block);
|
2021-01-14 18:34:43 +01:00
|
|
|
self.cond_var.notify_one();
|
2020-04-18 21:31:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn stop(&mut self) {
|
2021-02-28 22:17:03 +01:00
|
|
|
self.mining.store(false, Ordering::SeqCst);
|
|
|
|
|
self.running.store(false, Ordering::SeqCst);
|
2021-01-14 18:34:43 +01:00
|
|
|
self.cond_var.notify_all();
|
2020-04-18 21:31:40 +02:00
|
|
|
}
|
|
|
|
|
|
2021-01-14 18:34:43 +01:00
|
|
|
pub fn start_mining_thread(&mut self) {
|
|
|
|
|
let context = self.context.clone();
|
2021-03-02 18:11:17 +01:00
|
|
|
let blocks = self.blocks.clone();
|
2021-01-14 18:34:43 +01:00
|
|
|
let running = self.running.clone();
|
|
|
|
|
let mining = self.mining.clone();
|
|
|
|
|
let cond_var = self.cond_var.clone();
|
|
|
|
|
thread::spawn(move || {
|
2021-03-06 21:28:06 +01:00
|
|
|
running.store(true, Ordering::SeqCst);
|
|
|
|
|
while running.load(Ordering::SeqCst) {
|
2021-01-14 18:34:43 +01:00
|
|
|
// If some transaction is being mined now, we yield
|
2021-03-06 21:28:06 +01:00
|
|
|
if mining.load(Ordering::SeqCst) {
|
|
|
|
|
thread::sleep(Duration::from_millis(1000));
|
2021-01-14 18:34:43 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-02 18:11:17 +01:00
|
|
|
let mut lock = blocks.lock().unwrap();
|
2021-01-14 18:34:43 +01:00
|
|
|
if lock.len() > 0 {
|
2021-03-02 18:11:17 +01:00
|
|
|
info!("Got new block to mine");
|
|
|
|
|
let block = lock.remove(0);
|
2021-02-28 22:17:03 +01:00
|
|
|
mining.store(true, Ordering::SeqCst);
|
2021-03-02 18:11:17 +01:00
|
|
|
Miner::mine_internal(context.clone(), block, mining.clone());
|
2021-01-14 18:34:43 +01:00
|
|
|
} else {
|
2021-02-14 18:20:30 +01:00
|
|
|
let _ = cond_var.wait(lock).expect("Error in wait lock!");
|
2021-01-14 18:34:43 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
2021-01-30 14:18:37 +01:00
|
|
|
let mining = self.mining.clone();
|
2021-03-06 21:28:06 +01:00
|
|
|
let blocks = self.blocks.clone();
|
|
|
|
|
let cond_var = self.cond_var.clone();
|
2021-02-14 18:20:30 +01:00
|
|
|
self.context.lock().unwrap().bus.register(move |_uuid, e| {
|
2021-03-06 21:28:06 +01:00
|
|
|
match e {
|
|
|
|
|
Event::NewBlockReceived => {}
|
2021-03-16 18:16:31 +01:00
|
|
|
Event::BlockchainChanged {..} => {}
|
2021-03-06 21:28:06 +01:00
|
|
|
Event::ActionStopMining => {
|
|
|
|
|
mining.store(false, Ordering::SeqCst);
|
|
|
|
|
}
|
|
|
|
|
Event::ActionMineLocker { index, hash } => {
|
|
|
|
|
if !mining.load(Ordering::SeqCst) {
|
2021-03-17 14:55:05 +01:00
|
|
|
let mut block = Block::new(None, Bytes::default(), hash, LOCKER_DIFFICULTY);
|
2021-03-06 21:28:06 +01:00
|
|
|
block.index = index;
|
|
|
|
|
blocks.lock().unwrap().push(block);
|
|
|
|
|
cond_var.notify_all();
|
|
|
|
|
info!("Added a locker block to mine");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
_ => {}
|
2021-01-30 14:18:37 +01:00
|
|
|
}
|
2021-02-17 13:06:24 +01:00
|
|
|
true
|
2021-01-30 14:18:37 +01:00
|
|
|
});
|
2020-04-18 21:31:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn is_mining(&self) -> bool {
|
|
|
|
|
self.running.load(Ordering::Relaxed)
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-02 18:11:17 +01:00
|
|
|
fn mine_internal(context: Arc<Mutex<Context>>, mut block: Block, mining: Arc<AtomicBool>) {
|
|
|
|
|
// Clear signature and hash just in case
|
|
|
|
|
block.signature = Bytes::default();
|
|
|
|
|
block.hash = Bytes::default();
|
|
|
|
|
block.version = CHAIN_VERSION;
|
2021-03-06 21:28:06 +01:00
|
|
|
// If this block needs to be a locker
|
|
|
|
|
if block.index > 0 && !block.prev_block_hash.is_empty() {
|
|
|
|
|
info!("Mining locker block");
|
|
|
|
|
block.pub_key = context.lock().unwrap().keystore.get_public();
|
2021-03-12 01:36:54 +01:00
|
|
|
if !check_public_key_strength(&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);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-03-10 22:21:50 +01:00
|
|
|
match context.lock().unwrap().chain.last_block() {
|
2021-03-06 21:28:06 +01:00
|
|
|
None => {}
|
|
|
|
|
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 {
|
|
|
|
|
warn!("We missed block to lock");
|
|
|
|
|
context.lock().unwrap().bus.post(Event::MinerStopped);
|
|
|
|
|
mining.store(false, Ordering::SeqCst);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2021-03-10 22:21:50 +01:00
|
|
|
block.index = context.lock().unwrap().chain.height() + 1;
|
|
|
|
|
block.prev_block_hash = match context.lock().unwrap().chain.last_block() {
|
2021-03-06 21:28:06 +01:00
|
|
|
None => { Bytes::default() }
|
|
|
|
|
Some(block) => { block.hash }
|
|
|
|
|
};
|
|
|
|
|
}
|
2021-01-14 18:34:43 +01:00
|
|
|
|
2021-03-02 18:11:17 +01:00
|
|
|
context.lock().unwrap().bus.post(Event::MinerStarted);
|
2021-03-16 18:16:31 +01:00
|
|
|
let thread_spawn_interval = Duration::from_millis(10);
|
2021-01-14 18:34:43 +01:00
|
|
|
let live_threads = Arc::new(AtomicU32::new(0u32));
|
|
|
|
|
let cpus = num_cpus::get();
|
2021-02-20 16:28:10 +01:00
|
|
|
debug!("Starting {} threads for mining", cpus);
|
2021-03-21 14:34:32 +01:00
|
|
|
for _cpu in 0..cpus {
|
2021-03-16 18:16:31 +01:00
|
|
|
let context = Arc::clone(&context);
|
2021-01-14 18:34:43 +01:00
|
|
|
let block = block.clone();
|
2021-03-16 18:16:31 +01:00
|
|
|
let mining = Arc::clone(&mining);
|
|
|
|
|
let live_threads = Arc::clone(&live_threads);
|
2021-01-14 18:34:43 +01:00
|
|
|
thread::spawn(move || {
|
2021-02-28 22:17:03 +01:00
|
|
|
live_threads.fetch_add(1, Ordering::SeqCst);
|
2021-03-22 19:20:51 +01:00
|
|
|
match find_hash(Arc::clone(&context), block, Arc::clone(&mining)) {
|
2021-01-14 18:34:43 +01:00
|
|
|
None => {
|
2021-02-28 22:17:03 +01:00
|
|
|
debug!("Mining was cancelled");
|
|
|
|
|
let count = live_threads.fetch_sub(1, Ordering::SeqCst);
|
2021-01-14 18:34:43 +01:00
|
|
|
// If this is the last thread, but mining was not stopped by another thread
|
2021-02-28 22:17:03 +01:00
|
|
|
if count == 1 {
|
|
|
|
|
let mut context = context.lock().unwrap();
|
|
|
|
|
context.bus.post(Event::MinerStopped);
|
2021-01-14 18:34:43 +01:00
|
|
|
}
|
|
|
|
|
},
|
2021-03-02 18:11:17 +01:00
|
|
|
Some(mut block) => {
|
2021-02-17 12:35:26 +01:00
|
|
|
let index = block.index;
|
2021-01-30 14:18:37 +01:00
|
|
|
let mut context = context.lock().unwrap();
|
2021-03-02 18:11:17 +01:00
|
|
|
block.signature = Bytes::from_bytes(&context.keystore.sign(&block.as_bytes()));
|
2021-03-10 22:21:50 +01:00
|
|
|
if context.chain.check_new_block(&block) != BlockQuality::Good {
|
2021-02-20 16:28:10 +01:00
|
|
|
warn!("Error adding mined block!");
|
2021-02-17 12:35:26 +01:00
|
|
|
if index == 0 {
|
2021-02-20 16:28:10 +01:00
|
|
|
error!("To mine genesis block you need to make 'origin' an empty string in config.");
|
2021-02-17 12:35:26 +01:00
|
|
|
}
|
2021-03-02 18:11:17 +01:00
|
|
|
} else {
|
2021-03-16 21:59:51 +01:00
|
|
|
if block.index == 1 {
|
|
|
|
|
context.settings.origin = block.hash.to_string();
|
|
|
|
|
}
|
2021-03-10 22:21:50 +01:00
|
|
|
context.chain.add_block(block);
|
2021-02-17 12:35:26 +01:00
|
|
|
}
|
2021-01-30 14:18:37 +01:00
|
|
|
context.bus.post(Event::MinerStopped);
|
2021-02-28 22:17:03 +01:00
|
|
|
mining.store(false, Ordering::SeqCst);
|
2021-01-14 18:34:43 +01:00
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
});
|
2021-03-16 18:16:31 +01:00
|
|
|
thread::sleep(thread_spawn_interval);
|
2021-01-14 18:34:43 +01:00
|
|
|
}
|
|
|
|
|
}
|
2020-04-18 21:31:40 +02:00
|
|
|
}
|
|
|
|
|
|
2021-03-22 19:20:51 +01:00
|
|
|
fn find_hash(context: Arc<Mutex<Context>>, mut block: Block, running: Arc<AtomicBool>) -> Option<Block> {
|
2021-03-02 18:11:17 +01:00
|
|
|
let difficulty = block.difficulty as usize;
|
2021-03-19 14:25:11 +01:00
|
|
|
let full = block.transaction.is_some();
|
2021-03-22 19:20:51 +01:00
|
|
|
let mut digest = Blakeout::new();
|
2021-02-28 22:17:03 +01:00
|
|
|
loop {
|
|
|
|
|
block.random = rand::random();
|
2021-03-21 14:34:32 +01:00
|
|
|
let next_allowed_block = {
|
|
|
|
|
let context = context.lock().unwrap();
|
|
|
|
|
// We use this block to fill some fields of our block as well
|
|
|
|
|
block.index = context.chain.height() + 1;
|
|
|
|
|
if let Some(b) = context.chain.last_block() {
|
|
|
|
|
block.prev_block_hash = b.hash;
|
|
|
|
|
}
|
|
|
|
|
context.chain.next_allowed_block()
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if full && next_allowed_block > block.index {
|
2021-03-17 10:22:34 +01:00
|
|
|
// We can't mine now, as we need to wait for block to be signed
|
|
|
|
|
thread::sleep(Duration::from_millis(1000));
|
|
|
|
|
continue;
|
2021-03-16 18:16:31 +01:00
|
|
|
}
|
2021-02-28 22:17:03 +01:00
|
|
|
debug!("Mining block {}", serde_json::to_string(&block).unwrap());
|
|
|
|
|
for nonce in 0..std::u64::MAX {
|
|
|
|
|
if !running.load(Ordering::Relaxed) {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
block.timestamp = Utc::now().timestamp();
|
|
|
|
|
block.nonce = nonce;
|
2020-04-18 21:31:40 +02:00
|
|
|
|
2021-02-28 22:17:03 +01:00
|
|
|
digest.reset();
|
2021-03-22 19:20:51 +01:00
|
|
|
digest.update(&block.as_bytes());
|
|
|
|
|
if hash_is_good(digest.result(), difficulty) {
|
|
|
|
|
block.hash = Bytes::from_bytes(digest.result());
|
2021-02-28 22:17:03 +01:00
|
|
|
return Some(block);
|
|
|
|
|
}
|
2021-03-16 18:16:31 +01:00
|
|
|
|
2021-03-21 14:34:32 +01:00
|
|
|
if nonce % 1000 == 0 {
|
|
|
|
|
if let Ok(context) = context.lock() {
|
|
|
|
|
if context.chain.height() >= block.index {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-16 18:16:31 +01:00
|
|
|
}
|
2020-04-18 21:31:40 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|