Added blockchain full check on start. Fixed cond_var blocking.
This commit is contained in:
+93
-19
@@ -30,13 +30,16 @@ const SQL_REPLACE_BLOCK: &str = "UPDATE blocks SET timestamp = ?, version = ?, d
|
||||
prev_block_hash = ?, hash = ?, pub_key = ?, signature = ? WHERE id = ?;";
|
||||
const SQL_GET_LAST_BLOCK: &str = "SELECT * FROM blocks ORDER BY id DESC LIMIT 1;";
|
||||
const SQL_GET_FIRST_BLOCK_FOR_KEY: &str = "SELECT id FROM blocks WHERE pub_key = ? LIMIT 1;";
|
||||
const SQL_DELETE_BLOCK_AND_TRANSACTIONS: &str = "DELETE FROM blocks WHERE id = ?; DELETE FROM domains WHERE id = ?; DELETE FROM zones WHERE id = ?;";
|
||||
const SQL_TRUNCATE_DB_FROM_BLOCK: &str = "DELETE FROM blocks WHERE id >= ?; DELETE FROM domains WHERE id >= ?; DELETE FROM zones WHERE id >= ?;";
|
||||
|
||||
const SQL_ADD_DOMAIN: &str = "INSERT INTO domains (id, timestamp, identity, confirmation, data, pub_key) VALUES (?, ?, ?, ?, ?, ?)";
|
||||
const SQL_ADD_ZONE: &str = "INSERT INTO zones (id, timestamp, identity, confirmation, data, pub_key) VALUES (?, ?, ?, ?, ?, ?)";
|
||||
const SQL_DELETE_DOMAIN: &str = "DELETE FROM domains WHERE id = ?";
|
||||
const SQL_DELETE_ZONE: &str = "DELETE FROM zones WHERE id = ?";
|
||||
const SQL_GET_BLOCK_BY_ID: &str = "SELECT * FROM blocks WHERE id=? LIMIT 1;";
|
||||
const SQL_GET_LAST_FULL_BLOCK: &str = "SELECT * FROM blocks WHERE `transaction`<>'' ORDER BY id DESC LIMIT 1;";
|
||||
const SQL_GET_LAST_FULL_BLOCK_FOR_KEY: &str = "SELECT * FROM blocks WHERE `transaction`<>'' AND pub_key = ? ORDER BY id DESC LIMIT 1;";
|
||||
const SQL_GET_LAST_FULL_BLOCK: &str = "SELECT * FROM blocks WHERE id < ? AND `transaction`<>'' ORDER BY id DESC LIMIT 1;";
|
||||
const SQL_GET_LAST_FULL_BLOCK_FOR_KEY: &str = "SELECT * FROM blocks WHERE id < ? AND `transaction`<>'' AND pub_key = ? ORDER BY id DESC LIMIT 1;";
|
||||
const SQL_GET_DOMAIN_PUBLIC_KEY_BY_ID: &str = "SELECT pub_key FROM domains WHERE identity = ? ORDER BY id DESC LIMIT 1;";
|
||||
const SQL_GET_ZONE_PUBLIC_KEY_BY_ID: &str = "SELECT pub_key FROM zones WHERE identity = ? ORDER BY id DESC LIMIT 1;";
|
||||
const SQL_GET_DOMAIN_BY_ID: &str = "SELECT * FROM domains WHERE identity = ? ORDER BY id DESC LIMIT 1;";
|
||||
@@ -83,9 +86,68 @@ impl Chain {
|
||||
if block.transaction.is_some() {
|
||||
self.last_full_block = Some(block);
|
||||
} else {
|
||||
self.last_full_block = self.get_last_full_block(None);
|
||||
self.last_full_block = self.get_last_full_block(u64::MAX, None);
|
||||
}
|
||||
}
|
||||
self.check_chain();
|
||||
}
|
||||
|
||||
fn check_chain(&mut self) {
|
||||
let height = self.height();
|
||||
info!("Local blockchain height is {}, starting full blockchain check...", height);
|
||||
for id in 1..=height {
|
||||
info!("Checking block {}", id);
|
||||
match self.get_block(id) {
|
||||
None => {
|
||||
panic!("Blockchain is corrupted! Please, delete 'blockchain.db' and restart.");
|
||||
}
|
||||
Some(block) => {
|
||||
if block.index == 1 {
|
||||
if block.hash != self.origin {
|
||||
panic!("Loaded DB is not of origin {:?}! Please, delete 'blockchain.db' and restart.", &self.origin);
|
||||
}
|
||||
self.last_block = Some(block);
|
||||
continue;
|
||||
}
|
||||
|
||||
//let last = self.last_block.clone().unwrap();
|
||||
if self.check_new_block(&block) != BlockQuality::Good {
|
||||
error!("Block {} is bad:\n{:?}", block.index, &block);
|
||||
info!("Truncating database from block {}...", block.index);
|
||||
if self.truncate_db_from_block(block.index).is_err() {
|
||||
panic!("Error truncating database! Please, delete 'blockchain.db' and restart.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
info!("Block {} is good!", block.index);
|
||||
if block.transaction.is_some() {
|
||||
self.last_full_block = Some(block.clone());
|
||||
}
|
||||
self.last_block = Some(block);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.last_block = self.load_last_block();
|
||||
self.last_full_block = self.get_last_full_block(u64::MAX, None);
|
||||
info!("Last block after chain check: {:?}", &self.last_block);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn delete_block(&mut self, index: u64) -> sqlite::Result<State> {
|
||||
let mut statement = self.db.prepare(SQL_DELETE_BLOCK_AND_TRANSACTIONS)?;
|
||||
statement.bind(1, index as i64)?;
|
||||
statement.bind(2, index as i64)?;
|
||||
statement.bind(3, index as i64)?;
|
||||
statement.next()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn truncate_db_from_block(&mut self, index: u64) -> sqlite::Result<State> {
|
||||
let mut statement = self.db.prepare(SQL_TRUNCATE_DB_FROM_BLOCK)?;
|
||||
statement.bind(1, index as i64)?;
|
||||
statement.bind(2, index as i64)?;
|
||||
statement.bind(3, index as i64)?;
|
||||
statement.next()
|
||||
}
|
||||
|
||||
fn load_last_block(&mut self) -> Option<Block> {
|
||||
@@ -241,14 +303,21 @@ impl Chain {
|
||||
}
|
||||
|
||||
pub fn update_sign_block_for_mining(&self, mut block: Block) -> Option<Block> {
|
||||
info!("11");
|
||||
if let Some(full_block) = &self.last_full_block {
|
||||
info!("22");
|
||||
let sign_count = self.height() - full_block.index;
|
||||
if sign_count >= BLOCK_SIGNERS_MIN {
|
||||
info!("23");
|
||||
return None;
|
||||
}
|
||||
block.index = self.height() + 1;
|
||||
block.prev_block_hash = self.last_block.clone().unwrap().hash;
|
||||
return Some(block);
|
||||
info!("33");
|
||||
if let Some(last) = &self.last_block {
|
||||
block.index = last.index + 1;
|
||||
block.prev_block_hash = last.hash.clone();
|
||||
info!("44");
|
||||
return Some(block);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
@@ -363,7 +432,7 @@ impl Chain {
|
||||
}
|
||||
|
||||
/// Gets last block that has a Transaction within
|
||||
pub fn get_last_full_block(&self, pub_key: Option<&[u8]>) -> Option<Block> {
|
||||
pub fn get_last_full_block(&self, index: u64, pub_key: Option<&[u8]>) -> Option<Block> {
|
||||
if let Some(block) = &self.last_full_block {
|
||||
match pub_key {
|
||||
None => { return Some(block.clone()); }
|
||||
@@ -377,11 +446,14 @@ impl Chain {
|
||||
|
||||
let mut statement = match pub_key {
|
||||
None => {
|
||||
self.db.prepare(SQL_GET_LAST_FULL_BLOCK).expect("Unable to prepare")
|
||||
let mut statement = self.db.prepare(SQL_GET_LAST_FULL_BLOCK).expect("Unable to prepare");
|
||||
statement.bind(1, index as i64).expect("Unable to bind");
|
||||
statement
|
||||
}
|
||||
Some(pub_key) => {
|
||||
let mut statement = self.db.prepare(SQL_GET_LAST_FULL_BLOCK_FOR_KEY).expect("Unable to prepare");
|
||||
statement.bind(1, pub_key).expect("Unable to bind");
|
||||
statement.bind(1, index as i64).expect("Unable to bind");
|
||||
statement.bind(2, pub_key).expect("Unable to bind");
|
||||
statement
|
||||
}
|
||||
};
|
||||
@@ -506,7 +578,7 @@ impl Chain {
|
||||
}
|
||||
}
|
||||
let identity_hash = hash_identity(&name, None);
|
||||
if let Some(last) = self.get_last_full_block(Some(&pub_key)) {
|
||||
if let Some(last) = self.get_last_full_block(u64::MAX, Some(&pub_key)) {
|
||||
let new_id = !self.is_id_in_blockchain(&identity_hash, false);
|
||||
let time = last.timestamp + NEW_DOMAINS_INTERVAL - Utc::now().timestamp();
|
||||
if new_id && time > 0 {
|
||||
@@ -684,7 +756,7 @@ impl Chain {
|
||||
return Bad;
|
||||
}
|
||||
if let Some(prev_block) = self.get_block(block.index - 1) {
|
||||
if block.prev_block_hash != prev_block.hash {
|
||||
if block.prev_block_hash.ne(&prev_block.hash) {
|
||||
warn!("Ignoring block with wrong previous hash:\n{:?}", &block);
|
||||
return Bad;
|
||||
}
|
||||
@@ -710,11 +782,13 @@ impl Chain {
|
||||
warn!("Block {:?} is trying to spoof an identity!", &block);
|
||||
return Bad;
|
||||
}
|
||||
if let Some(last) = self.get_last_full_block(Some(&block.pub_key)) {
|
||||
let new_id = !self.is_id_in_blockchain(&transaction.identity, false);
|
||||
if new_id && last.timestamp + NEW_DOMAINS_INTERVAL > block.timestamp {
|
||||
warn!("Block {:?} is mined too early!", &block);
|
||||
return Bad;
|
||||
if let Some(last) = self.get_last_full_block(block.index, Some(&block.pub_key)) {
|
||||
if last.index < block.index {
|
||||
let new_id = !self.is_id_in_blockchain(&transaction.identity, false);
|
||||
if new_id && last.timestamp + NEW_DOMAINS_INTERVAL > block.timestamp {
|
||||
warn!("Block {:?} is mined too early!", &block);
|
||||
return Bad;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check if yggdrasil only quality of zone is not violated
|
||||
@@ -740,7 +814,7 @@ impl Chain {
|
||||
warn!("Block is from the future, how is this possible?");
|
||||
return Future;
|
||||
}
|
||||
if !self.origin.is_zero() && block.hash != self.origin {
|
||||
if !self.origin.is_zero() && block.hash.ne(&self.origin) {
|
||||
warn!("Mining gave us a bad block:\n{:?}", &block);
|
||||
return Bad;
|
||||
}
|
||||
@@ -767,7 +841,7 @@ impl Chain {
|
||||
return Twin;
|
||||
}
|
||||
if let Some(my_block) = self.get_block(block.index) {
|
||||
return if my_block.hash != block.hash {
|
||||
return if my_block.hash.ne(&block.hash) {
|
||||
warn!("Got forked block {} with hash {:?} instead of {:?}", block.index, block.hash, last_block.hash);
|
||||
Fork
|
||||
} else {
|
||||
@@ -775,7 +849,7 @@ impl Chain {
|
||||
Twin
|
||||
};
|
||||
}
|
||||
} else if block.prev_block_hash != last_block.hash {
|
||||
} else if block.prev_block_hash.ne(&last_block.hash) {
|
||||
warn!("Ignoring block with wrong previous hash:\n{:?}", &block);
|
||||
return Bad;
|
||||
}
|
||||
|
||||
+4
-4
@@ -78,8 +78,8 @@ impl Miner {
|
||||
Miner::mine_internal(Arc::clone(&context), job, mining.clone());
|
||||
} else {
|
||||
debug!("This job will wait for now");
|
||||
thread::sleep(delay);
|
||||
jobs.push(job);
|
||||
jobs.insert(0, job);
|
||||
let _ = cond_var.wait_timeout(jobs, delay).expect("Error in wait lock!");
|
||||
}
|
||||
} else {
|
||||
let _ = cond_var.wait(jobs).expect("Error in wait lock!");
|
||||
@@ -87,7 +87,7 @@ impl Miner {
|
||||
}
|
||||
});
|
||||
let mining = self.mining.clone();
|
||||
let blocks = self.jobs.clone();
|
||||
let jobs = self.jobs.clone();
|
||||
let cond_var = self.cond_var.clone();
|
||||
self.context.lock().unwrap().bus.register(move |_uuid, e| {
|
||||
match e {
|
||||
@@ -100,7 +100,7 @@ impl Miner {
|
||||
if !mining.load(Ordering::SeqCst) {
|
||||
let mut block = Block::new(None, Bytes::default(), hash, SIGNER_DIFFICULTY);
|
||||
block.index = index;
|
||||
blocks.lock().unwrap().push(MineJob { start, block, keystore: keystore.deref().clone() });
|
||||
jobs.lock().unwrap().push(MineJob { start, block, keystore: keystore.deref().clone() });
|
||||
cond_var.notify_all();
|
||||
info!("Added a signing block to mine");
|
||||
}
|
||||
|
||||
+9
-47
@@ -4,7 +4,7 @@ extern crate serde_json;
|
||||
use std::{io, thread};
|
||||
use std::io::{Read, Write};
|
||||
use std::net::{IpAddr, Shutdown, SocketAddr, SocketAddrV4};
|
||||
use std::sync::{Arc, Mutex, MutexGuard};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
@@ -167,6 +167,7 @@ impl Network {
|
||||
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");
|
||||
}
|
||||
}
|
||||
(height, context.chain.last_hash())
|
||||
@@ -429,7 +430,9 @@ fn handle_message(context: Arc<Mutex<Context>>, message: Message, peers: &mut Pe
|
||||
let mut context = context.lock().unwrap();
|
||||
context.chain.update_max_height(height);
|
||||
}
|
||||
if hash != my_hash {
|
||||
if hash.ne(&my_hash) {
|
||||
debug!("1Hashes are different, requesting block {} from {}", my_height, peer.get_addr().ip());
|
||||
debug!("My hash: {:?}, their hash: {:?}", &my_hash, &hash);
|
||||
State::message(Message::GetBlock { index: my_height })
|
||||
} else {
|
||||
State::message(Message::pong(my_height, my_hash))
|
||||
@@ -443,7 +446,9 @@ fn handle_message(context: Arc<Mutex<Context>>, message: Message, peers: &mut Pe
|
||||
let mut context = context.lock().unwrap();
|
||||
context.chain.update_max_height(height);
|
||||
}
|
||||
if hash != my_hash {
|
||||
if hash.ne(&my_hash) {
|
||||
debug!("2Hashes are different, requesting block {} from {}", my_height, peer.get_addr().ip());
|
||||
debug!("My hash: {:?}, their hash: {:?}", &my_hash, &hash);
|
||||
State::message(Message::GetBlock { index: my_height })
|
||||
} else {
|
||||
if random::<u8>() < 10 {
|
||||
@@ -511,7 +516,7 @@ fn process_new_block(context: Arc<Mutex<Context>>, peers: &mut Peers, token: &To
|
||||
BlockQuality::Future => { debug!("Ignoring future block {}", block.index); }
|
||||
BlockQuality::Bad => {
|
||||
// TODO save bad public keys to banned table
|
||||
debug!("Ignoring bad block {} with hash {:?} from {}", block.index, block.hash, peer.get_addr());
|
||||
debug!("Ignoring bad block from {}:\n{:?}", peer.get_addr(), &block);
|
||||
let height = context.chain.height();
|
||||
context.chain.update_max_height(height);
|
||||
context.bus.post(crate::event::Event::SyncFinished);
|
||||
@@ -533,49 +538,6 @@ fn process_new_block(context: Arc<Mutex<Context>>, peers: &mut Peers, token: &To
|
||||
State::idle()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn deal_with_fork(context: MutexGuard<Context>, peer: &mut Peer, block: Block) {
|
||||
peer.add_fork_block(block);
|
||||
let mut vector: Vec<&Block> = peer.get_fork().values().collect();
|
||||
vector.sort_by(|a, b| a.index.cmp(&b.index));
|
||||
if vector[0].index == 0 {
|
||||
return;
|
||||
}
|
||||
if let Some(prev_block) = context.chain.get_block(vector[0].index - 1) {
|
||||
// If this block is not root of the fork (we need to go ~deeper~ more backwards)
|
||||
if vector[0].prev_block_hash != prev_block.hash {
|
||||
return;
|
||||
}
|
||||
// Okay, prev_block is the common root for our chain and the fork
|
||||
let mut check_ok = true;
|
||||
vector.insert(0, &prev_block);
|
||||
let mut prev_block = &vector[0];
|
||||
for block in &vector {
|
||||
if block == prev_block {
|
||||
continue;
|
||||
}
|
||||
if !check_block(block, prev_block) {
|
||||
check_ok = false;
|
||||
break;
|
||||
}
|
||||
prev_block = block;
|
||||
}
|
||||
match check_ok {
|
||||
true => {
|
||||
// TODO count fork chain "work" and decide which chain is "better"
|
||||
}
|
||||
false => {
|
||||
warn!("Fork chain is wrong!");
|
||||
peer.set_state(State::Banned);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn check_block(block: &Block, prev: &Block) -> bool {
|
||||
prev.index == block.index - 1 && prev.hash == block.prev_block_hash
|
||||
}
|
||||
|
||||
fn would_block(err: &io::Error) -> bool {
|
||||
err.kind() == io::ErrorKind::WouldBlock
|
||||
}
|
||||
|
||||
@@ -196,11 +196,6 @@ fn action_loaded(context: &Arc<Mutex<Context>>, web_view: &mut WebView<()>) {
|
||||
let context_copy = Arc::clone(&context);
|
||||
let mut c = context.lock().unwrap();
|
||||
|
||||
let keystore = c.keystore.clone();
|
||||
if let Some(event) = c.chain.update(&keystore) {
|
||||
c.bus.post(event);
|
||||
}
|
||||
|
||||
c.bus.register(move |_uuid, e| {
|
||||
//debug!("Got event from bus {:?}", &e);
|
||||
let status = Arc::clone(&status);
|
||||
@@ -220,10 +215,6 @@ fn action_loaded(context: &Arc<Mutex<Context>>, web_view: &mut WebView<()>) {
|
||||
Event::KeyLoaded { path, public, hash } |
|
||||
Event::KeySaved { path, public, hash } => {
|
||||
load_domains(&mut context, &handle);
|
||||
let keystore = context.keystore.clone();
|
||||
if let Some(event) = context.chain.update(&keystore) {
|
||||
context.bus.post(event);
|
||||
}
|
||||
format!("keystoreChanged('{}', '{}', '{}');", &path, &public, &hash)
|
||||
}
|
||||
Event::MinerStarted | Event::KeyGeneratorStarted => {
|
||||
|
||||
Reference in New Issue
Block a user