From 92209689d6873034c091a7db34ced391af2b254b Mon Sep 17 00:00:00 2001 From: Revertron Date: Sun, 11 Apr 2021 17:50:55 +0200 Subject: [PATCH] Fixed parsing of DomainData, many other fixes. --- Cargo.toml | 2 +- src/blockchain/chain.rs | 100 ++++++++++++++++++++++++++-------- src/blockchain/hash_utils.rs | 2 +- src/blockchain/transaction.rs | 2 +- src/bytes.rs | 12 ++-- src/web_ui.rs | 3 +- src/webview/index.html | 2 +- src/webview/scripts.js | 24 ++++++-- src/webview/styles.css | 6 +- 9 files changed, 112 insertions(+), 41 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6935e8f..a0ff669 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "alfis" -version = "0.4.0" +version = "0.4.1" authors = ["Revertron "] edition = "2018" build = "build.rs" diff --git a/src/blockchain/chain.rs b/src/blockchain/chain.rs index 6b1b3d0..72dd0d7 100644 --- a/src/blockchain/chain.rs +++ b/src/blockchain/chain.rs @@ -8,7 +8,7 @@ use chrono::Utc; use log::{debug, error, info, trace, warn}; use sqlite::{Connection, State, Statement}; -use crate::{Block, Bytes, Keystore, Transaction, check_domain, get_domain_zone, is_yggdrasil_record}; +use crate::{Block, Bytes, Keystore, Transaction, check_domain, get_domain_zone, is_yggdrasil_record, from_hex}; use crate::commons::constants::*; use crate::blockchain::types::{BlockQuality, MineResult, Options}; use crate::blockchain::types::BlockQuality::*; @@ -27,6 +27,7 @@ const SQL_ADD_BLOCK: &str = "INSERT INTO blocks (id, timestamp, version, difficu prev_block_hash, hash, pub_key, signature) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"; const SQL_REPLACE_BLOCK: &str = "UPDATE blocks SET timestamp = ?, version = ?, difficulty = ?, random = ?, nonce = ?, 'transaction' = ?,\ prev_block_hash = ?, hash = ?, pub_key = ?, signature = ? WHERE id = ?;"; +const SQL_DELETE_FAULTY_BLOCKS: &str = "DELETE FROM blocks WHERE id >= ?;"; const SQL_GET_LAST_BLOCK: &str = "SELECT * FROM blocks ORDER BY id DESC LIMIT 1;"; 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 (?, ?, ?, ?, ?, ?)"; @@ -73,7 +74,21 @@ impl Chain { } // Trying to get last block from DB to check its version - let block: Option = match self.db.prepare(SQL_GET_LAST_BLOCK) { + // If some block loaded we check its version and determine if we need some migration + if let Some(block) = self.load_last_block() { + // Cache some info + self.last_block = Some(block.clone()); + if block.transaction.is_some() { + self.last_full_block = Some(block); + } else { + self.last_full_block = self.get_last_full_block(None); + } + } + self.check_chain(); + } + + fn load_last_block(&mut self) -> Option { + match self.db.prepare(SQL_GET_LAST_BLOCK) { Ok(mut statement) => { let mut result = None; while statement.next().unwrap() == State::Row { @@ -96,16 +111,6 @@ impl Chain { self.db.execute(SQL_CREATE_TABLES).expect("Error creating DB tables"); None } - }; - // If some block loaded we check its version and determine if we need some migration - if let Some(block) = block { - // Cache some info - self.last_block = Some(block.clone()); - if block.transaction.is_some() { - self.last_full_block = Some(block); - } else { - self.last_full_block = self.get_last_full_block(None); - } } } @@ -128,6 +133,30 @@ impl Chain { let _ = fs::remove_file(&file).is_err(); } + fn check_chain(&mut self) { + let height = self.height(); + info!("Local blockchain height is {}", height); + for id in (1..=height).rev() { + info!("Checking block {}", id); + match self.get_block(id) { + None => {} + Some(block) => { + let faulty_block_hash = "0000133B790B61460D757E1F1F2D04480C8340D28CA73AE5AF27DBBF60548D00"; + let bytes = Bytes::from_bytes(&from_hex(faulty_block_hash).unwrap()); + if block.hash == bytes { + if block.transaction.is_some() { + let _ = self.delete_transaction(block.index); + } + + let _ = self.delete_faulty_blocks(id); + } + } + } + } + self.last_block = self.load_last_block(); + info!("Last block after chain check: {:?}", &self.last_block); + } + fn get_options(&self) -> Options { let mut options = Options::empty(); if let Ok(mut statement) = self.db.prepare(SQL_GET_OPTIONS) { @@ -144,6 +173,13 @@ impl Chain { options } + fn delete_faulty_blocks(&mut self, from: u64) -> sqlite::Result { + warn!("Removing block with error {}", from); + let mut statement = self.db.prepare(SQL_DELETE_FAULTY_BLOCKS)?; + statement.bind(1, from as i64)?; + statement.next() + } + pub fn add_block(&mut self, block: Block) { debug!("Adding block:\n{:?}", &block); let index = block.index; @@ -164,13 +200,7 @@ impl Chain { debug!("Replacing block {} with:\n{:?}", index, &block); let old_block = self.get_block(index).unwrap(); if old_block.transaction.is_some() { - let mut statement = self.db.prepare(SQL_DELETE_DOMAIN)?; - statement.bind(1, index as i64)?; - statement.next()?; - - let mut statement = self.db.prepare(SQL_DELETE_ZONE)?; - statement.bind(1, index as i64)?; - statement.next()?; + let _ = self.delete_transaction(index); } let index = block.index; @@ -188,6 +218,17 @@ impl Chain { Ok(()) } + fn delete_transaction(&mut self, index: u64) -> sqlite::Result<()> { + let mut statement = self.db.prepare(SQL_DELETE_DOMAIN)?; + statement.bind(1, index as i64)?; + statement.next()?; + + let mut statement = self.db.prepare(SQL_DELETE_ZONE)?; + statement.bind(1, index as i64)?; + statement.next()?; + Ok(()) + } + /// Adds block to blocks table fn add_block_to_table(&mut self, block: Block) -> sqlite::Result { let mut statement = self.db.prepare(SQL_ADD_BLOCK)?; @@ -447,10 +488,10 @@ impl Chain { } let identity = Bytes::from_bytes(&statement.read::>(2).unwrap()); let confirmation = Bytes::from_bytes(&statement.read::>(3).unwrap()); - let method = statement.read::(4).unwrap(); - let data = statement.read::(5).unwrap(); - let pub_key = Bytes::from_bytes(&statement.read::>(6).unwrap()); - let transaction = Transaction { identity, confirmation, class: method, data, pub_key }; + let class = String::from("domain"); + let data = statement.read::(4).unwrap(); + let pub_key = Bytes::from_bytes(&statement.read::>(5).unwrap()); + let transaction = Transaction { identity, confirmation, class, data, pub_key }; debug!("Found transaction for domain {}: {:?}", domain, &transaction); if transaction.check_identity(domain) { return Some(transaction); @@ -556,6 +597,14 @@ impl Chain { warn!("Block {:?} has wrong signature! Ignoring!", &block); return Bad; } + + let faulty_block_hash = "0000133B790B61460D757E1F1F2D04480C8340D28CA73AE5AF27DBBF60548D00"; + let bytes = Bytes::from_bytes(&from_hex(faulty_block_hash).unwrap()); + if block.hash == bytes { + warn!("Block {:?} is faulty! Ignoring!", &block); + return Bad; + } + if let Some(transaction) = &block.transaction { // TODO check for zone transaction if !self.is_id_available(&transaction.identity, &block.pub_key, false) || !self.is_id_available(&transaction.identity, &block.pub_key, true) { @@ -662,7 +711,10 @@ impl Chain { } u32::max_value() } - Err(_) => { u32::max_value() } + Err(_) => { + warn!("Error parsing DomainData from {:?}", transaction); + u32::max_value() + } } } "zone" => { ZONE_DIFFICULTY } diff --git a/src/blockchain/hash_utils.rs b/src/blockchain/hash_utils.rs index 220536a..e1a7c1f 100644 --- a/src/blockchain/hash_utils.rs +++ b/src/blockchain/hash_utils.rs @@ -36,7 +36,7 @@ pub fn hash_identity(identity: &str, key: Option<&Bytes>) -> Bytes { None => { Bytes::from_bytes(&identity) } Some(key) => { let mut buf = Vec::new(); - buf.append(&mut identity.clone()); + buf.append(&mut base.clone()); buf.append(&mut key.to_vec()); Bytes::from_bytes(&hash_sha256(&buf)) } diff --git a/src/blockchain/transaction.rs b/src/blockchain/transaction.rs index 8cd99a4..eba04ec 100644 --- a/src/blockchain/transaction.rs +++ b/src/blockchain/transaction.rs @@ -89,7 +89,7 @@ impl Serialize for Transaction { } } -#[derive(Clone, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct DomainData { pub domain: Bytes, pub zone: String, diff --git a/src/bytes.rs b/src/bytes.rs index 16f34be..49e5468 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -144,22 +144,24 @@ impl<'de> Visitor<'de> for BytesVisitor { type Value = Bytes; fn expecting(&self, formatter: &mut Formatter) -> Result<(), Error> { - formatter.write_str("32 or 64 bytes") + formatter.write_str("bytes in HEX format") } fn visit_str(self, value: &str) -> Result where E: DeError, { - if value.len() == 64 || value.len() == 128 { + if !value.is_empty() && value.len() % 2 == 0 { Ok(Bytes::new(crate::from_hex(value).unwrap())) + } else if value.is_empty() { + Ok(Bytes::default()) } else { - Err(E::custom("Key must be 32 or 64 bytes!")) + Err(E::custom("Expected bytes in HEX format!")) } } fn visit_bytes(self, value: &[u8]) -> Result where E: DeError, { - if value.len() == 32 || value.len() == 64 { + if !value.is_empty() { Ok(Bytes::from_bytes(value)) } else { - Err(E::custom("Key must be 32 or 64 bytes!")) + Ok(Bytes::default()) } } } diff --git a/src/web_ui.rs b/src/web_ui.rs index b3d93bc..d1442d6 100644 --- a/src/web_ui.rs +++ b/src/web_ui.rs @@ -331,8 +331,9 @@ fn action_create_domain(context: Arc>, miner: Arc>, let pub_key = keystore.get_public(); let mut data = match serde_json::from_str::(&data) { Ok(data) => { data } - Err(_) => { + Err(e) => { show_warning(web_view, "Something wrong with domain data. I cannot mine it."); + warn!("Error parsing data: {}", e); return; } }; diff --git a/src/webview/index.html b/src/webview/index.html index 23d88c8..63cbdc2 100644 --- a/src/webview/index.html +++ b/src/webview/index.html @@ -68,7 +68,7 @@
- +

