Refactored Chain a lot to make it possible to test.
Added a CPU hard test for Chain, checking all current DB. Added an option to config file to set a count of checked top blocks on start.
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "alfis"
|
||||
version = "0.4.30"
|
||||
version = "0.4.31"
|
||||
authors = ["Revertron <alfis@revertron.com>"]
|
||||
edition = "2018"
|
||||
build = "build.rs"
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
origin = "0AE588D62D710422A7972EA1E8A659CC8E93DB59489ACE32C499CD279B000000"
|
||||
# A path to your key file to load automatically
|
||||
key_file = "default.key"
|
||||
# How many last blocks to check on start
|
||||
check_blocks = 8
|
||||
|
||||
# Network settings
|
||||
[net]
|
||||
|
||||
+118
-110
@@ -8,7 +8,7 @@ use chrono::Utc;
|
||||
use log::{debug, error, info, trace, warn};
|
||||
use sqlite::{Connection, State, Statement};
|
||||
|
||||
use crate::{Block, Bytes, Keystore, Transaction, check_domain, get_domain_zone, is_yggdrasil_record, from_hex};
|
||||
use crate::{Block, Bytes, Keystore, Transaction, check_domain, get_domain_zone, is_yggdrasil_record};
|
||||
use crate::commons::constants::*;
|
||||
use crate::blockchain::types::{BlockQuality, MineResult, Options};
|
||||
use crate::blockchain::types::BlockQuality::*;
|
||||
@@ -20,14 +20,11 @@ use crate::blockchain::transaction::{ZoneData, DomainData};
|
||||
use std::ops::Deref;
|
||||
use crate::blockchain::types::MineResult::*;
|
||||
|
||||
const DB_NAME: &str = "blockchain.db";
|
||||
const TEMP_DB_NAME: &str = "temp.db";
|
||||
const SQL_CREATE_TABLES: &str = include_str!("sql/create_db.sql");
|
||||
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_GET_FIRST_BLOCK_FOR_KEY: &str = "SELECT id FROM blocks WHERE pub_key = ? LIMIT 1;";
|
||||
const SQL_DELETE_BLOCK_AND_TRANSACTIONS: &str = "DELETE FROM blocks WHERE id = ?; DELETE FROM domains WHERE id = ?; DELETE FROM zones WHERE id = ?;";
|
||||
const SQL_TRUNCATE_BLOCKS: &str = "DELETE FROM blocks WHERE id >= ?;";
|
||||
const SQL_TRUNCATE_DOMAINS: &str = "DELETE FROM domains WHERE id >= ?;";
|
||||
const SQL_TRUNCATE_ZONES: &str = "DELETE FROM zones WHERE id >= ?;";
|
||||
@@ -37,8 +34,8 @@ const SQL_ADD_ZONE: &str = "INSERT INTO zones (id, timestamp, identity, confirma
|
||||
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 id < ? AND `transaction`<>'' ORDER BY id DESC LIMIT 1;";
|
||||
const SQL_GET_LAST_FULL_BLOCK_FOR_KEY: &str = "SELECT * FROM blocks WHERE id < ? AND `transaction`<>'' AND pub_key = ? ORDER BY id DESC LIMIT 1;";
|
||||
const SQL_GET_DOMAIN_PUBLIC_KEY_BY_ID: &str = "SELECT pub_key FROM domains WHERE identity = ? ORDER BY id DESC LIMIT 1;";
|
||||
const SQL_GET_ZONE_PUBLIC_KEY_BY_ID: &str = "SELECT pub_key FROM zones WHERE identity = ? ORDER BY id DESC LIMIT 1;";
|
||||
const SQL_GET_DOMAIN_PUBLIC_KEY_BY_ID: &str = "SELECT pub_key FROM domains WHERE id < ? AND identity = ? LIMIT 1;";
|
||||
const SQL_GET_ZONE_PUBLIC_KEY_BY_ID: &str = "SELECT pub_key FROM zones WHERE id < ? AND identity = ? LIMIT 1;";
|
||||
const SQL_GET_DOMAIN_BY_ID: &str = "SELECT * FROM domains WHERE identity = ? ORDER BY id DESC LIMIT 1;";
|
||||
const SQL_GET_DOMAINS_BY_KEY: &str = "SELECT * FROM domains WHERE pub_key = ?;";
|
||||
const SQL_GET_ZONES: &str = "SELECT data FROM zones;";
|
||||
@@ -59,10 +56,10 @@ pub struct Chain {
|
||||
}
|
||||
|
||||
impl Chain {
|
||||
pub fn new(settings: &Settings) -> Self {
|
||||
pub fn new(settings: &Settings, db_name: &str) -> Self {
|
||||
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 zones = RefCell::new(HashSet::new());
|
||||
let mut chain = Chain { origin, last_block: None, last_full_block: None, max_height: 0, db, zones, signers: SignersCache::new() };
|
||||
chain.init_db();
|
||||
@@ -90,17 +87,33 @@ impl Chain {
|
||||
self.last_full_block = self.get_last_full_block(MAX, None);
|
||||
}
|
||||
}
|
||||
// TODO Add env-var and commandline switches for full check
|
||||
//self.check_chain();
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn check_chain(&mut self) {
|
||||
pub fn check_chain(&mut self, count: u64) {
|
||||
let height = self.get_height();
|
||||
info!("Local blockchain height is {}, starting full blockchain check...", height);
|
||||
for id in 1..=height {
|
||||
info!("Checking block {}", id);
|
||||
match self.get_block(id) {
|
||||
let start = if height > count {
|
||||
info!("Checking last {} blocks...", count);
|
||||
height - count + 1
|
||||
} else {
|
||||
info!("Local blockchain height is {}, starting full blockchain check...", height);
|
||||
1
|
||||
};
|
||||
let mut last_block: Option<Block> = None;
|
||||
let mut last_full_block: Option<Block> = None;
|
||||
if start > 1 {
|
||||
last_block = self.get_block(start - 1);
|
||||
if let Some(last) = &last_block {
|
||||
last_full_block = match &last.transaction {
|
||||
None => { self.get_last_full_block(last.index, None) }
|
||||
Some(_) => { Some(last.clone()) }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
for id in start..=height {
|
||||
debug!("Checking block {}", id);
|
||||
let block = self.get_block(id);
|
||||
match block {
|
||||
None => {
|
||||
panic!("Blockchain is corrupted! Please, delete 'blockchain.db' and restart.");
|
||||
}
|
||||
@@ -109,12 +122,13 @@ impl Chain {
|
||||
if block.hash != self.origin {
|
||||
panic!("Loaded DB is not of origin {:?}! Please, delete 'blockchain.db' and restart.", &self.origin);
|
||||
}
|
||||
self.last_block = Some(block);
|
||||
debug!("Block {} with hash {:?} is good!", block.index, &block.hash);
|
||||
last_block = Some(block);
|
||||
continue;
|
||||
}
|
||||
|
||||
//let last = self.last_block.clone().unwrap();
|
||||
if self.check_new_block(&block) != BlockQuality::Good {
|
||||
if self.check_block(&block, &last_block, &last_full_block) != BlockQuality::Good {
|
||||
error!("Block {} is bad:\n{:?}", block.index, &block);
|
||||
info!("Truncating database from block {}...", block.index);
|
||||
match self.truncate_db_from_block(block.index) {
|
||||
@@ -126,29 +140,22 @@ impl Chain {
|
||||
}
|
||||
break;
|
||||
}
|
||||
info!("Block {} with hash {:?} is good!", block.index, &block.hash);
|
||||
debug!("Block {} with hash {:?} is good!", block.index, &block.hash);
|
||||
if block.transaction.is_some() {
|
||||
self.last_full_block = Some(block.clone());
|
||||
}
|
||||
self.last_block = Some(block);
|
||||
if block.transaction.is_some() {
|
||||
last_full_block = Some(block.clone());
|
||||
}
|
||||
last_block = Some(block);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.last_block = self.load_last_block();
|
||||
self.last_full_block = self.get_last_full_block(MAX, None);
|
||||
info!("Last block after chain check: {:?}", &self.last_block);
|
||||
debug!("Last block after chain check: {:?}", &self.last_block);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn delete_block(&mut self, index: u64) -> sqlite::Result<State> {
|
||||
let mut statement = self.db.prepare(SQL_DELETE_BLOCK_AND_TRANSACTIONS)?;
|
||||
statement.bind(1, index as i64)?;
|
||||
statement.bind(2, index as i64)?;
|
||||
statement.bind(3, index as i64)?;
|
||||
statement.next()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn truncate_db_from_block(&mut self, index: u64) -> sqlite::Result<State> {
|
||||
let mut statement = self.db.prepare(SQL_TRUNCATE_BLOCKS)?;
|
||||
statement.bind(1, index as i64)?;
|
||||
@@ -396,13 +403,15 @@ impl Chain {
|
||||
}
|
||||
|
||||
/// Gets last block that has a Transaction within
|
||||
pub fn get_last_full_block(&self, index: u64, pub_key: Option<&[u8]>) -> Option<Block> {
|
||||
pub fn get_last_full_block(&self, before: u64, pub_key: Option<&[u8]>) -> Option<Block> {
|
||||
if let Some(block) = &self.last_full_block {
|
||||
match pub_key {
|
||||
None => { return Some(block.clone()); }
|
||||
Some(key) => {
|
||||
if block.pub_key.deref().eq(key) {
|
||||
return Some(block.clone());
|
||||
if block.index < before {
|
||||
match pub_key {
|
||||
None => { return Some(block.clone()); }
|
||||
Some(key) => {
|
||||
if block.pub_key.deref().eq(key) {
|
||||
return Some(block.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -411,12 +420,12 @@ impl Chain {
|
||||
let mut statement = match pub_key {
|
||||
None => {
|
||||
let mut statement = self.db.prepare(SQL_GET_LAST_FULL_BLOCK).expect("Unable to prepare");
|
||||
statement.bind(1, index as i64).expect("Unable to bind");
|
||||
statement.bind(1, before as i64).expect("Unable to bind");
|
||||
statement
|
||||
}
|
||||
Some(pub_key) => {
|
||||
let mut statement = self.db.prepare(SQL_GET_LAST_FULL_BLOCK_FOR_KEY).expect("Unable to prepare");
|
||||
statement.bind(1, index as i64).expect("Unable to bind");
|
||||
statement.bind(1, before as i64).expect("Unable to bind");
|
||||
statement.bind(2, pub_key).expect("Unable to bind");
|
||||
statement
|
||||
}
|
||||
@@ -437,12 +446,12 @@ impl Chain {
|
||||
}
|
||||
|
||||
/// 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, height: u64, domain: &str, keystore: &Keystore) -> bool {
|
||||
if domain.is_empty() {
|
||||
return false;
|
||||
}
|
||||
let identity_hash = hash_identity(domain, None);
|
||||
if !self.is_id_available(&identity_hash, &keystore.get_public(), false) {
|
||||
if !self.is_id_available(height, &identity_hash, &keystore.get_public(), false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -452,20 +461,21 @@ impl Chain {
|
||||
if parts.last().unwrap().contains(".") {
|
||||
return false;
|
||||
}
|
||||
return self.is_zone_in_blockchain(parts.first().unwrap());
|
||||
return self.is_zone_in_blockchain(height, parts.first().unwrap());
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Checks if this identity is free or is owned by the same pub_key
|
||||
pub fn is_id_available(&self, identity: &Bytes, public_key: &Bytes, zone: bool) -> bool {
|
||||
pub fn is_id_available(&self, height: u64, identity: &Bytes, public_key: &Bytes, zone: bool) -> bool {
|
||||
let sql = match zone {
|
||||
true => { SQL_GET_ZONE_PUBLIC_KEY_BY_ID }
|
||||
false => { SQL_GET_DOMAIN_PUBLIC_KEY_BY_ID }
|
||||
};
|
||||
|
||||
let mut statement = self.db.prepare(sql).unwrap();
|
||||
statement.bind(1, &***identity).expect("Error in bind");
|
||||
statement.bind(1, height as i64).expect("Error in bind");
|
||||
statement.bind(2, &***identity).expect("Error in bind");
|
||||
while let State::Row = statement.next().unwrap() {
|
||||
let pub_key = Bytes::from_bytes(&statement.read::<Vec<u8>>(0).unwrap());
|
||||
if !pub_key.eq(public_key) {
|
||||
@@ -496,14 +506,14 @@ impl Chain {
|
||||
}
|
||||
|
||||
/// 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, height: u64, zone: &str) -> bool {
|
||||
if self.zones.borrow().contains(zone) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Checking for existing zone in DB
|
||||
let identity_hash = hash_identity(zone, None);
|
||||
if self.is_id_in_blockchain(&identity_hash, true) {
|
||||
if self.is_id_in_blockchain(height, &identity_hash, true) {
|
||||
// If there is such a zone
|
||||
self.zones.borrow_mut().insert(zone.to_owned());
|
||||
return true;
|
||||
@@ -512,14 +522,15 @@ impl Chain {
|
||||
}
|
||||
|
||||
/// Checks if some id exists in our blockchain
|
||||
pub fn is_id_in_blockchain(&self, id: &Bytes, zone: bool) -> bool {
|
||||
pub fn is_id_in_blockchain(&self, height: u64, id: &Bytes, zone: bool) -> bool {
|
||||
let sql = match zone {
|
||||
true => { SQL_GET_ZONE_PUBLIC_KEY_BY_ID }
|
||||
false => { SQL_GET_DOMAIN_PUBLIC_KEY_BY_ID }
|
||||
};
|
||||
// Checking for existing zone in DB
|
||||
let mut statement = self.db.prepare(sql).unwrap();
|
||||
statement.bind(1, &***id).expect("Error in bind");
|
||||
statement.bind(1, height as i64).expect("Error in bind");
|
||||
statement.bind(2, &***id).expect("Error in bind");
|
||||
while let State::Row = statement.next().unwrap() {
|
||||
// If there is such a zone
|
||||
return true;
|
||||
@@ -527,13 +538,13 @@ impl Chain {
|
||||
false
|
||||
}
|
||||
|
||||
pub fn can_mine_domain(&self, domain: &str, pub_key: &Bytes) -> MineResult {
|
||||
pub fn can_mine_domain(&self, height: u64, domain: &str, pub_key: &Bytes) -> MineResult {
|
||||
let name = domain.to_lowercase();
|
||||
if !check_domain(&name, true) {
|
||||
return WrongName;
|
||||
}
|
||||
let zone = get_domain_zone(&name);
|
||||
if !self.is_zone_in_blockchain(&zone) {
|
||||
if !self.is_zone_in_blockchain(height, &zone) {
|
||||
return WrongZone;
|
||||
}
|
||||
if let Some(transaction) = self.get_domain_transaction(&name) {
|
||||
@@ -543,7 +554,7 @@ impl Chain {
|
||||
}
|
||||
let identity_hash = hash_identity(&name, None);
|
||||
if let Some(last) = self.get_last_full_block(MAX, Some(&pub_key)) {
|
||||
let new_id = !self.is_id_in_blockchain(&identity_hash, false);
|
||||
let new_id = !self.is_id_in_blockchain(height, &identity_hash, false);
|
||||
let time = last.timestamp + NEW_DOMAINS_INTERVAL - Utc::now().timestamp();
|
||||
if new_id && time > 0 {
|
||||
return Cooldown { time }
|
||||
@@ -684,8 +695,12 @@ impl Chain {
|
||||
self.max_height = height;
|
||||
}
|
||||
|
||||
/// Check if this block can be added to our blockchain
|
||||
pub fn check_new_block(&self, block: &Block) -> BlockQuality {
|
||||
self.check_block(block, &self.last_block, &self.last_full_block)
|
||||
}
|
||||
|
||||
/// Check if this block can be added to our blockchain
|
||||
pub fn check_block(&self, block: &Block, last_block: &Option<Block>, last_full_block: &Option<Block>) -> BlockQuality {
|
||||
if block.version > CHAIN_VERSION {
|
||||
warn!("Ignoring block from unsupported version:\n{:?}", &block);
|
||||
return Bad;
|
||||
@@ -695,9 +710,11 @@ impl Chain {
|
||||
warn!("Ignoring block from the future:\n{:?}", &block);
|
||||
return Bad;
|
||||
}
|
||||
if block.index > self.get_height() + 1 {
|
||||
info!("Ignoring future block:\n{:?}", &block);
|
||||
return Future;
|
||||
if let Some(last) = last_block {
|
||||
if block.index > last.index + 1 {
|
||||
info!("Ignoring future block:\n{:?}", &block);
|
||||
return Future;
|
||||
}
|
||||
}
|
||||
if !check_public_key_strength(&block.pub_key, KEYSTORE_DIFFICULTY) {
|
||||
warn!("Ignoring block with weak public key:\n{:?}", &block);
|
||||
@@ -736,36 +753,28 @@ impl Chain {
|
||||
}
|
||||
}
|
||||
|
||||
let faulty_blocks = vec![
|
||||
"0000133B790B61460D757E1F1F2D04480C8340D28CA73AE5AF27DBBF60548D00",
|
||||
"8564E56AB50AE8473C3A26D7F5FF768A0238D463FAAE4A2049B2A6052F140000",
|
||||
"0000FD8442CE01D9F25A4F53BE21A8552E83182184F2FF75E2A77718CF87483E",
|
||||
"000CF01FA8E538A5AEA1E0E7B5FAB14914A4407B1CBE93CBB0F2129782661160",
|
||||
];
|
||||
for hash in faulty_blocks {
|
||||
let bytes = Bytes::from_bytes(&from_hex(hash).unwrap());
|
||||
if block.hash == bytes {
|
||||
warn!("Block {:?} is faulty! Ignoring!", &block);
|
||||
return Bad;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(transaction) = &block.transaction {
|
||||
let current_height = match last_block {
|
||||
None => { 0 }
|
||||
Some(block) => { block.index }
|
||||
};
|
||||
// TODO check for zone transaction
|
||||
if !self.is_id_available(&transaction.identity, &block.pub_key, false) || !self.is_id_available(&transaction.identity, &block.pub_key, true) {
|
||||
let is_domain_available = self.is_id_available(current_height, &transaction.identity, &block.pub_key, false);
|
||||
let is_zone_available = self.is_id_available(current_height, &transaction.identity, &block.pub_key, true);
|
||||
if !is_domain_available || !is_zone_available {
|
||||
warn!("Block {:?} is trying to spoof an identity!", &block);
|
||||
return Bad;
|
||||
}
|
||||
if let Some(last) = self.get_last_full_block(block.index, Some(&block.pub_key)) {
|
||||
if last.index < block.index {
|
||||
let new_id = !self.is_id_in_blockchain(&transaction.identity, false);
|
||||
let new_id = !self.is_id_in_blockchain(block.index, &transaction.identity, false);
|
||||
if new_id && last.timestamp + NEW_DOMAINS_INTERVAL > block.timestamp {
|
||||
warn!("Block {:?} is mined too early!", &block);
|
||||
return Bad;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check if yggdrasil only quality of zone is not violated
|
||||
// Check if yggdrasil only property of zone is not violated
|
||||
if let Some(block_data) = transaction.get_domain_data() {
|
||||
let zones = self.get_zones();
|
||||
for z in &zones {
|
||||
@@ -782,7 +791,7 @@ impl Chain {
|
||||
}
|
||||
}
|
||||
}
|
||||
match &self.last_block {
|
||||
match last_block {
|
||||
None => {
|
||||
if !block.is_genesis() {
|
||||
warn!("Block is from the future, how is this possible?");
|
||||
@@ -804,7 +813,7 @@ impl Chain {
|
||||
}
|
||||
if block.index > BLOCK_SIGNERS_START {
|
||||
// If this block is main, signed part of blockchain
|
||||
if !self.is_good_sign_block(&block) {
|
||||
if !self.is_good_sign_block(&block, last_full_block) {
|
||||
return Bad;
|
||||
}
|
||||
}
|
||||
@@ -834,12 +843,12 @@ impl Chain {
|
||||
}
|
||||
|
||||
/// Checks if this block is a good signature block
|
||||
fn is_good_sign_block(&self, block: &Block) -> bool {
|
||||
fn is_good_sign_block(&self, block: &Block, last_full_block: &Option<Block>) -> bool {
|
||||
// If this is not a signing block
|
||||
if block.transaction.is_some() {
|
||||
return true;
|
||||
}
|
||||
if let Some(full_block) = &self.last_full_block {
|
||||
if let Some(full_block) = &last_full_block {
|
||||
let sign_count = self.get_height() - full_block.index;
|
||||
if sign_count < BLOCK_SIGNERS_MIN {
|
||||
// Last full block is not locked enough
|
||||
@@ -847,12 +856,12 @@ impl Chain {
|
||||
warn!("Not enough signing blocks over full {} block!", full_block.index);
|
||||
return false;
|
||||
} else {
|
||||
if !self.is_good_signer_for_block(&block, full_block, sign_count) {
|
||||
if !self.is_good_signer_for_block(&block, full_block) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if sign_count < BLOCK_SIGNERS_ALL && block.transaction.is_none() {
|
||||
if !self.is_good_signer_for_block(&block, full_block, sign_count) {
|
||||
if !self.is_good_signer_for_block(&block, full_block) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -861,11 +870,7 @@ impl Chain {
|
||||
}
|
||||
|
||||
/// Check if this block's owner is a good candidate to sign last full block
|
||||
fn is_good_signer_for_block(&self, block: &Block, full_block: &Block, sign_count: u64) -> bool {
|
||||
// If the time for chosen signers is up
|
||||
if self.can_sign_by_pos(sign_count, full_block.timestamp, block.timestamp, &block.pub_key) {
|
||||
return true;
|
||||
}
|
||||
fn is_good_signer_for_block(&self, block: &Block, full_block: &Block) -> bool {
|
||||
// If we got a signing block
|
||||
let signers: HashSet<Bytes> = self.get_block_signers(full_block).into_iter().collect();
|
||||
if !signers.contains(&block.pub_key) {
|
||||
@@ -883,33 +888,6 @@ impl Chain {
|
||||
true
|
||||
}
|
||||
|
||||
/// Gets an id of first block of this public key
|
||||
fn get_first_block_id_for_key(&self, key: &Bytes) -> u64 {
|
||||
match self.db.prepare(SQL_GET_FIRST_BLOCK_FOR_KEY) {
|
||||
Ok(mut statement) => {
|
||||
statement.bind(1, &***key).expect("Error in bind");
|
||||
while statement.next().unwrap() == State::Row {
|
||||
return statement.read::<i64>(0).unwrap() as u64;
|
||||
}
|
||||
0
|
||||
}
|
||||
Err(_) => {
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if an owner of this public key can sign full block by PoS scheme (be in first 1000 users)
|
||||
fn can_sign_by_pos(&self, sign_count: u64, block_time: i64, now: i64, pub_key: &Bytes) -> bool {
|
||||
if sign_count < BLOCK_SIGNERS_MIN && block_time - now > BLOCK_SIGNERS_TIME {
|
||||
let index = self.get_first_block_id_for_key(&pub_key);
|
||||
if index > 0 && index <= BLOCK_POS_SIGNERS {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn get_difficulty_for_transaction(&self, transaction: &Transaction) -> u32 {
|
||||
match transaction.class.as_ref() {
|
||||
"domain" => {
|
||||
@@ -997,4 +975,34 @@ impl SignersCache {
|
||||
pub fn has_signers_for(&self, index: u64) -> bool {
|
||||
self.index == index && !self.signers.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use crate::{Chain, Settings};
|
||||
use simplelog::{ConfigBuilder, TermLogger, TerminalMode, ColorChoice};
|
||||
use log::LevelFilter;
|
||||
|
||||
fn init_logger() {
|
||||
let config = ConfigBuilder::new()
|
||||
.add_filter_ignore_str("mio::poll")
|
||||
.set_thread_level(LevelFilter::Off)
|
||||
.set_location_level(LevelFilter::Off)
|
||||
.set_target_level(LevelFilter::Error)
|
||||
.set_time_level(LevelFilter::Error)
|
||||
.set_time_to_local(true)
|
||||
.build();
|
||||
if let Err(e) = TermLogger::init(LevelFilter::Trace, config, TerminalMode::Stdout, ColorChoice::Auto) {
|
||||
println!("Unable to initialize logger!\n{}", e);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn load_and_check() {
|
||||
init_logger();
|
||||
let settings = Settings::default();
|
||||
let mut chain = Chain::new(&settings, "./tests/blockchain.db");
|
||||
chain.check_chain(u64::MAX);
|
||||
assert_eq!(chain.get_height(), 214);
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,7 @@ impl DnsFilter for BlockchainFilter {
|
||||
let zone = parts[0].to_owned();
|
||||
match data {
|
||||
None => {
|
||||
if self.context.lock().unwrap().chain.is_zone_in_blockchain(&zone) {
|
||||
if self.context.lock().unwrap().chain.is_zone_in_blockchain(u64::MAX, &zone) {
|
||||
trace!("Not found data for domain {}", &search);
|
||||
// Create DnsPacket
|
||||
let mut packet = DnsPacket::new();
|
||||
@@ -192,7 +192,7 @@ impl BlockchainFilter {
|
||||
}
|
||||
|
||||
fn get_zone_response(&self, zone: &str, mut packet: &mut DnsPacket) -> bool {
|
||||
let have_zone = self.context.lock().unwrap().chain.is_zone_in_blockchain(zone);
|
||||
let have_zone = self.context.lock().unwrap().chain.is_zone_in_blockchain(i64::MAX as u64, zone);
|
||||
if have_zone {
|
||||
BlockchainFilter::add_soa_record(zone.to_owned(), &mut packet);
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ pub const DOMAIN_LIFETIME: i64 = 86400 * 365; // One year
|
||||
pub const ZONE_MAX_LENGTH: usize = 10;
|
||||
pub const MAX_RECONNECTS: u32 = 5;
|
||||
|
||||
pub const DB_NAME: &str = "blockchain.db";
|
||||
pub const CLASS_ZONE: &str = "zone";
|
||||
pub const CLASS_DOMAIN: &str = "domain";
|
||||
pub const ALFIS_DEBUG: &str = "ALFIS_DEBUG";
|
||||
|
||||
+4
-3
@@ -16,7 +16,7 @@ use simplelog::*;
|
||||
#[cfg(windows)]
|
||||
use winapi::um::wincon::{ATTACH_PARENT_PROCESS, AttachConsole, FreeConsole};
|
||||
|
||||
use alfis::{Block, Bytes, Chain, Miner, Context, Network, Settings, dns_utils, Keystore, ZONE_DIFFICULTY, ALFIS_DEBUG};
|
||||
use alfis::{Block, Bytes, Chain, Miner, Context, Network, Settings, dns_utils, Keystore, ZONE_DIFFICULTY, ALFIS_DEBUG, DB_NAME};
|
||||
use std::fs::OpenOptions;
|
||||
use std::process::exit;
|
||||
use std::io::{Seek, SeekFrom};
|
||||
@@ -103,9 +103,9 @@ fn main() {
|
||||
info!(target: LOG_TARGET_MAIN, "Starting ALFIS {}", env!("CARGO_PKG_VERSION"));
|
||||
|
||||
let settings = Settings::load(&config_name).expect(&format!("Cannot load settings from {}!", &config_name));
|
||||
info!(target: LOG_TARGET_MAIN, "Loaded settings: {:?}", &settings);
|
||||
debug!(target: LOG_TARGET_MAIN, "Loaded settings: {:?}", &settings);
|
||||
let keystore = Keystore::from_file(&settings.key_file, "");
|
||||
let chain: Chain = Chain::new(&settings);
|
||||
let mut chain: Chain = Chain::new(&settings, DB_NAME);
|
||||
if opt_matches.opt_present("b") {
|
||||
for i in 1..(chain.get_height() + 1) {
|
||||
if let Some(block) = chain.get_block(i) {
|
||||
@@ -114,6 +114,7 @@ fn main() {
|
||||
}
|
||||
return;
|
||||
}
|
||||
chain.check_chain(settings.check_blocks);
|
||||
|
||||
match chain.get_block(1) {
|
||||
None => { info!(target: LOG_TARGET_MAIN, "No blocks found in DB"); }
|
||||
|
||||
+2
-2
@@ -147,7 +147,7 @@ impl Miner {
|
||||
}
|
||||
|
||||
if !signing_waits {
|
||||
if let Ok(context) = context.try_lock() {
|
||||
if let Ok(context) = context.lock() {
|
||||
let keystore = context.get_keystore();
|
||||
// Ask the blockchain if we have to sign something
|
||||
if let Some(block) = context.chain.get_sign_block(&keystore) {
|
||||
@@ -175,7 +175,7 @@ impl Miner {
|
||||
}
|
||||
} else {
|
||||
// If our queue is empty
|
||||
if let Ok(context) = context.try_lock() {
|
||||
if let Ok(context) = context.lock() {
|
||||
let keystore = context.get_keystore();
|
||||
// Ask the blockchain if we have to sign something
|
||||
if let Some(block) = context.chain.get_sign_block(&keystore) {
|
||||
|
||||
+9
-2
@@ -13,6 +13,8 @@ pub struct Settings {
|
||||
pub origin: String,
|
||||
#[serde(default)]
|
||||
pub key_file: String,
|
||||
#[serde(default = "default_check_blocks")]
|
||||
pub check_blocks: u64,
|
||||
#[serde(default)]
|
||||
pub net: Net,
|
||||
#[serde(default)]
|
||||
@@ -50,8 +52,9 @@ impl Settings {
|
||||
impl Default for Settings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
origin: String::from("00000102C2F9BFD2803284D93327F089D60FC72A06F19AF2384567F2646B8348"),
|
||||
origin: String::from("0AE588D62D710422A7972EA1E8A659CC8E93DB59489ACE32C499CD279B000000"),
|
||||
key_file: String::from("default.key"),
|
||||
check_blocks: default_check_blocks(),
|
||||
net: Net::default(),
|
||||
dns: Default::default(),
|
||||
mining: Mining::default()
|
||||
@@ -121,5 +124,9 @@ fn default_listen_dns() -> String {
|
||||
}
|
||||
|
||||
fn default_threads() -> usize {
|
||||
20
|
||||
100
|
||||
}
|
||||
|
||||
fn default_check_blocks() -> u64 {
|
||||
8
|
||||
}
|
||||
+3
-3
@@ -117,7 +117,7 @@ fn action_check_zone(context: &Arc<Mutex<Context>>, web_view: &mut WebView<()>,
|
||||
} else {
|
||||
let c = context.lock().unwrap();
|
||||
if let Some(keystore) = c.get_keystore() {
|
||||
let available = c.get_chain().is_domain_available(&name, &keystore);
|
||||
let available = c.get_chain().is_domain_available(c.get_chain().get_height(), &name, &keystore);
|
||||
web_view.eval(&format!("zoneAvailable({})", available)).expect("Error evaluating!");
|
||||
}
|
||||
}
|
||||
@@ -134,7 +134,7 @@ fn action_check_domain(context: &Arc<Mutex<Context>>, web_view: &mut WebView<()>
|
||||
let c = context.lock().unwrap();
|
||||
if let Some(keystore) = c.get_keystore() {
|
||||
let name = name.to_lowercase();
|
||||
let available = c.get_chain().is_domain_available(&name, &keystore);
|
||||
let available = c.get_chain().is_domain_available(c.get_chain().get_height(), &name, &keystore);
|
||||
web_view.eval(&format!("domainAvailable({})", available)).expect("Error evaluating!");
|
||||
}
|
||||
}
|
||||
@@ -380,7 +380,7 @@ fn action_create_domain(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>,
|
||||
}
|
||||
}
|
||||
}
|
||||
match context.chain.can_mine_domain(&name, &pub_key) {
|
||||
match context.chain.can_mine_domain(context.chain.get_height(), &name, &pub_key) {
|
||||
MineResult::Fine => {
|
||||
let zone = get_domain_zone(&name);
|
||||
let difficulty = context.chain.get_zone_difficulty(&zone);
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user