Added more restriction for blocks and zone names.
This commit is contained in:
@@ -29,4 +29,6 @@ invalid
|
||||
local
|
||||
localhost
|
||||
onion
|
||||
i2p
|
||||
meshname
|
||||
test
|
||||
+62
-30
@@ -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> {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user