From 394463ef159a5620a604024b5e2c4cb631aa967d Mon Sep 17 00:00:00 2001 From: Revertron Date: Tue, 16 Mar 2021 12:24:31 +0100 Subject: [PATCH] Added more restriction for blocks and zone names. --- other-tlds.txt | 2 + src/blockchain/chain.rs | 92 +++++++++++++++++++++++++------------ src/blockchain/constants.rs | 8 ++-- src/bytes.rs | 7 +++ src/context.rs | 4 +- src/p2p/network.rs | 22 ++++----- src/utils.rs | 2 +- src/web_ui.rs | 9 +++- 8 files changed, 95 insertions(+), 51 deletions(-) diff --git a/other-tlds.txt b/other-tlds.txt index 955a50c..900b71c 100644 --- a/other-tlds.txt +++ b/other-tlds.txt @@ -29,4 +29,6 @@ invalid local localhost onion +i2p +meshname test \ No newline at end of file diff --git a/src/blockchain/chain.rs b/src/blockchain/chain.rs index dd079ff..ba7803b 100644 --- a/src/blockchain/chain.rs +++ b/src/blockchain/chain.rs @@ -13,6 +13,7 @@ use crate::blockchain::enums::BlockQuality::*; use crate::blockchain::hash_utils::*; use crate::settings::Settings; use crate::keys::check_public_key_strength; +use std::cmp::min; const DB_NAME: &str = "blockchain.db"; const SQL_CREATE_TABLES: &str = "CREATE TABLE blocks ( @@ -196,6 +197,10 @@ impl Chain { /// Gets last block that has a Transaction within pub fn get_last_full_block(&self, pub_key: Option<&[u8]>) -> Option { + if self.last_full_block.is_some() && pub_key.is_none() { + return Some(self.last_full_block.clone().unwrap()); + } + let mut statement = match pub_key { None => { self.db.prepare(SQL_GET_LAST_FULL_BLOCK).expect("Unable to prepare") @@ -398,6 +403,28 @@ impl Chain { warn!("Block is from the future, how is this possible?"); return Future; } + if block.index > LOCKER_BLOCK_START { + // If this block is locked part of blockchain + if let Some(full_block) = &self.last_full_block { + let locker_blocks = self.height() - full_block.index; + if locker_blocks < LOCKER_BLOCK_SIGNS { + // Last full block is not locked enough + if block.transaction.is_some() { + warn!("Someone mined full block over full block"); + return Bad; + } else { + if self.check_block_for_lock(&block, full_block) == Bad { + return Bad; + } + } + } else if locker_blocks < LOCKER_BLOCK_LOCKERS && block.transaction.is_none() { + if self.check_block_for_lock(&block, full_block) == Bad { + return Bad; + } + } + } + } + if block.index <= last_block.index { if last_block.hash == block.hash { warn!("Ignoring block {}, we already have it", block.index); @@ -413,50 +440,55 @@ impl Chain { }; } } - if block.transaction.is_none() { - if let Some(locker) = self.get_block_locker(&last_block, block.timestamp) { - if locker != block.pub_key { - warn!("Ignoring block {}, as wrong locker", block.index); - return Bad; - } - } - } + } } Good } + fn check_block_for_lock(&self, block: &&Block, full_block: &Block) -> BlockQuality { + // If we got a locker/signing block + let lockers: HashSet = self.get_block_lockers(full_block).into_iter().collect(); + if !lockers.contains(&block.pub_key) { + warn!("Ignoring block {}, as wrong locker", block.index); + return Bad; + } + // If this locker's public key has already locked/signed that block we return error + for i in (full_block.index + 1)..block.index { + let locker = self.get_block(i).expect("Error in DB!"); + if locker.pub_key == block.pub_key { + warn!("Ignoring block {}, already locked by this key", block.index); + return Bad; + } + } + Good + } + /// Gets a public key of a node that needs to mine "locker" block above this block - pub fn get_block_locker(&self, block: &Block, timestamp: i64) -> Option { - if block.hash.is_empty() || block.hash.is_zero() { - return None; - } + /// block - last full block + pub fn get_block_lockers(&self, block: &Block) -> Vec { + let mut result = Vec::new(); if block.index < LOCKER_BLOCK_START { - return None; + return result; } - match self.get_last_full_block(None) { - Some(b) => { - if b.index + LOCKER_BLOCK_COUNT <= block.index { - trace!("Block {} is locked enough", b.index); - return None; - } - } - None => {} - } - // How many 5 min intervals have passed since this block? - let intervals = ((timestamp - block.timestamp) / LOCKER_BLOCK_INTERVAL) as u64; + let mut set = HashSet::new(); let tail = block.hash.get_tail_u64(); - let start_index = 1 + ((tail + tail * intervals) % (block.index - 2)); - for index in start_index..block.index { + let interval = min(block.index, LOCKER_BLOCK_INTERVAL) - 1; + let start_index = block.index - interval; + let mut count = 1; + while set.len() < LOCKER_BLOCK_LOCKERS as usize { + let index = start_index + ((tail * count) % LOCKER_BLOCK_INTERVAL); if let Some(b) = self.get_block(index) { - if b.pub_key != block.pub_key { - trace!("Locker block for block {} must be mined by owner of block {} block_hash: {:?}", block.index, b.index, block.hash); - return Some(b.pub_key); + if b.pub_key != block.pub_key && !set.contains(&b.pub_key) { + result.push(b.pub_key.clone()); + set.insert(b.pub_key); } + count += 1; } } - None + trace!("Got lockers for block {}: {:?}", block.index, &result); + result } fn get_block_from_statement(statement: &mut Statement) -> Option { diff --git a/src/blockchain/constants.rs b/src/blockchain/constants.rs index 0ebc1e2..f38c8a5 100644 --- a/src/blockchain/constants.rs +++ b/src/blockchain/constants.rs @@ -4,8 +4,10 @@ pub const BLOCK_DIFFICULTY: u32 = 24; pub const LOCKER_DIFFICULTY: u32 = 18; pub const KEYSTORE_DIFFICULTY: usize = 25; -pub const LOCKER_BLOCK_START: u64 = 10; -pub const LOCKER_BLOCK_COUNT: u64 = 3; -pub const LOCKER_BLOCK_INTERVAL: i64 = 300; +pub const LOCKER_BLOCK_START: u64 = 12; +pub const LOCKER_BLOCK_LOCKERS: u64 = 6; +pub const LOCKER_BLOCK_SIGNS: u64 = 4; +pub const LOCKER_BLOCK_TIME: i64 = 300; +pub const LOCKER_BLOCK_INTERVAL: u64 = 50; pub const FULL_BLOCKS_INTERVAL: i64 = 86400; // One day in seconds diff --git a/src/bytes.rs b/src/bytes.rs index 55fcb2d..b5d0ca6 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -12,6 +12,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; // For deserialization use serde::de::{Error as DeError, Visitor}; use std::ops::Deref; +use std::hash::{Hash, Hasher}; #[derive(Clone)] pub struct Bytes { @@ -110,6 +111,12 @@ impl Ord for Bytes { } } +impl Hash for Bytes { + fn hash(&self, state: &mut H) { + state.write(&self.data); + } +} + impl Deref for Bytes { type Target = Vec; diff --git a/src/context.rs b/src/context.rs index 788ef55..95e40ed 100644 --- a/src/context.rs +++ b/src/context.rs @@ -7,14 +7,14 @@ pub struct Context { pub settings: Settings, pub keystore: Keystore, pub chain: Chain, - pub iana: ExternalZones, + pub x_zones: ExternalZones, pub bus: Bus, } impl Context { /// Creating an essential context to work with pub fn new(settings: Settings, keystore: Keystore, chain: Chain) -> Context { - Context { settings, keystore, chain, iana: ExternalZones::new(), bus: Bus::new() } + Context { settings, keystore, chain, x_zones: ExternalZones::new(), bus: Bus::new() } } /// Load keystore and return Context diff --git a/src/p2p/network.rs b/src/p2p/network.rs index 718a5e3..7363c3a 100644 --- a/src/p2p/network.rs +++ b/src/p2p/network.rs @@ -13,11 +13,11 @@ use mio::net::{TcpListener, TcpStream}; #[allow(unused_imports)] use log::{trace, debug, info, warn, error}; -use crate::{Context, Block, p2p::Message, p2p::State, p2p::Peer, p2p::Peers}; +use crate::{Context, Block, p2p::Message, p2p::State, p2p::Peer, p2p::Peers, Bytes}; use std::net::{SocketAddr, IpAddr, SocketAddrV4, ToSocketAddrs}; use crate::blockchain::enums::BlockQuality; use crate::blockchain::CHAIN_VERSION; -use chrono::Utc; +use std::collections::HashSet; const SERVER: Token = Token(0); const POLL_TIMEOUT: Option = Some(Duration::from_millis(3000)); @@ -356,7 +356,7 @@ fn handle_message(context: Arc>, message: Message, peers: &mut Pe let peer = peers.get_mut_peer(token).unwrap(); peer.set_received_block(block.index); if let Some(transaction) = &block.transaction { - if context.lock().unwrap().iana.has_hash(&transaction.identity.to_string()) { + if context.lock().unwrap().x_zones.has_hash(&transaction.identity.to_string()) { // This peer has mined some of the forbidden zones return State::Banned; } @@ -403,16 +403,12 @@ fn mine_locker_block(context: Arc>) { info!("No locker mining while syncing"); return; } - match context.chain.get_block_locker(&block, Utc::now().timestamp()) { - Some(key) => { - if key == context.keystore.get_public() { - info!("We have an honor to mine locker block!"); - context.bus.post(crate::event::Event::ActionMineLocker { index: block.index + 1, hash: block.hash }); - } else { - info!("Locker block must be mined by another node: {:?}", &key); - } - } - None => {} + let lockers: HashSet = context.chain.get_block_lockers(&block).into_iter().collect(); + if lockers.contains(&context.keystore.get_public()) { + info!("We have an honor to mine locker block!"); + context.bus.post(crate::event::Event::ActionMineLocker { index: block.index + 1, hash: block.hash }); + } else { + info!("Locker block must be mined by other nodes"); } } } diff --git a/src/utils.rs b/src/utils.rs index f33d6f5..327f118 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -41,7 +41,7 @@ pub fn check_domain(name: &str, allow_dots: bool) -> bool { } last_dot = false; last_hyphen = false; - if !char.is_alphanumeric() { + if !char.is_ascii_alphanumeric() { return false; } } diff --git a/src/web_ui.rs b/src/web_ui.rs index 8c1a0e5..fef9489 100644 --- a/src/web_ui.rs +++ b/src/web_ui.rs @@ -185,7 +185,7 @@ pub fn run_interface(context: Arc>, miner: Arc>) { TransferDomain { .. } => {} CheckZone { name } => { let name = name.to_lowercase(); - if !check_domain(&name, false) || context.lock().unwrap().iana.has_zone(&name) { + if name.len() > 10 || !check_domain(&name, false) || context.lock().unwrap().x_zones.has_zone(&name) { web_view.eval("zoneAvailable(false)").expect("Error evaluating!"); } else { let c = context.lock().unwrap(); @@ -195,6 +195,11 @@ pub fn run_interface(context: Arc>, miner: Arc>) { } CreateZone { name, data } => { let name = name.to_lowercase(); + if name.len() > 10 || !check_domain(&name, false) || context.lock().unwrap().x_zones.has_zone(&name) { + warn!("This zone is unavailable for mining!"); + let _ = web_view.eval(&format!("showWarning('{}');", "This zone is unavailable for mining!")); + return Ok(()); + } let data = data.to_lowercase(); let (keystore, transaction) = { let context = context.lock().unwrap(); @@ -256,7 +261,7 @@ pub fn run_interface(context: Arc>, miner: Arc>) { fn create_domain>(context: Arc>, miner: Arc>, name: S, data: S, keystore: &Keystore) { let name = name.into(); info!("Generating domain or zone {}", name); - if context.lock().unwrap().iana.has_zone(&name) { + if context.lock().unwrap().x_zones.has_zone(&name) { error!("Unable to mine IANA zone {}!", &name); return; }