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.
This commit is contained in:
@@ -64,7 +64,7 @@ impl Block {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_genesis(&self) -> bool {
|
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> {
|
pub fn as_bytes(&self) -> Vec<u8> {
|
||||||
|
|||||||
+209
-83
@@ -9,18 +9,43 @@ use std::cell::RefCell;
|
|||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use crate::blockchain::transaction::hash_identity;
|
use crate::blockchain::transaction::hash_identity;
|
||||||
use crate::blockchain::blockchain::BlockQuality::*;
|
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 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 {
|
pub struct Blockchain {
|
||||||
origin: Bytes,
|
origin: Bytes,
|
||||||
pub version: u32,
|
pub version: u32,
|
||||||
pub blocks: Vec<Block>,
|
pub blocks: Vec<Block>,
|
||||||
last_block: Option<Block>,
|
last_block: Option<Block>,
|
||||||
|
last_full_block: Option<Block>,
|
||||||
max_height: u64,
|
max_height: u64,
|
||||||
db: Connection,
|
db: Connection,
|
||||||
zones: RefCell<HashSet<String>>
|
zones: RefCell<HashSet<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Blockchain {
|
impl Blockchain {
|
||||||
@@ -28,99 +53,120 @@ impl Blockchain {
|
|||||||
let origin = settings.get_origin();
|
let origin = settings.get_origin();
|
||||||
|
|
||||||
let db = sqlite::open(DB_NAME).expect("Unable to open blockchain DB");
|
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.init_db();
|
||||||
blockchain
|
blockchain
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads options from DB or initializes and writes them to DB if not found
|
/// Reads options from DB or initializes and writes them to DB if not found
|
||||||
fn init_db(&mut self) {
|
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) => {
|
Ok(mut statement) => {
|
||||||
|
let mut result = None;
|
||||||
while statement.next().unwrap() == State::Row {
|
while statement.next().unwrap() == State::Row {
|
||||||
match Self::get_block_from_statement(&mut statement) {
|
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) => {
|
Some(block) => {
|
||||||
info!("Loaded last block: {:?}", &block);
|
debug!("Loaded last block: {:?}", &block);
|
||||||
self.version = block.version;
|
result = Some(block);
|
||||||
self.last_block = Some(block);
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
debug!("Blockchain version from DB = {}", self.version);
|
|
||||||
}
|
}
|
||||||
|
result
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
info!("No blockchain database found. Creating new.");
|
info!("No blockchain database found. Creating new.");
|
||||||
self.db.execute("
|
self.db.execute(SQL_CREATE_TABLES).expect("Error creating blocks table");
|
||||||
CREATE TABLE blocks (
|
None
|
||||||
'id' BIGINT,
|
}
|
||||||
'timestamp' BIGINT,
|
};
|
||||||
'version' TEXT,
|
// If some block loaded we check its version and determine if we need some migration
|
||||||
'difficulty' INTEGER,
|
if let Some(block) = block {
|
||||||
'random' INTEGER,
|
self.max_height = block.index;
|
||||||
'nonce' INTEGER,
|
if self.version > block.version {
|
||||||
'transaction' TEXT,
|
self.migrate_db(block.version, self.version);
|
||||||
'prev_block_hash' BINARY,
|
} else if self.version < block.version {
|
||||||
'hash' BINARY,
|
error!("Version downgrade {}->{} is not supported!", block.version, self.version);
|
||||||
'pub_key' BINARY,
|
panic!();
|
||||||
'signature' BINARY
|
}
|
||||||
);
|
// Cache some info
|
||||||
CREATE INDEX block_index ON blocks (id);
|
self.last_block = Some(block.clone());
|
||||||
CREATE TABLE transactions (id INTEGER PRIMARY KEY AUTOINCREMENT, identity BINARY, confirmation BINARY, method TEXT, data TEXT, pub_key BINARY);
|
if block.transaction.is_some() {
|
||||||
CREATE INDEX ids ON transactions (identity);"
|
self.last_full_block = Some(block);
|
||||||
).expect("Error creating blocks table");
|
} 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) {
|
pub fn add_block(&mut self, block: Block) {
|
||||||
info!("Adding block:\n{:?}", &block);
|
info!("Adding block:\n{:?}", &block);
|
||||||
self.blocks.push(block.clone());
|
self.blocks.push(block.clone());
|
||||||
self.last_block = Some(block.clone());
|
self.last_block = Some(block.clone());
|
||||||
let transaction = block.transaction.clone();
|
if block.transaction.is_some() {
|
||||||
|
self.last_full_block = Some(block.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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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");
|
|
||||||
}
|
}
|
||||||
|
let transaction = block.transaction.clone();
|
||||||
if let Some(transaction) = transaction {
|
if self.add_block_to_table(block).is_ok() {
|
||||||
self.add_transaction(&transaction);
|
if let Some(transaction) = transaction {
|
||||||
|
self.add_transaction_to_table(&transaction).expect("Error adding transaction");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_transaction(&mut self, t: &Transaction) {
|
/// Adds block to blocks table
|
||||||
let mut statement = self.db.prepare("INSERT INTO transactions (identity, confirmation, method, data, pub_key) VALUES (?, ?, ?, ?, ?)").unwrap();
|
fn add_block_to_table(&mut self, block: Block) -> sqlite::Result<State> {
|
||||||
statement.bind(1, t.identity.as_bytes()).expect("Error in bind");
|
let mut statement = self.db.prepare(SQL_ADD_BLOCK)?;
|
||||||
statement.bind(2, t.confirmation.as_bytes()).expect("Error in bind");
|
statement.bind(1, block.index as i64)?;
|
||||||
statement.bind(3, t.method.as_ref() as &str).expect("Error in bind");
|
statement.bind(2, block.timestamp as i64)?;
|
||||||
statement.bind(4, t.data.as_ref() as &str).expect("Error in bind");
|
statement.bind(3, block.version as i64)?;
|
||||||
statement.bind(5, t.pub_key.as_bytes()).expect("Error in bind");
|
statement.bind(4, block.difficulty as i64)?;
|
||||||
statement.next().expect("Error adding transaction to DB");
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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> {
|
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) => {
|
Ok(mut statement) => {
|
||||||
statement.bind(1, index as i64).expect("Error in bind");
|
statement.bind(1, index as i64).expect("Error in bind");
|
||||||
while statement.next().unwrap() == State::Row {
|
while statement.next().unwrap() == State::Row {
|
||||||
@@ -130,10 +176,10 @@ impl Blockchain {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
Some(block) => {
|
Some(block) => {
|
||||||
debug!("Loaded block: {:?}", &block);
|
trace!("Loaded block: {:?}", &block);
|
||||||
Some(block)
|
Some(block)
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
None
|
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 {
|
pub fn is_domain_available(&self, domain: &str, keystore: &Keystore) -> bool {
|
||||||
if domain.is_empty() {
|
if domain.is_empty() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let identity_hash = hash_identity(domain, None);
|
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");
|
statement.bind(1, identity_hash.as_bytes()).expect("Error in bind");
|
||||||
while let State::Row = statement.next().unwrap() {
|
while let State::Row = statement.next().unwrap() {
|
||||||
let pub_key = Bytes::from_bytes(statement.read::<Vec<u8>>(0).unwrap().as_slice());
|
let pub_key = Bytes::from_bytes(statement.read::<Vec<u8>>(0).unwrap().as_slice());
|
||||||
@@ -170,6 +242,7 @@ impl Blockchain {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if some zone exists in our blockchain
|
||||||
pub fn is_zone_in_blockchain(&self, zone: &str) -> bool {
|
pub fn is_zone_in_blockchain(&self, zone: &str) -> bool {
|
||||||
if self.zones.borrow().contains(zone) {
|
if self.zones.borrow().contains(zone) {
|
||||||
return true;
|
return true;
|
||||||
@@ -177,7 +250,7 @@ impl Blockchain {
|
|||||||
|
|
||||||
// Checking for existing zone in DB
|
// Checking for existing zone in DB
|
||||||
let identity_hash = hash_identity(zone, None);
|
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");
|
statement.bind(1, identity_hash.as_bytes()).expect("Error in bind");
|
||||||
while let State::Row = statement.next().unwrap() {
|
while let State::Row = statement.next().unwrap() {
|
||||||
// If there is such a zone
|
// If there is such a zone
|
||||||
@@ -187,13 +260,14 @@ impl Blockchain {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets full Transaction info for any domain. Used by DNS part.
|
||||||
pub fn get_domain_transaction(&self, domain: &str) -> Option<Transaction> {
|
pub fn get_domain_transaction(&self, domain: &str) -> Option<Transaction> {
|
||||||
if domain.is_empty() {
|
if domain.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let identity_hash = hash_identity(domain, 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");
|
statement.bind(1, identity_hash.as_bytes()).expect("Error in bind");
|
||||||
while let State::Row = statement.next().unwrap() {
|
while let State::Row = statement.next().unwrap() {
|
||||||
let identity = Bytes::from_bytes(statement.read::<Vec<u8>>(1).unwrap().as_slice());
|
let identity = Bytes::from_bytes(statement.read::<Vec<u8>>(1).unwrap().as_slice());
|
||||||
@@ -225,7 +299,7 @@ impl Blockchain {
|
|||||||
match self.last_block {
|
match self.last_block {
|
||||||
None => { 0u64 }
|
None => { 0u64 }
|
||||||
Some(ref block) => {
|
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 {
|
pub fn check_new_block(&self, block: &Block) -> BlockQuality {
|
||||||
let timestamp = Utc::now().timestamp();
|
let timestamp = Utc::now().timestamp();
|
||||||
if block.timestamp > timestamp {
|
if block.timestamp > timestamp {
|
||||||
warn!("Ignoring block from the future:\n{:?}", &block);
|
warn!("Ignoring block from the future:\n{:?}", &block);
|
||||||
return Bad;
|
return Bad;
|
||||||
}
|
}
|
||||||
if !hash_is_good(block.hash.as_bytes(), BLOCK_DIFFICULTY as usize) {
|
let difficulty = match block.transaction {
|
||||||
warn!("Ignoring block with low difficulty:\n{:?}", &block);
|
None => { LOCKER_DIFFICULTY }
|
||||||
|
Some(_) => { BLOCK_DIFFICULTY }
|
||||||
|
};
|
||||||
|
if block.difficulty < difficulty {
|
||||||
|
warn!("Block difficulty is lower than needed");
|
||||||
return Bad;
|
return Bad;
|
||||||
}
|
}
|
||||||
if !hash_is_good(block.hash.as_bytes(), block.difficulty as usize) {
|
if !hash_is_good(block.hash.as_bytes(), block.difficulty as usize) {
|
||||||
@@ -264,6 +343,7 @@ impl Blockchain {
|
|||||||
match &self.last_block {
|
match &self.last_block {
|
||||||
None => {
|
None => {
|
||||||
if !block.is_genesis() {
|
if !block.is_genesis() {
|
||||||
|
warn!("Block is from the future, how is this possible?");
|
||||||
return Future;
|
return Future;
|
||||||
}
|
}
|
||||||
if !self.origin.is_zero() && block.hash != self.origin {
|
if !self.origin.is_zero() && block.hash != self.origin {
|
||||||
@@ -277,16 +357,31 @@ impl Blockchain {
|
|||||||
return Bad;
|
return Bad;
|
||||||
}
|
}
|
||||||
if last_block.index + 1 < block.index {
|
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;
|
return Future;
|
||||||
}
|
}
|
||||||
if last_block.index >= block.index && last_block.hash == block.hash {
|
if block.index <= last_block.index {
|
||||||
warn!("Ignoring block {}, we already have it", block.index);
|
if last_block.hash == block.hash {
|
||||||
return Twin;
|
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 {
|
if block.transaction.is_none() {
|
||||||
warn!("Got forked block {} with hash {:?} instead of {:?}", block.index, block.hash, last_block.hash);
|
if let Some(locker) = self.get_block_locker(&last_block, block.timestamp) {
|
||||||
return Fork;
|
if locker != block.pub_key {
|
||||||
|
warn!("Ignoring block {}, as wrong locker", block.index);
|
||||||
|
return Bad;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -302,6 +397,38 @@ impl Blockchain {
|
|||||||
Good
|
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> {
|
fn get_block_from_statement(statement: &mut Statement) -> Option<Block> {
|
||||||
let index = statement.read::<i64>(0).unwrap() as u64;
|
let index = statement.read::<i64>(0).unwrap() as u64;
|
||||||
let timestamp = statement.read::<i64>(1).unwrap();
|
let timestamp = statement.read::<i64>(1).unwrap();
|
||||||
@@ -324,7 +451,7 @@ pub enum BlockQuality {
|
|||||||
Twin,
|
Twin,
|
||||||
Future,
|
Future,
|
||||||
Bad,
|
Bad,
|
||||||
Fork
|
Fork,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_block_hash(block: &Block) -> bool {
|
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 {
|
pub fn check_block_signature(block: &Block) -> bool {
|
||||||
let mut copy = block.clone();
|
let mut copy = block.clone();
|
||||||
copy.signature = Bytes::zero64();
|
copy.signature = Bytes::default();
|
||||||
let data = serde_json::to_string(©).unwrap();
|
Keystore::check(©.as_bytes(), copy.pub_key.as_bytes(), block.signature.as_bytes())
|
||||||
Keystore::check(data.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 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::path::Path;
|
||||||
use std::sync::{LockResult, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
use std::sync::{LockResult, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||||
|
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use log::{trace, debug, info, warn, error};
|
||||||
use derive_more::{Display, From, Error};
|
use derive_more::{Display, From, Error};
|
||||||
|
|
||||||
use crate::dns::buffer::{PacketBuffer, StreamPacketBuffer, VectorPacketBuffer};
|
use crate::dns::buffer::{PacketBuffer, StreamPacketBuffer, VectorPacketBuffer};
|
||||||
@@ -71,7 +73,13 @@ impl<'a> Zones {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn load(&mut self) -> Result<()> {
|
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 {
|
for wrapped_filename in zones_dir {
|
||||||
let filename = match wrapped_filename {
|
let filename = match wrapped_filename {
|
||||||
|
|||||||
+1
-5
@@ -1,6 +1,5 @@
|
|||||||
//! The `ServerContext in this thread holds the common state across the server
|
//! The `ServerContext in this thread holds the common state across the server
|
||||||
|
|
||||||
use std::fs;
|
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@@ -70,7 +69,7 @@ impl ServerContext {
|
|||||||
authority: Authority::new(),
|
authority: Authority::new(),
|
||||||
cache: SynchronizedCache::new(),
|
cache: SynchronizedCache::new(),
|
||||||
filters: Vec::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_host: String::from("0.0.0.0"),
|
||||||
dns_port: 53,
|
dns_port: 53,
|
||||||
api_port: 5380,
|
api_port: 5380,
|
||||||
@@ -88,9 +87,6 @@ impl ServerContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn initialize(&mut self) -> Result<()> {
|
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
|
// Start UDP client thread
|
||||||
self.client.run()?;
|
self.client.run()?;
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use crate::Bytes;
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Debug)]
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
MinerStarted,
|
MinerStarted,
|
||||||
@@ -10,6 +12,7 @@ pub enum Event {
|
|||||||
NewBlockReceived,
|
NewBlockReceived,
|
||||||
BlockchainChanged,
|
BlockchainChanged,
|
||||||
ActionStopMining,
|
ActionStopMining,
|
||||||
|
ActionMineLocker { index: u64, hash: Bytes },
|
||||||
NetworkStatus { nodes: usize, blocks: u64 },
|
NetworkStatus { nodes: usize, blocks: u64 },
|
||||||
Syncing { have: u64, height: u64 },
|
Syncing { have: u64, height: u64 },
|
||||||
SyncFinished,
|
SyncFinished,
|
||||||
|
|||||||
+14
-1
@@ -18,6 +18,7 @@ use log::{trace, debug, info, warn, error};
|
|||||||
use crate::hash_is_good;
|
use crate::hash_is_good;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use num_bigint::BigUint;
|
use num_bigint::BigUint;
|
||||||
|
use std::convert::TryInto;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct Keystore {
|
pub struct Keystore {
|
||||||
@@ -137,6 +138,12 @@ impl Bytes {
|
|||||||
crate::utils::to_hex(&self.data)
|
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 {
|
pub fn zero32() -> Self {
|
||||||
Bytes { data: [0u8; 32].to_vec() }
|
Bytes { data: [0u8; 32].to_vec() }
|
||||||
}
|
}
|
||||||
@@ -227,7 +234,7 @@ impl<'dd> Deserialize<'dd> for Bytes {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::Keystore;
|
use crate::{Keystore, Bytes};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn test_signature() {
|
pub fn test_signature() {
|
||||||
@@ -237,4 +244,10 @@ mod tests {
|
|||||||
let signature = keystore.sign(data);
|
let signature = keystore.sign(data);
|
||||||
assert!(Keystore::check(data, keystore.get_public().as_bytes(), &signature), "Wrong signature!")
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+39
-29
@@ -4,6 +4,8 @@
|
|||||||
#![windows_subsystem = "windows"]
|
#![windows_subsystem = "windows"]
|
||||||
extern crate web_view;
|
extern crate web_view;
|
||||||
extern crate tinyfiledialogs as tfd;
|
extern crate tinyfiledialogs as tfd;
|
||||||
|
extern crate serde;
|
||||||
|
extern crate serde_json;
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
@@ -32,11 +34,10 @@ use alfis::dns::server::{DnsServer, DnsUdpServer, DnsTcpServer};
|
|||||||
use alfis::dns::protocol::DnsRecord;
|
use alfis::dns::protocol::DnsRecord;
|
||||||
use alfis::blockchain::filter::BlockchainFilter;
|
use alfis::blockchain::filter::BlockchainFilter;
|
||||||
|
|
||||||
extern crate serde;
|
|
||||||
extern crate serde_json;
|
|
||||||
|
|
||||||
const KEYSTORE_DIFFICULTY: usize = 24;
|
const KEYSTORE_DIFFICULTY: usize = 24;
|
||||||
const SETTINGS_FILENAME: &str = "alfis.cfg";
|
const SETTINGS_FILENAME: &str = "alfis.cfg";
|
||||||
|
const LOG_TARGET_MAIN: &str = "alfis::Main";
|
||||||
|
const LOG_TARGET_UI: &str = "alfis::UI";
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// When linked with the windows subsystem windows won't automatically attach
|
// When linked with the windows subsystem windows won't automatically attach
|
||||||
@@ -46,8 +47,6 @@ fn main() {
|
|||||||
AttachConsole(ATTACH_PARENT_PROCESS);
|
AttachConsole(ATTACH_PARENT_PROCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
let version = env!("CARGO_PKG_VERSION");
|
|
||||||
println!("Starting ALFIS {}", version);
|
|
||||||
let args: Vec<String> = env::args().collect();
|
let args: Vec<String> = env::args().collect();
|
||||||
let program = args[0].clone();
|
let program = args[0].clone();
|
||||||
|
|
||||||
@@ -56,6 +55,7 @@ fn main() {
|
|||||||
opts.optflag("n", "nogui", "Run without graphic user interface");
|
opts.optflag("n", "nogui", "Run without graphic user interface");
|
||||||
opts.optflag("v", "verbose", "Show more debug messages");
|
opts.optflag("v", "verbose", "Show more debug messages");
|
||||||
opts.optflag("d", "debug", "Show trace messages, more than debug");
|
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", "");
|
opts.optopt("c", "config", "Path to config file", "");
|
||||||
|
|
||||||
let opt_matches = match opts.parse(&args[1..]) {
|
let opt_matches = match opts.parse(&args[1..]) {
|
||||||
@@ -83,19 +83,29 @@ fn main() {
|
|||||||
Some(path) => { path }
|
Some(path) => { path }
|
||||||
};
|
};
|
||||||
SimpleLogger::new().with_level(level).init().unwrap();
|
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 settings = Settings::load(&config_name).expect("Error loading settings");
|
||||||
let keystore: Keystore = match Keystore::from_file(&settings.key_file, "") {
|
let keystore: Keystore = match Keystore::from_file(&settings.key_file, "") {
|
||||||
None => {
|
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()
|
Keystore::new()
|
||||||
}
|
}
|
||||||
Some(keystore) => { keystore }
|
Some(keystore) => { keystore }
|
||||||
};
|
};
|
||||||
let blockchain: Blockchain = Blockchain::new(&settings);
|
let blockchain: Blockchain = Blockchain::new(&settings);
|
||||||
match blockchain.get_block(0) {
|
if opt_matches.opt_present("l") {
|
||||||
None => { info!("No blocks found in DB"); }
|
for i in 1..(blockchain.height() + 1) {
|
||||||
Some(block) => { info!("Loaded DB with origin {:?}", &block.hash); }
|
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 settings_copy = settings.clone();
|
||||||
let context: Arc<Mutex<Context>> = Arc::new(Mutex::new(Context::new(settings, keystore, blockchain)));
|
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 {
|
if server_context.enable_udp {
|
||||||
let udp_server = DnsUdpServer::new(server_context.clone(), 20);
|
let udp_server = DnsUdpServer::new(server_context.clone(), 20);
|
||||||
if let Err(e) = udp_server.run_server() {
|
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 {
|
if server_context.enable_tcp {
|
||||||
let tcp_server = DnsTcpServer::new(server_context.clone(), 20);
|
let tcp_server = DnsTcpServer::new(server_context.clone(), 20);
|
||||||
if let Err(e) = tcp_server.run_server() {
|
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(())
|
.user_data(())
|
||||||
.invoke_handler(|web_view, arg| {
|
.invoke_handler(|web_view, arg| {
|
||||||
use Cmd::*;
|
use Cmd::*;
|
||||||
debug!("Command {}", arg);
|
debug!(target: LOG_TARGET_UI, "Command {}", arg);
|
||||||
match serde_json::from_str(arg).unwrap() {
|
match serde_json::from_str(arg).unwrap() {
|
||||||
Loaded => {
|
Loaded => {
|
||||||
web_view.eval("showMiningIndicator(false, false);").expect("Error evaluating!");
|
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 status = Status::new();
|
||||||
let mut c = context.lock().unwrap();
|
let mut c = context.lock().unwrap();
|
||||||
c.bus.register(move |_uuid, e| {
|
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 {
|
let eval = match e {
|
||||||
Event::KeyCreated { path, public } => { format!("keystoreChanged('{}', '{}');", &path, &public) }
|
Event::KeyCreated { path, public } => { format!("keystoreChanged('{}', '{}');", &path, &public) }
|
||||||
Event::KeyLoaded { 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() {
|
if !eval.is_empty() {
|
||||||
debug!("Evaluating {}", &eval);
|
debug!(target: LOG_TARGET_UI, "Evaluating {}", &eval);
|
||||||
handle.dispatch(move |web_view| {
|
handle.dispatch(move |web_view| {
|
||||||
web_view.eval(&eval.replace("\\", "\\\\"))
|
web_view.eval(&eval.replace("\\", "\\\\"))
|
||||||
}).expect("Error dispatching!");
|
}).expect("Error dispatching!");
|
||||||
@@ -238,7 +248,7 @@ fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
|
|||||||
true
|
true
|
||||||
});
|
});
|
||||||
let eval = format!("keystoreChanged('{}', '{}');", c.keystore.get_path(), &c.keystore.get_public().to_string());
|
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!");
|
web_view.eval(&eval.replace("\\", "\\\\")).expect("Error evaluating!");
|
||||||
}
|
}
|
||||||
LoadKey {} => {
|
LoadKey {} => {
|
||||||
@@ -248,10 +258,10 @@ fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
|
|||||||
Some(file_name) => {
|
Some(file_name) => {
|
||||||
match Keystore::from_file(&file_name, "") {
|
match Keystore::from_file(&file_name, "") {
|
||||||
None => {
|
None => {
|
||||||
error!("Error loading keystore '{}'!", &file_name);
|
error!(target: LOG_TARGET_UI, "Error loading keystore '{}'!", &file_name);
|
||||||
}
|
}
|
||||||
Some(keystore) => {
|
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();
|
let mut c = context.lock().unwrap();
|
||||||
c.bus.post(Event::KeyLoaded { path: keystore.get_path().to_owned(), public: keystore.get_public().to_string() });
|
c.bus.post(Event::KeyLoaded { path: keystore.get_path().to_owned(), public: keystore.get_public().to_string() });
|
||||||
c.set_keystore(keystore);
|
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 path = new_path.clone();
|
||||||
let public = c.keystore.get_public().to_string();
|
let public = c.keystore.get_public().to_string();
|
||||||
c.keystore.save(&new_path, "");
|
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 });
|
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!");
|
web_view.eval(&format!("domainAvailable({})", available)).expect("Error evaluating!");
|
||||||
}
|
}
|
||||||
CreateDomain { name, records, .. } => {
|
CreateDomain { name, records, .. } => {
|
||||||
debug!("Got records: {}", records);
|
debug!(target: LOG_TARGET_UI, "Got records: {}", records);
|
||||||
let name = name.to_lowercase();
|
let name = name.to_lowercase();
|
||||||
if !check_domain(&name, true) {
|
if !check_domain(&name, true) {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@@ -302,13 +312,13 @@ fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
|
|||||||
if transaction.pub_key == keystore.get_public() {
|
if transaction.pub_key == keystore.get_public() {
|
||||||
create_domain(miner.clone(), name, records, &keystore);
|
create_domain(miner.clone(), name, records, &keystore);
|
||||||
} else {
|
} 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!"));
|
let _ = web_view.eval(&format!("showWarning('{}');", "You cannot change domain that you don't own!"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} 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."));
|
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() {
|
if transaction.pub_key == keystore.get_public() {
|
||||||
create_domain(miner.clone(), name, data, &keystore);
|
create_domain(miner.clone(), name, data, &keystore);
|
||||||
} else {
|
} 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!"));
|
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 {
|
loop {
|
||||||
match interface.step() {
|
match interface.step() {
|
||||||
None => {
|
None => {
|
||||||
info!("Interface closed, exiting");
|
info!(target: LOG_TARGET_UI, "Interface closed, exiting");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Some(result) => {
|
Some(result) => {
|
||||||
match result {
|
match result {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
error!("Something wrong with webview, exiting");
|
error!(target: LOG_TARGET_UI, "Something wrong with webview, exiting");
|
||||||
break;
|
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) {
|
fn create_domain<S: Into<String>>(miner: Arc<Mutex<Miner>>, name: S, data: S, keystore: &Keystore) {
|
||||||
let name = name.into();
|
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 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 block = Block::new(Some(transaction), keystore.get_public(), Bytes::default());
|
||||||
let mut miner_guard = miner.lock().unwrap();
|
let mut miner_guard = miner.lock().unwrap();
|
||||||
miner_guard.add_block(block);
|
miner_guard.add_block(block);
|
||||||
@@ -413,10 +423,10 @@ fn create_key(context: Arc<Mutex<Context>>) {
|
|||||||
miners_count.fetch_add(1, Ordering::Relaxed);
|
miners_count.fetch_add(1, Ordering::Relaxed);
|
||||||
match generate_key(KEYSTORE_DIFFICULTY, mining.clone()) {
|
match generate_key(KEYSTORE_DIFFICULTY, mining.clone()) {
|
||||||
None => {
|
None => {
|
||||||
debug!("Keystore mining finished");
|
debug!(target: LOG_TARGET_UI, "Keystore mining finished");
|
||||||
}
|
}
|
||||||
Some(keystore) => {
|
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();
|
let mut c = context.lock().unwrap();
|
||||||
mining.store(false, Ordering::Relaxed);
|
mining.store(false, Ordering::Relaxed);
|
||||||
c.bus.post(Event::KeyCreated { path: keystore.get_path().to_owned(), public: keystore.get_public().to_string() });
|
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);
|
rng.fill_bytes(&mut buf);
|
||||||
let keystore = Keystore::from_bytes(&buf);
|
let keystore = Keystore::from_bytes(&buf);
|
||||||
if keystore.hash_is_good(difficulty) {
|
if keystore.hash_is_good(difficulty) {
|
||||||
info!("Generated keypair: {:?}", &keystore);
|
info!(target: LOG_TARGET_UI, "Generated keypair: {:?}", &keystore);
|
||||||
return Some(keystore);
|
return Some(keystore);
|
||||||
}
|
}
|
||||||
if !mining.load(Ordering::Relaxed) {
|
if !mining.load(Ordering::Relaxed) {
|
||||||
|
|||||||
+49
-13
@@ -12,7 +12,7 @@ use num_cpus;
|
|||||||
|
|
||||||
use crate::{Block, Bytes, Context, hash_is_good};
|
use crate::{Block, Bytes, Context, hash_is_good};
|
||||||
use crate::blockchain::blockchain::BlockQuality;
|
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;
|
use crate::event::Event;
|
||||||
|
|
||||||
pub struct Miner {
|
pub struct Miner {
|
||||||
@@ -52,11 +52,11 @@ impl Miner {
|
|||||||
let mining = self.mining.clone();
|
let mining = self.mining.clone();
|
||||||
let cond_var = self.cond_var.clone();
|
let cond_var = self.cond_var.clone();
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
running.store(true, Ordering::Relaxed);
|
running.store(true, Ordering::SeqCst);
|
||||||
while running.load(Ordering::Relaxed) {
|
while running.load(Ordering::SeqCst) {
|
||||||
// If some transaction is being mined now, we yield
|
// If some transaction is being mined now, we yield
|
||||||
if mining.load(Ordering::Relaxed) {
|
if mining.load(Ordering::SeqCst) {
|
||||||
thread::sleep(Duration::from_millis(100));
|
thread::sleep(Duration::from_millis(1000));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,9 +72,25 @@ impl Miner {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
let mining = self.mining.clone();
|
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| {
|
self.context.lock().unwrap().bus.register(move |_uuid, e| {
|
||||||
if e == Event::ActionStopMining {
|
match e {
|
||||||
mining.store(false, Ordering::SeqCst);
|
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
|
true
|
||||||
});
|
});
|
||||||
@@ -89,12 +105,32 @@ impl Miner {
|
|||||||
block.signature = Bytes::default();
|
block.signature = Bytes::default();
|
||||||
block.hash = Bytes::default();
|
block.hash = Bytes::default();
|
||||||
block.version = CHAIN_VERSION;
|
block.version = CHAIN_VERSION;
|
||||||
block.difficulty = BLOCK_DIFFICULTY;
|
// If this block needs to be a locker
|
||||||
block.index = context.lock().unwrap().blockchain.height();
|
if block.index > 0 && !block.prev_block_hash.is_empty() {
|
||||||
block.prev_block_hash = match context.lock().unwrap().blockchain.last_block() {
|
info!("Mining locker block");
|
||||||
None => { Bytes::default() }
|
block.difficulty = LOCKER_DIFFICULTY;
|
||||||
Some(block) => { block.hash }
|
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);
|
context.lock().unwrap().bus.post(Event::MinerStarted);
|
||||||
let live_threads = Arc::new(AtomicU32::new(0u32));
|
let live_threads = Arc::new(AtomicU32::new(0u32));
|
||||||
|
|||||||
+89
-9
@@ -3,7 +3,7 @@ extern crate serde_json;
|
|||||||
|
|
||||||
use std::{io, thread};
|
use std::{io, thread};
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex, MutexGuard};
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
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 std::net::{SocketAddr, IpAddr, SocketAddrV4, ToSocketAddrs};
|
||||||
use crate::blockchain::blockchain::BlockQuality;
|
use crate::blockchain::blockchain::BlockQuality;
|
||||||
use crate::blockchain::CHAIN_VERSION;
|
use crate::blockchain::CHAIN_VERSION;
|
||||||
|
use chrono::Utc;
|
||||||
|
|
||||||
const SERVER: Token = Token(0);
|
const SERVER: Token = Token(0);
|
||||||
const POLL_TIMEOUT: Option<Duration> = Some(Duration::from_millis(3000));
|
const POLL_TIMEOUT: Option<Duration> = Some(Duration::from_millis(3000));
|
||||||
@@ -107,6 +108,7 @@ impl Network {
|
|||||||
let context = context.lock().unwrap();
|
let context = context.lock().unwrap();
|
||||||
(context.blockchain.height(), context.blockchain.last_hash())
|
(context.blockchain.height(), context.blockchain.last_hash())
|
||||||
};
|
};
|
||||||
|
mine_locker_block(context.clone());
|
||||||
peers.send_pings(poll.registry(), height, hash);
|
peers.send_pings(poll.registry(), height, hash);
|
||||||
peers.connect_new_peers(poll.registry(), &mut unique_token);
|
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) {
|
if peer.is_higher(my_height) {
|
||||||
context.blockchain.update_max_height(height);
|
context.blockchain.update_max_height(height);
|
||||||
context.bus.post(crate::event::Event::Syncing { have: my_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 {
|
} else {
|
||||||
State::message(Message::GetPeers)
|
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();
|
let peer = peers.get_mut_peer(token).unwrap();
|
||||||
peer.set_height(height);
|
peer.set_height(height);
|
||||||
peer.set_active(true);
|
peer.set_active(true);
|
||||||
if peer.is_higher(my_height) || my_hash.ne(&hash) {
|
if peer.is_higher(my_height) || ( height == my_height && my_hash != hash) {
|
||||||
State::message(Message::GetBlock { index: my_height })
|
State::message(Message::GetBlock { index: my_height + 1 })
|
||||||
} else {
|
} else {
|
||||||
State::message(Message::pong(my_height, my_hash))
|
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();
|
let peer = peers.get_mut_peer(token).unwrap();
|
||||||
peer.set_height(height);
|
peer.set_height(height);
|
||||||
peer.set_active(true);
|
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 })
|
State::message(Message::GetBlock { index: my_height })
|
||||||
} else {
|
} 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()
|
State::idle()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -346,7 +353,10 @@ fn handle_message(context: Arc<Mutex<Context>>, message: Message, peers: &mut Pe
|
|||||||
Ok(block) => block,
|
Ok(block) => block,
|
||||||
Err(_) => return State::Error
|
Err(_) => return State::Error
|
||||||
};
|
};
|
||||||
|
let peer = peers.get_mut_peer(token).unwrap();
|
||||||
|
peer.set_received_block(block.index);
|
||||||
let context = context.clone();
|
let context = context.clone();
|
||||||
|
let peers_count = peers.get_peers_active_count();
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let mut context = context.lock().unwrap();
|
let mut context = context.lock().unwrap();
|
||||||
let max_height = context.blockchain.max_height();
|
let max_height = context.blockchain.max_height();
|
||||||
@@ -361,12 +371,17 @@ fn handle_message(context: Arc<Mutex<Context>>, message: Message, peers: &mut Pe
|
|||||||
} else {
|
} else {
|
||||||
context.bus.post(crate::event::Event::Syncing { have: my_height, height: max_height});
|
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::Twin => { debug!("Ignoring duplicate block {}", block.index); }
|
||||||
BlockQuality::Future => { debug!("Ignoring future block {}", block.index); }
|
BlockQuality::Future => { debug!("Ignoring future block {}", block.index); }
|
||||||
BlockQuality::Bad => { debug!("Ignoring bad block {} with hash {:?}", block.index, block.hash); }
|
BlockQuality::Bad => { debug!("Ignoring bad block {} with hash {:?}", block.index, block.hash); }
|
||||||
// TODO deal with forks
|
// 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()
|
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
|
/// Connecting to configured (bootstrap) peers
|
||||||
fn connect_peers(peers_addrs: Vec<String>, poll: &mut Poll, peers: &mut Peers, unique_token: &mut Token) {
|
fn connect_peers(peers_addrs: Vec<String>, poll: &mut Poll, peers: &mut Peers, unique_token: &mut Token) {
|
||||||
for peer in peers_addrs.iter() {
|
for peer in peers_addrs.iter() {
|
||||||
|
|||||||
+26
-2
@@ -1,6 +1,8 @@
|
|||||||
use crate::p2p::State;
|
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
use std::collections::HashMap;
|
||||||
use mio::net::TcpStream;
|
use mio::net::TcpStream;
|
||||||
|
use crate::p2p::State;
|
||||||
|
use crate::Block;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Peer {
|
pub struct Peer {
|
||||||
@@ -11,11 +13,13 @@ pub struct Peer {
|
|||||||
inbound: bool,
|
inbound: bool,
|
||||||
public: bool,
|
public: bool,
|
||||||
active: bool,
|
active: bool,
|
||||||
|
received_block: u64,
|
||||||
|
fork: HashMap<u64, Block>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Peer {
|
impl Peer {
|
||||||
pub fn new(addr: SocketAddr, stream: TcpStream, state: State, inbound: bool) -> Self {
|
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 {
|
pub fn get_addr(&self) -> SocketAddr {
|
||||||
@@ -46,6 +50,18 @@ impl Peer {
|
|||||||
self.height > height
|
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 {
|
pub fn is_public(&self) -> bool {
|
||||||
self.public
|
self.public
|
||||||
}
|
}
|
||||||
@@ -70,6 +86,14 @@ impl Peer {
|
|||||||
self.inbound
|
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 loopback address then we care about ip and port.
|
||||||
/// If regular address then we only care about the ip and ignore the port.
|
/// If regular address then we only care about the ip and ignore the port.
|
||||||
pub fn equals(&self, addr: &SocketAddr) -> bool {
|
pub fn equals(&self, addr: &SocketAddr) -> bool {
|
||||||
|
|||||||
+20
-2
@@ -157,17 +157,35 @@ impl Peers {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If someone has more blocks we sync
|
||||||
if !ping_sent {
|
if !ping_sent {
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
match self.peers
|
match self.peers
|
||||||
.iter_mut()
|
.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) {
|
.choose(&mut rng) {
|
||||||
None => {}
|
None => {}
|
||||||
Some((token, peer)) => {
|
Some((token, peer)) => {
|
||||||
debug!("Found some peer higher than we are, sending block request");
|
debug!("Found some peer higher than we are, sending block request");
|
||||||
registry.reregister(peer.get_stream(), token.clone(), Interest::WRITABLE).unwrap();
|
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 }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user