To mine domains (or zones) you need to mine a strong pair of keys.

diff --git a/src/webview/scripts.js b/src/webview/scripts.js index 8170591..a4dc6c7 100644 --- a/src/webview/scripts.js +++ b/src/webview/scripts.js @@ -159,20 +159,26 @@ function recordOkay(okay) { function createDomain() { var new_domain = document.getElementById("new_domain").value.toLowerCase(); - var new_dom_records = JSON.stringify(recordsBuffer); var domain = new_domain + "." + currentZone.name; var data = {}; - data.domain = []; + data.domain = ""; data.zone = currentZone.name; - data.records = new_dom_records; + data.records = recordsBuffer; data.owners = []; // TODO make a dialog to fill them data.contacts = []; // TODO make a dialog to fill them - external.invoke(JSON.stringify({cmd: 'mineDomain', name: domain, records: new_dom_records})); + data = JSON.stringify(data); + external.invoke(JSON.stringify({cmd: 'mineDomain', name: domain, data: data})); } function domainMiningStarted() { - recordsBuffer = []; - refreshRecordsList(); + //recordsBuffer = []; + //refreshRecordsList(); + document.getElementById("tab_domains").disabled = true; + document.getElementById("domain_records").disabled = true; + document.getElementById("add_record_button").disabled = true; + document.getElementById("new_domain_button").disabled = true; + document.getElementById("new_zone_button").disabled = true; + document.getElementById("new_key_button").disabled = true; } function createZone() { @@ -309,6 +315,12 @@ function showMiningIndicator(visible, blue) { } else { indicator.className = 'busy_indicator is-hidden'; parent.style.display = 'none'; + document.getElementById("tab_domains").disabled = false; + document.getElementById("domain_records").disabled = false; + document.getElementById("add_record_button").disabled = false; + document.getElementById("new_domain_button").disabled = false; + document.getElementById("new_zone_button").disabled = false; + document.getElementById("new_key_button").disabled = false; } } diff --git a/src/webview/styles.css b/src/webview/styles.css index 2f97778..36754f7 100644 --- a/src/webview/styles.css +++ b/src/webview/styles.css @@ -59,7 +59,7 @@ body { .notification.is-success { position: absolute; - bottom: 10pt; + bottom: 30pt; right: 10pt; } @@ -81,4 +81,8 @@ path { .is-danger > span.icon > svg > path { fill: #f14668; +} + +.is-danger:hover > span.icon > svg > path { + fill: #ffffff; } \ No newline at end of file