diff --git a/src/blockchain/block.rs b/src/blockchain/block.rs index 567282a..b090825 100644 --- a/src/blockchain/block.rs +++ b/src/blockchain/block.rs @@ -1,6 +1,7 @@ extern crate serde; extern crate serde_json; +use std::cell::RefCell; use std::fmt::Debug; use serde::{Deserialize, Serialize}; @@ -27,7 +28,9 @@ pub struct Block { #[serde(default, skip_serializing_if = "Bytes::is_zero")] pub signature: Bytes, #[serde(skip_serializing_if = "Option::is_none")] - pub transaction: Option + pub transaction: Option, + #[serde(default, skip)] + hash_good: RefCell } impl Block { @@ -43,7 +46,8 @@ impl Block { prev_block_hash, hash: Bytes::default(), pub_key, - signature: Bytes::default() + signature: Bytes::default(), + hash_good: RefCell::new(false) } } @@ -60,7 +64,8 @@ impl Block { prev_block_hash, hash, pub_key, - signature + signature, + hash_good: RefCell::new(false) } } @@ -74,6 +79,14 @@ impl Block { self.prev_block_hash == Bytes::default() } + pub fn is_hash_good(&self) -> bool { + *self.hash_good.borrow() + } + + pub fn set_hash_good(&self, good: bool) { + *self.hash_good.borrow_mut() = good; + } + /// Serializes block to CBOR for network pub fn as_bytes(&self) -> Vec { serde_cbor::to_vec(&self).unwrap() diff --git a/src/blockchain/chain.rs b/src/blockchain/chain.rs index 431f464..637548f 100644 --- a/src/blockchain/chain.rs +++ b/src/blockchain/chain.rs @@ -35,7 +35,7 @@ const SQL_ADD_DOMAIN: &str = "INSERT INTO domains (id, timestamp, identity, conf 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 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_OWNER_BY_ID: &str = "SELECT signing FROM domains WHERE id < ? AND identity = ? ORDER BY id DESC LIMIT 1;"; +const SQL_GET_DOMAIN_OWNER_BY_ID: &str = "SELECT signing, timestamp FROM domains WHERE id < ? AND identity = ? ORDER BY id DESC LIMIT 1;"; const SQL_GET_DOMAIN_BY_ID: &str = "SELECT * FROM domains WHERE identity = ? AND id < ? ORDER BY id DESC LIMIT 1;"; const SQL_GET_DOMAINS_BY_KEY: &str = "SELECT timestamp, identity, data, signing FROM domains WHERE signing = ? ORDER BY id;"; const SQL_GET_DOMAINS_COUNT: &str = "SELECT count(DISTINCT identity) FROM domains;"; @@ -152,7 +152,7 @@ impl Chain { } //let last = self.last_block.clone().unwrap(); - if self.check_block(&block, &last_block, &last_full_block) != BlockQuality::Good { + if self.check_block(&block, &last_block, &last_full_block) != Good { error!("Block {} is bad:\n{:?}", block.index, &block); info!("Truncating database from block {}...", block.index); match self.truncate_db_from_block(block.index) { @@ -391,10 +391,10 @@ impl Chain { statement.bind(7, transaction.to_string().as_str())?; } } - statement.bind(8, &**block.prev_block_hash)?; - statement.bind(9, &**block.hash)?; - statement.bind(10, &**block.pub_key)?; - statement.bind(11, &**block.signature)?; + statement.bind(8, block.prev_block_hash.as_slice())?; + statement.bind(9, block.hash.as_slice())?; + statement.bind(10, block.pub_key.as_slice())?; + statement.bind(11, block.signature.as_slice())?; statement.next() } @@ -409,11 +409,11 @@ impl Chain { let mut statement = self.db.prepare(sql)?; statement.bind(1, index as i64)?; statement.bind(2, timestamp)?; - statement.bind(3, &**t.identity)?; - statement.bind(4, &**t.confirmation)?; + statement.bind(3, t.identity.as_slice())?; + statement.bind(4, t.confirmation.as_slice())?; statement.bind(5, t.data.as_ref() as &str)?; - statement.bind(6, &**t.signing)?; - statement.bind(7, &**t.encryption)?; + statement.bind(6, t.signing.as_slice())?; + statement.bind(7, t.encryption.as_slice())?; statement.next() } @@ -488,12 +488,13 @@ impl Chain { } /// Checks if any domain is available to mine for this client (pub_key) - pub fn is_domain_available(&self, height: u64, domain: &str, keystore: &Keystore) -> bool { + pub fn is_domain_available(&self, height: u64, domain: &str, public_key: &Bytes) -> bool { if domain.is_empty() { return false; } let identity_hash = hash_identity(domain, None); - if !self.is_id_available(height, &identity_hash, &keystore.get_public()) { + if !self.is_id_available(height, &identity_hash, public_key) { + warn!("Domain {} is not available!", domain); return false; } @@ -512,7 +513,7 @@ impl Chain { pub fn is_id_available(&self, height: u64, identity: &Bytes, public_key: &Bytes) -> bool { let mut statement = self.db.prepare(SQL_GET_DOMAIN_OWNER_BY_ID).unwrap(); statement.bind(1, height as i64).expect("Error in bind"); - statement.bind(2, &***identity).expect("Error in bind"); + statement.bind(2, identity.as_slice()).expect("Error in bind"); while let State::Row = statement.next().unwrap() { let pub_key = Bytes::from_bytes(&statement.read::>(0).unwrap()); if !pub_key.eq(public_key) { @@ -557,7 +558,7 @@ impl Chain { // Checking for existing domain in DB let mut statement = self.db.prepare(SQL_GET_DOMAIN_OWNER_BY_ID).unwrap(); statement.bind(1, height as i64).expect("Error in bind"); - statement.bind(2, &***id).expect("Error in bind"); + statement.bind(2, id.as_slice()).expect("Error in bind"); if let State::Row = statement.next().unwrap() { // If there is such an ID return true; @@ -705,7 +706,7 @@ impl Chain { let keystore = keystore.unwrap(); let pub_key = keystore.get_public(); let mut statement = self.db.prepare(SQL_GET_DOMAINS_BY_KEY).unwrap(); - statement.bind(1, &**pub_key).expect("Error in bind"); + statement.bind(1, pub_key.as_slice()).expect("Error in bind"); let height = self.get_height(); while let State::Row = statement.next().unwrap() { let timestamp = statement.read::(0).unwrap(); @@ -805,14 +806,10 @@ impl Chain { } if let Some(last) = last_block { if block.index > last.index + 1 { - info!("Got future block {}", block.index); + debug!("Got future block {}", block.index); return Future; } } - if !check_public_key_strength(&block.pub_key, KEYSTORE_DIFFICULTY) { - warn!("Ignoring block with weak public key:\n{:?}", &block); - return Bad; - } let difficulty = match &block.transaction { None => { if block.index == 1 { @@ -853,12 +850,12 @@ impl Chain { } if let Some(transaction) = &block.transaction { - let current_height = match last_block { - None => 0, - Some(block) => block.index - }; + if !check_public_key_strength(&block.pub_key, KEYSTORE_DIFFICULTY) { + warn!("Ignoring block with weak public key:\n{:?}", &block); + return Bad; + } // If this domain is not available to this public key - if !self.is_id_available(current_height, &transaction.identity, &block.pub_key) { + if !self.is_id_available(block.index - 1, &transaction.identity, &block.pub_key) { warn!("Block {:?} is trying to spoof an identity!", &block); return Bad; } diff --git a/src/blockchain/hash_utils.rs b/src/blockchain/hash_utils.rs index 24fdf62..8c73a92 100644 --- a/src/blockchain/hash_utils.rs +++ b/src/blockchain/hash_utils.rs @@ -7,10 +7,16 @@ use crate::{Block, Bytes, Keystore}; /// Checks block's hash and returns true on valid hash or false otherwise pub fn check_block_hash(block: &Block) -> bool { + // If this block's hash was already checked as good + if block.is_hash_good() { + return true; + } let mut copy: Block = block.clone(); copy.hash = Bytes::default(); copy.signature = Bytes::default(); - blakeout_data(©.as_bytes_compact()) == block.hash + let good = blakeout_data(©.as_bytes_compact()) == block.hash; + block.set_hash_good(good); + good } /// Hashes data by given hasher diff --git a/src/p2p/network.rs b/src/p2p/network.rs index 2b85713..578c5e5 100644 --- a/src/p2p/network.rs +++ b/src/p2p/network.rs @@ -598,7 +598,7 @@ impl Network { if index != block.index { return State::Banned; } - info!("Received block {} with hash {:?}", block.index, &block.hash); + debug!("Received block {} with hash {:?}", block.index, &block.hash); if !seen_blocks.contains(&block.hash) { self.handle_block(token, block, seen_blocks) } else { diff --git a/src/web_ui.rs b/src/web_ui.rs index be70844..f82fd42 100644 --- a/src/web_ui.rs +++ b/src/web_ui.rs @@ -126,7 +126,7 @@ fn action_check_domain(context: &Arc>, web_view: &mut WebView<()> let c = context.lock().unwrap(); if let Some(keystore) = c.get_keystore() { let name = name.to_lowercase(); - let available = c.get_chain().is_domain_available(c.get_chain().get_height(), &name, keystore); + let available = c.get_chain().is_domain_available(c.get_chain().get_height(), &name, &keystore.get_public()); web_view.eval(&format!("domainAvailable({})", available)).expect("Error evaluating!"); } }