Added more restriction for blocks and zone names.

This commit is contained in:
Revertron
2021-03-16 12:24:31 +01:00
parent fb4ed70f7c
commit 394463ef15
8 changed files with 95 additions and 51 deletions
+2
View File
@@ -29,4 +29,6 @@ invalid
local
localhost
onion
i2p
meshname
test
+62 -30
View File
@@ -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<Block> {
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<Bytes> = 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<Bytes> {
if block.hash.is_empty() || block.hash.is_zero() {
return None;
}
/// block - last full block
pub fn get_block_lockers(&self, block: &Block) -> Vec<Bytes> {
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<Block> {
+5 -3
View File
@@ -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
+7
View File
@@ -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<H: Hasher>(&self, state: &mut H) {
state.write(&self.data);
}
}
impl Deref for Bytes {
type Target = Vec<u8>;
+2 -2
View File
@@ -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<Event>,
}
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
+9 -13
View File
@@ -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<Duration> = Some(Duration::from_millis(3000));
@@ -356,7 +356,7 @@ fn handle_message(context: Arc<Mutex<Context>>, 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<Mutex<Context>>) {
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<Bytes> = 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");
}
}
}
+1 -1
View File
@@ -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;
}
}
+7 -2
View File
@@ -185,7 +185,7 @@ pub fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
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<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
}
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<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
fn create_domain<S: Into<String>>(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>, 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;
}