Added blockchain full check on start. Fixed cond_var blocking.

This commit is contained in:
Revertron
2021-04-20 18:46:06 +02:00
parent 51633f3e27
commit 161e600290
4 changed files with 106 additions and 79 deletions
+93 -19
View File
@@ -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
View File
@@ -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
View File
@@ -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
}
-9
View File
@@ -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 => {