Changed origin block index to 1. Added "locker" blocks - mining, exchange etc. Removed unnecesarry creation of 'zones' directory on startup. Changed bind port of DNS-UDP socket to random (fixes inability to start several copies of Alfis). Sped up block exchange by sending additional pings when we have more blocks than other peers. Fixed unnecesarry double requests of blocks. Totally reworked block checking on arrival. Added target tags for logging in main. Added a commandline flag to list all blocks in DB and exit.

pull/3/head
Revertron 3 years ago
parent 59df68d7c7
commit b0e78edb3d

@ -64,7 +64,7 @@ impl Block {
}
pub fn is_genesis(&self) -> bool {
self.index == 0 && self.transaction.is_none() && self.prev_block_hash == Bytes::default()
self.index == 1 && self.transaction.is_none() && self.prev_block_hash == Bytes::default()
}
pub fn as_bytes(&self) -> Vec<u8> {

@ -9,18 +9,43 @@ use std::cell::RefCell;
use chrono::Utc;
use crate::blockchain::transaction::hash_identity;
use crate::blockchain::blockchain::BlockQuality::*;
use crate::blockchain::{BLOCK_DIFFICULTY, CHAIN_VERSION};
use crate::blockchain::{BLOCK_DIFFICULTY, CHAIN_VERSION, LOCKER_BLOCK_START, LOCKER_DIFFICULTY, LOCKER_BLOCK_COUNT, LOCKER_BLOCK_INTERVAL};
const DB_NAME: &str = "blockchain.db";
const SQL_CREATE_TABLES: &str = "CREATE TABLE blocks (
'id' BIGINT NOT NULL PRIMARY KEY,
'timestamp' BIGINT NOT NULL,
'version' INT,
'difficulty' INTEGER,
'random' INTEGER,
'nonce' INTEGER,
'transaction' TEXT,
'prev_block_hash' BINARY,
'hash' BINARY,
'pub_key' BINARY,
'signature' BINARY);
CREATE INDEX block_index ON blocks (id);
CREATE TABLE transactions (id INTEGER PRIMARY KEY AUTOINCREMENT, identity BINARY, confirmation BINARY, method TEXT, data TEXT, pub_key BINARY);
CREATE INDEX ids ON transactions (identity);";
const SQL_ADD_BLOCK: &str = "INSERT INTO blocks (id, timestamp, version, difficulty, random, nonce, 'transaction',\
prev_block_hash, hash, pub_key, signature) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);";
const SQL_GET_LAST_BLOCK: &str = "SELECT * FROM blocks ORDER BY id DESC LIMIT 1;";
const SQL_ADD_TRANSACTION: &str = "INSERT INTO transactions (identity, confirmation, method, data, pub_key) VALUES (?, ?, ?, ?, ?)";
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 `transaction`<>'' ORDER BY id DESC LIMIT 1;";
const SQL_GET_PUBLIC_KEY_BY_ID: &str = "SELECT pub_key FROM transactions WHERE identity = ? ORDER BY id DESC LIMIT 1;";
const SQL_GET_ID_BY_ID: &str = "SELECT identity FROM transactions WHERE identity = ? ORDER BY id DESC LIMIT 1;";
const SQL_GET_TRANSACTION_BY_ID: &str = "SELECT * FROM transactions WHERE identity = ? ORDER BY id DESC LIMIT 1;";
pub struct Blockchain {
origin: Bytes,
pub version: u32,
pub blocks: Vec<Block>,
last_block: Option<Block>,
last_full_block: Option<Block>,
max_height: u64,
db: Connection,
zones: RefCell<HashSet<String>>
zones: RefCell<HashSet<String>>,
}
impl Blockchain {
@ -28,99 +53,120 @@ impl Blockchain {
let origin = settings.get_origin();
let db = sqlite::open(DB_NAME).expect("Unable to open blockchain DB");
let mut blockchain = Blockchain{ origin, version: CHAIN_VERSION, blocks: Vec::new(), last_block: None, max_height: 0, db, zones: RefCell::new(HashSet::new()) };
let mut blockchain = Blockchain {
origin,
version: CHAIN_VERSION,
blocks: Vec::new(),
last_block: None,
last_full_block: None,
max_height: 0,
db,
zones: RefCell::new(HashSet::new()),
};
blockchain.init_db();
blockchain
}
/// Reads options from DB or initializes and writes them to DB if not found
fn init_db(&mut self) {
match self.db.prepare("SELECT * FROM blocks ORDER BY id DESC LIMIT 1;") {
// Trying to get last block from DB to check its version
let block: Option<Block> = match self.db.prepare(SQL_GET_LAST_BLOCK) {
Ok(mut statement) => {
let mut result = None;
while statement.next().unwrap() == State::Row {
match Self::get_block_from_statement(&mut statement) {
None => { error!("Something wrong with block in DB!"); }
None => {
error!("Something wrong with block in DB!");
panic!();
}
Some(block) => {
info!("Loaded last block: {:?}", &block);
self.version = block.version;
self.last_block = Some(block);
debug!("Loaded last block: {:?}", &block);
result = Some(block);
break;
}
}
debug!("Blockchain version from DB = {}", self.version);
}
result
}
Err(_) => {
info!("No blockchain database found. Creating new.");
self.db.execute("
CREATE TABLE blocks (
'id' BIGINT,
'timestamp' BIGINT,
'version' TEXT,
'difficulty' INTEGER,
'random' INTEGER,
'nonce' INTEGER,
'transaction' TEXT,
'prev_block_hash' BINARY,
'hash' BINARY,
'pub_key' BINARY,
'signature' BINARY
);
CREATE INDEX block_index ON blocks (id);
CREATE TABLE transactions (id INTEGER PRIMARY KEY AUTOINCREMENT, identity BINARY, confirmation BINARY, method TEXT, data TEXT, pub_key BINARY);
CREATE INDEX ids ON transactions (identity);"
).expect("Error creating blocks table");
self.db.execute(SQL_CREATE_TABLES).expect("Error creating blocks table");
None
}
};
// If some block loaded we check its version and determine if we need some migration
if let Some(block) = block {
self.max_height = block.index;
if self.version > block.version {
self.migrate_db(block.version, self.version);
} else if self.version < block.version {
error!("Version downgrade {}->{} is not supported!", block.version, self.version);
panic!();
}
// 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();
}
}
}
fn migrate_db(&mut self, from: u32, to: u32) {
debug!("Migrating DB from {} to {}", from, to);
}
pub fn add_block(&mut self, block: Block) {
info!("Adding block:\n{:?}", &block);
self.blocks.push(block.clone());
self.last_block = Some(block.clone());
if block.transaction.is_some() {
self.last_full_block = Some(block.clone());
}
let transaction = block.transaction.clone();
{
// Adding block to DB
let mut statement = self.db.prepare("INSERT INTO blocks (\
id, timestamp, version, difficulty, random, nonce, 'transaction',\
prev_block_hash, hash, pub_key, signature)\
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);").unwrap();
statement.bind(1, block.index as i64).expect("Error in bind");
statement.bind(2, block.timestamp as i64).expect("Error in bind");
statement.bind(3, block.version as i64).expect("Error in bind");
statement.bind(4, block.difficulty as i64).expect("Error in bind");
statement.bind(5, block.random as i64).expect("Error in bind");
statement.bind(6, block.nonce as i64).expect("Error in bind");
match &transaction {
None => { statement.bind(7, "").expect("Error in bind"); }
Some(transaction) => {
statement.bind(7, transaction.to_string().as_ref() as &str).expect("Error in bind");
}
if self.add_block_to_table(block).is_ok() {
if let Some(transaction) = transaction {
self.add_transaction_to_table(&transaction).expect("Error adding transaction");
}
statement.bind(8, block.prev_block_hash.as_bytes()).expect("Error in bind");
statement.bind(9, block.hash.as_bytes()).expect("Error in bind");
statement.bind(10, block.pub_key.as_bytes()).expect("Error in bind");
statement.bind(11, block.signature.as_bytes()).expect("Error in bind");
statement.next().expect("Error adding block to DB");
}
}
if let Some(transaction) = transaction {
self.add_transaction(&transaction);
/// Adds block to blocks table
fn add_block_to_table(&mut self, block: Block) -> sqlite::Result<State> {
let mut statement = self.db.prepare(SQL_ADD_BLOCK)?;
statement.bind(1, block.index as i64)?;
statement.bind(2, block.timestamp as i64)?;
statement.bind(3, block.version as i64)?;
statement.bind(4, block.difficulty as i64)?;
statement.bind(5, block.random as i64)?;
statement.bind(6, block.nonce as i64)?;
match &block.transaction {
None => { statement.bind(7, "")?; }
Some(transaction) => {
statement.bind(7, transaction.to_string().as_str())?;
}
}
statement.bind(8, block.prev_block_hash.as_bytes())?;
statement.bind(9, block.hash.as_bytes())?;
statement.bind(10, block.pub_key.as_bytes())?;
statement.bind(11, block.signature.as_bytes())?;
statement.next()
}
fn add_transaction(&mut self, t: &Transaction) {
let mut statement = self.db.prepare("INSERT INTO transactions (identity, confirmation, method, data, pub_key) VALUES (?, ?, ?, ?, ?)").unwrap();
statement.bind(1, t.identity.as_bytes()).expect("Error in bind");
statement.bind(2, t.confirmation.as_bytes()).expect("Error in bind");
statement.bind(3, t.method.as_ref() as &str).expect("Error in bind");
statement.bind(4, t.data.as_ref() as &str).expect("Error in bind");
statement.bind(5, t.pub_key.as_bytes()).expect("Error in bind");
statement.next().expect("Error adding transaction to DB");
/// Adds transaction to transactions table
fn add_transaction_to_table(&mut self, t: &Transaction) -> sqlite::Result<State> {
let mut statement = self.db.prepare(SQL_ADD_TRANSACTION)?;
statement.bind(1, t.identity.as_bytes())?;
statement.bind(2, t.confirmation.as_bytes())?;
statement.bind(3, t.method.as_ref() as &str)?;
statement.bind(4, t.data.as_ref() as &str)?;
statement.bind(5, t.pub_key.as_bytes())?;
statement.next()
}
pub fn get_block(&self, index: u64) -> Option<Block> {
match self.db.prepare("SELECT * FROM blocks WHERE id=? LIMIT 1;") {
match self.db.prepare(SQL_GET_BLOCK_BY_ID) {
Ok(mut statement) => {
statement.bind(1, index as i64).expect("Error in bind");
while statement.next().unwrap() == State::Row {
@ -130,10 +176,10 @@ impl Blockchain {
None
}
Some(block) => {
debug!("Loaded block: {:?}", &block);
trace!("Loaded block: {:?}", &block);
Some(block)
}
}
};
}
None
}
@ -144,12 +190,38 @@ impl Blockchain {
}
}
/// Gets last block that has a Transaction within
pub fn get_last_full_block(&self) -> Option<Block> {
match self.db.prepare(SQL_GET_LAST_FULL_BLOCK) {
Ok(mut statement) => {
while statement.next().unwrap() == State::Row {
return match Self::get_block_from_statement(&mut statement) {
None => {
error!("Something wrong with block in DB!");
None
}
Some(block) => {
trace!("Got last full block: {:?}", &block);
Some(block)
}
};
}
None
}
Err(e) => {
warn!("Can't find any full blocks: {}", e);
None
}
}
}
/// Checks if any domain is available to mine for this client (pub_key)
pub fn is_domain_available(&self, domain: &str, keystore: &Keystore) -> bool {
if domain.is_empty() {
return false;
}
let identity_hash = hash_identity(domain, None);
let mut statement = self.db.prepare("SELECT pub_key FROM transactions WHERE identity = ? ORDER BY id DESC LIMIT 1;").unwrap();
let mut statement = self.db.prepare(SQL_GET_PUBLIC_KEY_BY_ID).unwrap();
statement.bind(1, identity_hash.as_bytes()).expect("Error in bind");
while let State::Row = statement.next().unwrap() {
let pub_key = Bytes::from_bytes(statement.read::<Vec<u8>>(0).unwrap().as_slice());
@ -170,6 +242,7 @@ impl Blockchain {
true
}
/// Checks if some zone exists in our blockchain
pub fn is_zone_in_blockchain(&self, zone: &str) -> bool {
if self.zones.borrow().contains(zone) {
return true;
@ -177,7 +250,7 @@ impl Blockchain {
// Checking for existing zone in DB
let identity_hash = hash_identity(zone, None);
let mut statement = self.db.prepare("SELECT identity FROM transactions WHERE identity = ? ORDER BY id DESC LIMIT 1;").unwrap();
let mut statement = self.db.prepare(SQL_GET_ID_BY_ID).unwrap();
statement.bind(1, identity_hash.as_bytes()).expect("Error in bind");
while let State::Row = statement.next().unwrap() {
// If there is such a zone
@ -187,13 +260,14 @@ impl Blockchain {
false
}
/// Gets full Transaction info for any domain. Used by DNS part.
pub fn get_domain_transaction(&self, domain: &str) -> Option<Transaction> {
if domain.is_empty() {
return None;
}
let identity_hash = hash_identity(domain, None);
let mut statement = self.db.prepare("SELECT * FROM transactions WHERE identity = ? ORDER BY id DESC LIMIT 1;").unwrap();
let mut statement = self.db.prepare(SQL_GET_TRANSACTION_BY_ID).unwrap();
statement.bind(1, identity_hash.as_bytes()).expect("Error in bind");
while let State::Row = statement.next().unwrap() {
let identity = Bytes::from_bytes(statement.read::<Vec<u8>>(1).unwrap().as_slice());
@ -225,7 +299,7 @@ impl Blockchain {
match self.last_block {
None => { 0u64 }
Some(ref block) => {
block.index + 1
block.index
}
}
}
@ -247,14 +321,19 @@ impl Blockchain {
}
}
/// Check if this block can be added to our blockchain
pub fn check_new_block(&self, block: &Block) -> BlockQuality {
let timestamp = Utc::now().timestamp();
if block.timestamp > timestamp {
warn!("Ignoring block from the future:\n{:?}", &block);
return Bad;
}
if !hash_is_good(block.hash.as_bytes(), BLOCK_DIFFICULTY as usize) {
warn!("Ignoring block with low difficulty:\n{:?}", &block);
let difficulty = match block.transaction {
None => { LOCKER_DIFFICULTY }
Some(_) => { BLOCK_DIFFICULTY }
};
if block.difficulty < difficulty {
warn!("Block difficulty is lower than needed");
return Bad;
}
if !hash_is_good(block.hash.as_bytes(), block.difficulty as usize) {
@ -264,6 +343,7 @@ impl Blockchain {
match &self.last_block {
None => {
if !block.is_genesis() {
warn!("Block is from the future, how is this possible?");
return Future;
}
if !self.origin.is_zero() && block.hash != self.origin {
@ -277,16 +357,31 @@ impl Blockchain {
return Bad;
}
if last_block.index + 1 < block.index {
warn!("Got block from the future");
warn!("Block is from the future, how is this possible?");
return Future;
}
if last_block.index >= block.index && last_block.hash == block.hash {
warn!("Ignoring block {}, we already have it", block.index);
return Twin;
if block.index <= last_block.index {
if last_block.hash == block.hash {
warn!("Ignoring block {}, we already have it", block.index);
return Twin;
}
if let Some(my_block) = self.get_block(block.index) {
return if my_block.hash != block.hash {
warn!("Got forked block {} with hash {:?} instead of {:?}", block.index, block.hash, last_block.hash);
Fork
} else {
warn!("Ignoring block {}, we already have it", block.index);
Twin
};
}
}
if last_block.index == block.index && last_block.hash != block.hash {
warn!("Got forked block {} with hash {:?} instead of {:?}", block.index, block.hash, last_block.hash);
return Fork;
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;
}
}
}
}
}
@ -302,6 +397,38 @@ impl Blockchain {
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;
}
if block.index < LOCKER_BLOCK_START {
return None;
}
match self.get_last_full_block() {
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 tail = block.hash.get_tail_u64();
let start_index = 1 + ((tail + tail * intervals) % (block.index - 2));
for index in start_index..block.index {
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);
}
}
}
None
}
fn get_block_from_statement(statement: &mut Statement) -> Option<Block> {
let index = statement.read::<i64>(0).unwrap() as u64;
let timestamp = statement.read::<i64>(1).unwrap();
@ -324,7 +451,7 @@ pub enum BlockQuality {
Twin,
Future,
Bad,
Fork
Fork,
}
pub fn check_block_hash(block: &Block) -> bool {
@ -337,7 +464,6 @@ pub fn check_block_hash(block: &Block) -> bool {
pub fn check_block_signature(block: &Block) -> bool {
let mut copy = block.clone();
copy.signature = Bytes::zero64();
let data = serde_json::to_string(&copy).unwrap();
Keystore::check(data.as_bytes(), copy.pub_key.as_bytes(), block.signature.as_bytes())
copy.signature = Bytes::default();
Keystore::check(&copy.as_bytes(), copy.pub_key.as_bytes(), block.signature.as_bytes())
}

@ -1,2 +1,6 @@
pub const BLOCK_DIFFICULTY: u32 = 24;
pub const BLOCK_DIFFICULTY: u32 = 20;
pub const LOCKER_DIFFICULTY: u32 = 16;
pub const CHAIN_VERSION: u32 = 1;
pub const LOCKER_BLOCK_START: u64 = 5;
pub const LOCKER_BLOCK_COUNT: u64 = 3;
pub const LOCKER_BLOCK_INTERVAL: i64 = 300;

@ -6,6 +6,8 @@ use std::io::Write;
use std::path::Path;
use std::sync::{LockResult, RwLock, RwLockReadGuard, RwLockWriteGuard};
#[allow(unused_imports)]
use log::{trace, debug, info, warn, error};
use derive_more::{Display, From, Error};
use crate::dns::buffer::{PacketBuffer, StreamPacketBuffer, VectorPacketBuffer};
@ -71,7 +73,13 @@ impl<'a> Zones {
}
pub fn load(&mut self) -> Result<()> {
let zones_dir = Path::new("zones").read_dir()?;
let zones_dir = match Path::new("zones").read_dir() {
Ok(result) => { result }
Err(_) => {
debug!("Authority dir (zones) not found, skipping.");
return Ok(());
}
};
for wrapped_filename in zones_dir {
let filename = match wrapped_filename {

@ -1,6 +1,5 @@
//! The `ServerContext in this thread holds the common state across the server
use std::fs;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
@ -70,7 +69,7 @@ impl ServerContext {
authority: Authority::new(),
cache: SynchronizedCache::new(),
filters: Vec::new(),
client: Box::new(DnsNetworkClient::new(34255)),
client: Box::new(DnsNetworkClient::new(10000 + (rand::random::<u16>() % 20000))),
dns_host: String::from("0.0.0.0"),
dns_port: 53,
api_port: 5380,
@ -88,9 +87,6 @@ impl ServerContext {
}
pub fn initialize(&mut self) -> Result<()> {
// Create zones directory if it doesn't exist
fs::create_dir_all(self.zones_dir)?;
// Start UDP client thread
self.client.run()?;

@ -1,3 +1,5 @@
use crate::Bytes;
#[derive(Clone, PartialEq, Debug)]
pub enum Event {
MinerStarted,
@ -10,6 +12,7 @@ pub enum Event {
NewBlockReceived,
BlockchainChanged,
ActionStopMining,
ActionMineLocker { index: u64, hash: Bytes },
NetworkStatus { nodes: usize, blocks: u64 },
Syncing { have: u64, height: u64 },
SyncFinished,

@ -18,6 +18,7 @@ use log::{trace, debug, info, warn, error};
use crate::hash_is_good;
use std::cmp::Ordering;
use num_bigint::BigUint;
use std::convert::TryInto;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Keystore {
@ -137,6 +138,12 @@ impl Bytes {
crate::utils::to_hex(&self.data)
}
pub fn get_tail_u64(&self) -> u64 {
let index = self.data.len() - 8;
let bytes: [u8; 8] = self.data[index..].try_into().unwrap();
u64::from_be_bytes(bytes)
}
pub fn zero32() -> Self {
Bytes { data: [0u8; 32].to_vec() }
}
@ -227,7 +234,7 @@ impl<'dd> Deserialize<'dd> for Bytes {
#[cfg(test)]
mod tests {
use crate::Keystore;
use crate::{Keystore, Bytes};
#[test]
pub fn test_signature() {
@ -237,4 +244,10 @@ mod tests {
let signature = keystore.sign(data);
assert!(Keystore::check(data, keystore.get_public().as_bytes(), &signature), "Wrong signature!")
}
#[test]
pub fn test_tail_bytes() {
let bytes = Bytes::new(vec![0,255,255,255]);
assert_eq!(bytes.get_tail_u64(), 16777215);
}
}

@ -4,6 +4,8 @@
#![windows_subsystem = "windows"]
extern crate web_view;
extern crate tinyfiledialogs as tfd;
extern crate serde;
extern crate serde_json;
use std::env;
use std::sync::{Arc, Mutex};
@ -32,11 +34,10 @@ use alfis::dns::server::{DnsServer, DnsUdpServer, DnsTcpServer};
use alfis::dns::protocol::DnsRecord;
use alfis::blockchain::filter::BlockchainFilter;
extern crate serde;
extern crate serde_json;
const KEYSTORE_DIFFICULTY: usize = 24;
const SETTINGS_FILENAME: &str = "alfis.cfg";
const LOG_TARGET_MAIN: &str = "alfis::Main";
const LOG_TARGET_UI: &str = "alfis::UI";
fn main() {
// When linked with the windows subsystem windows won't automatically attach
@ -46,8 +47,6 @@ fn main() {
AttachConsole(ATTACH_PARENT_PROCESS);
}
let version = env!("CARGO_PKG_VERSION");
println!("Starting ALFIS {}", version);
let args: Vec<String> = env::args().collect();
let program = args[0].clone();
@ -56,6 +55,7 @@ fn main() {
opts.optflag("n", "nogui", "Run without graphic user interface");
opts.optflag("v", "verbose", "Show more debug messages");
opts.optflag("d", "debug", "Show trace messages, more than debug");
opts.optflag("l", "list", "List blocks from DB and exit");
opts.optopt("c", "config", "Path to config file", "");
let opt_matches = match opts.parse(&args[1..]) {
@ -83,19 +83,29 @@ fn main() {
Some(path) => { path }
};
SimpleLogger::new().with_level(level).init().unwrap();
info!(target: LOG_TARGET_MAIN, "Starting ALFIS {}", env!("CARGO_PKG_VERSION"));
let settings = Settings::load(&config_name).expect("Error loading settings");
let keystore: Keystore = match Keystore::from_file(&settings.key_file, "") {
None => {
warn!("Generated temporary keystore. Please, generate full-privileged keys.");
warn!(target: LOG_TARGET_MAIN, "Generated temporary keystore. Please, generate full-privileged keys.");
Keystore::new()
}
Some(keystore) => { keystore }
};
let blockchain: Blockchain = Blockchain::new(&settings);
match blockchain.get_block(0) {
None => { info!("No blocks found in DB"); }
Some(block) => { info!("Loaded DB with origin {:?}", &block.hash); }
if opt_matches.opt_present("l") {
for i in 1..(blockchain.height() + 1) {
if let Some(block) = blockchain.get_block(i) {
info!(target: LOG_TARGET_MAIN, "{:?}", &block);
}
}
return;
}
match blockchain.get_block(1) {
None => { info!(target: LOG_TARGET_MAIN, "No blocks found in DB"); }
Some(block) => { trace!(target: LOG_TARGET_MAIN, "Loaded DB with origin {:?}", &block.hash); }
}
let settings_copy = settings.clone();
let context: Arc<Mutex<Context>> = Arc::new(Mutex::new(Context::new(settings, keystore, blockchain)));
@ -131,14 +141,14 @@ fn start_dns_server(context: &Arc<Mutex<Context>>, settings: &Settings) {
if server_context.enable_udp {
let udp_server = DnsUdpServer::new(server_context.clone(), 20);
if let Err(e) = udp_server.run_server() {
error!("Failed to bind UDP listener: {:?}", e);
error!(target: LOG_TARGET_MAIN, "Failed to bind UDP listener: {:?}", e);
}
}
if server_context.enable_tcp {
let tcp_server = DnsTcpServer::new(server_context.clone(), 20);
if let Err(e) = tcp_server.run_server() {
error!("Failed to bind TCP listener: {:?}", e);
error!(target: LOG_TARGET_MAIN, "Failed to bind TCP listener: {:?}", e);
}
}
}
@ -172,7 +182,7 @@ fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
.user_data(())
.invoke_handler(|web_view, arg| {
use Cmd::*;
debug!("Command {}", arg);
debug!(target: LOG_TARGET_UI, "Command {}", arg);
match serde_json::from_str(arg).unwrap() {
Loaded => {
web_view.eval("showMiningIndicator(false, false);").expect("Error evaluating!");
@ -180,7 +190,7 @@ fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
let mut status = Status::new();
let mut c = context.lock().unwrap();
c.bus.register(move |_uuid, e| {
debug!("Got event from bus {:?}", &e);
debug!(target: LOG_TARGET_UI, "Got event from bus {:?}", &e);
let eval = match e {
Event::KeyCreated { path, public } => { format!("keystoreChanged('{}', '{}');", &path, &public) }
Event::KeyLoaded { path, public } => { format!("keystoreChanged('{}', '{}');", &path, &public) }
@ -230,7 +240,7 @@ fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
};
if !eval.is_empty() {
debug!("Evaluating {}", &eval);
debug!(target: LOG_TARGET_UI, "Evaluating {}", &eval);
handle.dispatch(move |web_view| {
web_view.eval(&eval.replace("\\", "\\\\"))
}).expect("Error dispatching!");
@ -238,7 +248,7 @@ fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
true
});
let eval = format!("keystoreChanged('{}', '{}');", c.keystore.get_path(), &c.keystore.get_public().to_string());
debug!("Evaluating {}", &eval);
debug!(target: LOG_TARGET_UI, "Evaluating {}", &eval);
web_view.eval(&eval.replace("\\", "\\\\")).expect("Error evaluating!");
}
LoadKey {} => {
@ -248,10 +258,10 @@ fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
Some(file_name) => {
match Keystore::from_file(&file_name, "") {
None => {
error!("Error loading keystore '{}'!", &file_name);
error!(target: LOG_TARGET_UI, "Error loading keystore '{}'!", &file_name);
}
Some(keystore) => {
info!("Loaded keystore with key: {:?}", &keystore.get_public());
info!(target: LOG_TARGET_UI, "Loaded keystore with key: {:?}", &keystore.get_public());
let mut c = context.lock().unwrap();
c.bus.post(Event::KeyLoaded { path: keystore.get_path().to_owned(), public: keystore.get_public().to_string() });
c.set_keystore(keystore);
@ -272,7 +282,7 @@ fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
let path = new_path.clone();
let public = c.keystore.get_public().to_string();
c.keystore.save(&new_path, "");
info!("Key file saved to {}", &path);
info!(target: LOG_TARGET_UI, "Key file saved to {}", &path);
c.bus.post(Event::KeySaved { path, public });
}
}
@ -284,7 +294,7 @@ fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
web_view.eval(&format!("domainAvailable({})", available)).expect("Error evaluating!");
}
CreateDomain { name, records, .. } => {
debug!("Got records: {}", records);
debug!(target: LOG_TARGET_UI, "Got records: {}", records);
let name = name.to_lowercase();
if !check_domain(&name, true) {
return Ok(());
@ -302,13 +312,13 @@ fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
if transaction.pub_key == keystore.get_public() {
create_domain(miner.clone(), name, records, &keystore);
} else {
warn!("Tried to mine not owned domain!");
warn!(target: LOG_TARGET_UI, "Tried to mine not owned domain!");
let _ = web_view.eval(&format!("showWarning('{}');", "You cannot change domain that you don't own!"));
}
}
}
} else {
warn!("Error in DNS records for domain!");
warn!(target: LOG_TARGET_UI, "Error in DNS records for domain!");
let _ = web_view.eval(&format!("showWarning('{}');", "Something wrong with your records! Please, correct the error and try again."));
}
}
@ -340,7 +350,7 @@ fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
if transaction.pub_key == keystore.get_public() {
create_domain(miner.clone(), name, data, &keystore);
} else {
warn!("Tried to mine not owned domain!");
warn!(target: LOG_TARGET_UI, "Tried to mine not owned domain!");
let _ = web_view.eval(&format!("showWarning('{}');", "You cannot change domain that you don't own!"));
}
}
@ -364,14 +374,14 @@ fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
loop {
match interface.step() {
None => {
info!("Interface closed, exiting");
info!(target: LOG_TARGET_UI, "Interface closed, exiting");
break;
}
Some(result) => {
match result {
Ok(_) => {}
Err(_) => {
error!("Something wrong with webview, exiting");
error!(target: LOG_TARGET_UI, "Something wrong with webview, exiting");
break;
}
}
@ -393,9 +403,9 @@ fn create_genesis(miner: Arc<Mutex<Miner>>, keystore: &Keystore) {
fn create_domain<S: Into<String>>(miner: Arc<Mutex<Miner>>, name: S, data: S, keystore: &Keystore) {
let name = name.into();
info!("Generating domain or zone {}", name);
info!(target: LOG_TARGET_UI, "Generating domain or zone {}", name);
//let tags_vector: Vec<String> = tags.into().trim().split(",").map(|s| s.trim()).map(String::from).collect();
let transaction = Transaction::from_str(name.into(), "domain".into(), data.into(), keystore.get_public().clone());
let transaction = Transaction::from_str(name.into(), "dns".into(), data.into(), keystore.get_public().clone());
let block = Block::new(Some(transaction), keystore.get_public(), Bytes::default());
let mut miner_guard = miner.lock().unwrap();
miner_guard.add_block(block);
@ -413,10 +423,10 @@ fn create_key(context: Arc<Mutex<Context>>) {
miners_count.fetch_add(1, Ordering::Relaxed);
match generate_key(KEYSTORE_DIFFICULTY, mining.clone()) {
None => {
debug!("Keystore mining finished");
debug!(target: LOG_TARGET_UI, "Keystore mining finished");
}
Some(keystore) => {
info!("Key mined successfully: {:?}", &keystore.get_public());
info!(target: LOG_TARGET_UI, "Key mined successfully: {:?}", &keystore.get_public());
let mut c = context.lock().unwrap();
mining.store(false, Ordering::Relaxed);
c.bus.post(Event::KeyCreated { path: keystore.get_path().to_owned(), public: keystore.get_public().to_string() });
@ -444,7 +454,7 @@ fn generate_key(difficulty: usize, mining: Arc<AtomicBool>) -> Option<Keystore>
rng.fill_bytes(&mut buf);
let keystore = Keystore::from_bytes(&buf);
if keystore.hash_is_good(difficulty) {
info!("Generated keypair: {:?}", &keystore);
info!(target: LOG_TARGET_UI, "Generated keypair: {:?}", &keystore);
return Some(keystore);
}
if !mining.load(Ordering::Relaxed) {

@ -12,7 +12,7 @@ use num_cpus;
use crate::{Block, Bytes, Context, hash_is_good};
use crate::blockchain::blockchain::BlockQuality;
use crate::blockchain::{BLOCK_DIFFICULTY, CHAIN_VERSION};
use crate::blockchain::{BLOCK_DIFFICULTY, CHAIN_VERSION, LOCKER_DIFFICULTY};
use crate::event::Event;
pub struct Miner {
@ -52,11 +52,11 @@ impl Miner {
let mining = self.mining.clone();
let cond_var = self.cond_var.clone();
thread::spawn(move || {
running.store(true, Ordering::Relaxed);
while running.load(Ordering::Relaxed) {
running.store(true, Ordering::SeqCst);
while running.load(Ordering::SeqCst) {
// If some transaction is being mined now, we yield
if mining.load(Ordering::Relaxed) {
thread::sleep(Duration::from_millis(100));
if mining.load(Ordering::SeqCst) {
thread::sleep(Duration::from_millis(1000));
continue;
}
@ -72,9 +72,25 @@ impl Miner {
}
});
let mining = self.mining.clone();
let blocks = self.blocks.clone();
let cond_var = self.cond_var.clone();
self.context.lock().unwrap().bus.register(move |_uuid, e| {
if e == Event::ActionStopMining {
mining.store(false, Ordering::SeqCst);
match e {
Event::NewBlockReceived => {}
Event::BlockchainChanged => {}
Event::ActionStopMining => {
mining.store(false, Ordering::SeqCst);
}
Event::ActionMineLocker { index, hash } => {
if !mining.load(Ordering::SeqCst) {
let mut block = Block::new(None, Bytes::default(), hash);
block.index = index;
blocks.lock().unwrap().push(block);
cond_var.notify_all();
info!("Added a locker block to mine");
}
}
_ => {}
}
true
});
@ -89,12 +105,32 @@ impl Miner {
block.signature = Bytes::default();
block.hash = Bytes::default();
block.version = CHAIN_VERSION;
block.difficulty = BLOCK_DIFFICULTY;
block.index = context.lock().unwrap().blockchain.height();
block.prev_block_hash = match context.lock().unwrap().blockchain.last_block() {
None => { Bytes::default() }
Some(block) => { block.hash }
};
// If this block needs to be a locker
if block.index > 0 && !block.prev_block_hash.is_empty() {
info!("Mining locker block");
block.difficulty = LOCKER_DIFFICULTY;
block.pub_key = context.lock().unwrap().keystore.get_public();
match context.lock().unwrap().blockchain.last_block() {
None => {}
Some(last_block) => {
info!("Last block found");
// If we were doing something else and got new block before we could mine this block
if last_block.index > block.index || last_block.hash != block.prev_block_hash {
warn!("We missed block to lock");
context.lock().unwrap().bus.post(Event::MinerStopped);
mining.store(false, Ordering::SeqCst);
return;
}
}
}
} else {
block.difficulty = BLOCK_DIFFICULTY;
block.index = context.lock().unwrap().blockchain.height() + 1;
block.prev_block_hash = match context.lock().unwrap().blockchain.last_block() {
None => { Bytes::default() }
Some(block) => { block.hash }
};
}
context.lock().unwrap().bus.post(Event::MinerStarted);
let live_threads = Arc::new(AtomicU32::new(0u32));

@ -3,7 +3,7 @@ extern crate serde_json;
use std::{io, thread};
use std::io::{Read, Write};
use std::sync::{Arc, Mutex};
use std::sync::{Arc, Mutex, MutexGuard};
use std::time::{Duration, Instant};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
@ -17,6 +17,7 @@ use crate::{Context, Block, p2p::Message, p2p::State, p2p::Peer, p2p::Peers};
use std::net::{SocketAddr, IpAddr, SocketAddrV4, ToSocketAddrs};
use crate::blockchain::blockchain::BlockQuality;
use crate::blockchain::CHAIN_VERSION;
use chrono::Utc;
const SERVER: Token = Token(0);
const POLL_TIMEOUT: Option<Duration> = Some(Duration::from_millis(3000));
@ -107,6 +108,7 @@ impl Network {
let context = context.lock().unwrap();
(context.blockchain.height(), context.blockchain.last_hash())
};
mine_locker_block(context.clone());
peers.send_pings(poll.registry(), height, hash);
peers.connect_new_peers(poll.registry(), &mut unique_token);
}
@ -293,7 +295,7 @@ fn handle_message(context: Arc<Mutex<Context>>, message: Message, peers: &mut Pe
if peer.is_higher(my_height) {
context.blockchain.update_max_height(height);
context.bus.post(crate::event::Event::Syncing { have: my_height, height});
State::message(Message::GetBlock { index: my_height })
State::message(Message::GetBlock { index: my_height + 1 })
} else {
State::message(Message::GetPeers)
}
@ -306,8 +308,8 @@ fn handle_message(context: Arc<Mutex<Context>>, message: Message, peers: &mut Pe
let peer = peers.get_mut_peer(token).unwrap();
peer.set_height(height);
peer.set_active(true);
if peer.is_higher(my_height) || my_hash.ne(&hash) {
State::message(Message::GetBlock { index: my_height })
if peer.is_higher(my_height) || ( height == my_height && my_hash != hash) {
State::message(Message::GetBlock { index: my_height + 1 })
} else {
State::message(Message::pong(my_height, my_hash))
}
@ -316,12 +318,17 @@ fn handle_message(context: Arc<Mutex<Context>>, message: Message, peers: &mut Pe
let peer = peers.get_mut_peer(token).unwrap();
peer.set_height(height);
peer.set_active(true);
if peer.is_higher(my_height) || my_hash.ne(&hash) {
let is_higher = peer.is_higher(my_height);
let mut context = context.lock().unwrap();
let blocks_count = context.blockchain.height();
context.bus.post(crate::event::Event::NetworkStatus { nodes: peers.get_peers_active_count(), blocks: blocks_count });
if is_higher {
State::message(Message::GetBlock { index: my_height + 1 })
} else if my_hash != hash {
State::message(Message::GetBlock { index: my_height })
} else {
let mut context = context.lock().unwrap();
let blocks_count = context.blockchain.height();
context.bus.post(crate::event::Event::NetworkStatus { nodes: peers.get_peers_active_count(), blocks: blocks_count });
State::idle()
}
}
@ -346,7 +353,10 @@ fn handle_message(context: Arc<Mutex<Context>>, message: Message, peers: &mut Pe
Ok(block) => block,
Err(_) => return State::Error
};
let peer = peers.get_mut_peer(token).unwrap();
peer.set_received_block(block.index);
let context = context.clone();
let peers_count = peers.get_peers_active_count();
thread::spawn(move || {
let mut context = context.lock().unwrap();
let max_height = context.blockchain.max_height();
@ -361,12 +371,17 @@ fn handle_message(context: Arc<Mutex<Context>>, message: Message, peers: &mut Pe
} else {
context.bus.post(crate::event::Event::Syncing { have: my_height, height: max_height});
}
context.bus.post(crate::event::Event::NetworkStatus { nodes: peers_count, blocks: my_height });
}
BlockQuality::Twin => { debug!("Ignoring duplicate block {}", block.index); }
BlockQuality::Future => { debug!("Ignoring future block {}", block.index); }
BlockQuality::Bad => { debug!("Ignoring bad block {} with hash {:?}", block.index, block.hash); }
// TODO deal with forks
BlockQuality::Fork => { debug!("Ignoring forked block {} with hash {:?}", block.index, block.hash); }
BlockQuality::Fork => {
debug!("Ignoring forked block {} with hash {:?}", block.index, block.hash);
//let peer = peers.get_mut_peer(token).unwrap();
//deal_with_fork(context, peer, block);
}
}
});
State::idle()
@ -374,6 +389,71 @@ fn handle_message(context: Arc<Mutex<Context>>, message: Message, peers: &mut Pe
}
}
/// Sends an Event to miner to start mining locker block if "locker" is our public key
fn mine_locker_block(context: Arc<Mutex<Context>>) {
let mut context = context.lock().unwrap();
if let Some(block) = context.blockchain.last_block() {
if block.index < context.blockchain.max_height() {
info!("No locker mining while syncing");
return;
}
match context.blockchain.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 => {}
}
}
}
#[allow(dead_code)]
fn deal_with_fork(context: MutexGuard<Context>, peer: &mut Peer, block: Block) {
peer.add_fork_block(block);
let mut vector: Vec<&Block> = peer.get_fork().values().collect();
vector.sort_by(|a, b| a.index.cmp(&b.index));
if vector[0].index == 0 {
return;
}
if let Some(prev_block) = context.blockchain.get_block(vector[0].index - 1) {
// If this block is not root of the fork (we need to go ~deeper~ more backwards)
if vector[0].prev_block_hash != prev_block.hash {
return;
}
// Okay, prev_block is the common root for our chain and the fork
let mut check_ok = true;
vector.insert(0, &prev_block);
let mut prev_block = &vector[0];
for block in &vector {
if block == prev_block {
continue;
}
if !check_block(block, prev_block) {
check_ok = false;
break;
}
prev_block = block;
}
match check_ok {
true => {
// TODO count fork chain "work" and decide which chain is "better"
}
false => {
warn!("Fork chain is wrong!");
peer.set_state(State::Banned);
}
}
};
}
fn check_block(block: &Block, prev: &Block) -> bool {
prev.index == block.index - 1 && prev.hash == block.prev_block_hash
}
/// Connecting to configured (bootstrap) peers
fn connect_peers(peers_addrs: Vec<String>, poll: &mut Poll, peers: &mut Peers, unique_token: &mut Token) {
for peer in peers_addrs.iter() {

@ -1,6 +1,8 @@
use crate::p2p::State;
use std::net::SocketAddr;
use std::collections::HashMap;
use mio::net::TcpStream;
use crate::p2p::State;
use crate::Block;
#[derive(Debug)]
pub struct Peer {
@ -11,11 +13,13 @@ pub struct Peer {
inbound: bool,
public: bool,
active: bool,
received_block: u64,
fork: HashMap<u64, Block>
}
impl Peer {
pub fn new(addr: SocketAddr, stream: TcpStream, state: State, inbound: bool) -> Self {
Peer { addr, stream, state, height: 0, inbound, public: false, active: false }
Peer { addr, stream, state, height: 0, inbound, public: false, active: false, received_block: 0, fork: HashMap::new() }
}
pub fn get_addr(&self) -> SocketAddr {
@ -46,6 +50,18 @@ impl Peer {
self.height > height
}
pub fn is_lower(&self, height: u64) -> bool {
self.height < height
}
pub fn set_received_block(&mut self, index: u64) {
self.received_block = index;
}
pub fn has_more_blocks(&self, height: u64) -> bool {
self.height > self.received_block && self.height > height && self.get_state().is_idle()
}
pub fn is_public(&self) -> bool {
self.public
}
@ -70,6 +86,14 @@ impl Peer {
self.inbound
}
pub fn add_fork_block(&mut self, block: Block) {
self.fork.insert(block.index, block);
}
pub fn get_fork(&self) -> &HashMap<u64, Block> {
&self.fork
}
/// If loopback address then we care about ip and port.
/// If regular address then we only care about the ip and ignore the port.
pub fn equals(&self, addr: &SocketAddr) -> bool {

@ -157,17 +157,35 @@ impl Peers {
}
}
// If someone has more blocks we sync
if !ping_sent {
let mut rng = rand::thread_rng();
match self.peers
.iter_mut()
.filter_map(|(token, peer)| if peer.get_state().is_idle() && peer.is_higher(height) { Some((token, peer)) } else { None })
.filter_map(|(token, peer)| if peer.has_more_blocks(height) { Some((token, peer)) } else { None })
.choose(&mut rng) {
None => {}
Some((token, peer)) => {
debug!("Found some peer higher than we are, sending block request");
registry.reregister(peer.get_stream(), token.clone(), Interest::WRITABLE).unwrap();
peer.set_state(State::message(Message::GetBlock { index: height }));
peer.set_state(State::message(Message::GetBlock { index: height + 1 }));
ping_sent = true;
}
}
}
// If someone has less blocks (we mined a new block) we send a ping with our height
if !ping_sent {
let mut rng = rand::thread_rng();
match self.peers
.iter_mut()
.filter_map(|(token, peer)| if peer.is_lower(height) && peer.get_state().is_idle() { Some((token, peer)) } else { None })
.choose(&mut rng) {
None => {}
Some((token, peer)) => {
debug!("Found some peer lower than we are, sending ping");
registry.reregister(peer.get_stream(), token.clone(), Interest::WRITABLE).unwrap();
peer.set_state(State::message(Message::Ping { height, hash }));
}
}
}

Loading…
Cancel
Save