@ -1,5 +1,7 @@
use std ::cell ::RefCell ;
use std ::collections ::{ HashSet , HashMap } ;
use std ::fs ;
use std ::path ::Path ;
use chrono ::Utc ;
#[ allow(unused_imports) ]
@ -8,49 +10,40 @@ use sqlite::{Connection, State, Statement};
use crate ::{ Block , Bytes , Keystore , Transaction , check_domain , get_domain_zone } ;
use crate ::commons ::constants ::* ;
use crate ::blockchain :: enum s::{ BlockQuality , MineResult } ;
use crate ::blockchain :: enum s::BlockQuality ::* ;
use crate ::blockchain ::typ es::{ BlockQuality , MineResult , Options } ;
use crate ::blockchain ::typ es::BlockQuality ::* ;
use crate ::blockchain ::hash_utils ::* ;
use crate ::settings ::Settings ;
use crate ::keys ::check_public_key_strength ;
use std ::cmp ::{ min , max } ;
use crate ::blockchain ::transaction ::{ ZoneData , DomainData } ;
use std ::ops ::Deref ;
use crate ::dns ::protocol ::DnsRecord ;
use crate ::blockchain ::enums ::MineResult ::* ;
use crate ::blockchain ::types ::MineResult ::* ;
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 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_REPLACE_BLOCK : & str = " UPDATE blocks SET timestamp = ? , version = ? , difficulty = ? , random = ? , nonce = ? , ' transaction ' = ? , \
prev_block_hash = ? , hash = ? , pub_key = ? , signature = ? WHERE id = ? ; " ;
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_ADD_DOMAIN : & str = "INSERT INTO domains (id, timestamp, identity, confirmation, data, pub_key) VALUES (?, ?, ?, ?, ?, ?)" ;
const SQL_ADD_ZONE : & str = "INSERT INTO zones (id, timestamp, identity, confirmation, data, pub_key) VALUES (?, ?, ?, ?, ?, ?)" ;
const SQL_DELETE_DOMAIN : & str = "DELETE FROM domains WHERE id = ?" ;
const SQL_DELETE_ZONE : & str = "DELETE FROM zones WHERE id = ?" ;
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_LAST_FULL_BLOCK_FOR_KEY : & str = "SELECT * FROM blocks WHERE `transaction`<>'' AND pub_key = ? 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;" ;
const SQL_GET_TRANSACTIONS_WITH_ZONE : & str = "SELECT data FROM transactions WHERE data LIKE '%difficulty%';" ;
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_BY_ID : & str = "SELECT * FROM domains WHERE identity = ? ORDER BY id DESC LIMIT 1;" ;
const SQL_GET_ZONES : & str = "SELECT data FROM zones;" ;
const SQL_GET_OPTIONS : & str = "SELECT * FROM options;" ;
pub struct Chain {
origin : Bytes ,
pub version : u32 ,
pub blocks : Vec < Block > ,
last_block : Option < Block > ,
last_full_block : Option < Block > ,
max_height : u64 ,
@ -63,22 +56,22 @@ impl Chain {
let origin = settings . get_origin ( ) ;
let db = sqlite ::open ( DB_NAME ) . expect ( "Unable to open blockchain DB" ) ;
let mut chain = Chain {
origin ,
version : CHAIN_VERSION ,
blocks : Vec ::new ( ) ,
last_block : None ,
last_full_block : None ,
max_height : 0 ,
db ,
zones : RefCell ::new ( HashSet ::new ( ) ) ,
} ;
let zones = RefCell ::new ( HashSet ::new ( ) ) ;
let mut chain = Chain { origin , last_block : None , last_full_block : None , max_height : 0 , db , zones } ;
chain . init_db ( ) ;
chain
}
/// Reads options from DB or initializes and writes them to DB if not found
fn init_db ( & mut self ) {
let options = self . get_options ( ) ;
if ! self . origin . is_zero ( ) & & ! options . origin . is_empty ( ) & & self . origin . to_string ( ) ! = options . origin {
self . clear_db ( ) ;
}
if options . version < DB_VERSION {
self . migrate_db ( options . version , DB_VERSION ) ;
}
// 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 ) = > {
@ -98,21 +91,14 @@ impl Chain {
}
result
}
Err ( _ ) = > {
info ! ( "No blockchain database found. Creating new. ") ;
self . db . execute ( SQL_CREATE_TABLES ) . expect ( "Error creating blocks table ") ;
Err ( e ) = > {
info ! ( "No blockchain database found. Creating new. {} ", e ) ;
self . db . execute ( SQL_CREATE_TABLES ) . expect ( "Error creating DB tables ") ;
None
}
} ;
// If some block loaded we check its version and determine if we need some migration
if let Some ( block ) = block {
self . max_height = block . index ;
if self . version > block . version {
self . migrate_db ( block . version , self . version ) ;
} else if self . version < block . version {
error ! ( "Version downgrade {}->{} is not supported!" , block . version , self . version ) ;
panic! ( ) ;
}
// Cache some info
self . last_block = Some ( block . clone ( ) ) ;
if block . transaction . is_some ( ) {
@ -127,9 +113,41 @@ impl Chain {
debug ! ( "Migrating DB from {} to {}" , from , to ) ;
}
fn clear_db ( & mut self ) {
warn ! ( "Clearing DB" ) ;
// We cannot close DB connection and recreate file,
// therefore we switch our db to temporary file, delete main DB and switch back.
// I know that this is a crutch, but this way I don't need to use Option<db> :)
self . db = sqlite ::open ( TEMP_DB_NAME ) . expect ( "Unable to open temporary blockchain DB" ) ;
let file = Path ::new ( DB_NAME ) ;
if fs ::remove_file ( & file ) . is_err ( ) {
panic! ( "Unable to remove database!" ) ;
}
self . db = sqlite ::open ( DB_NAME ) . expect ( "Unable to open blockchain DB" ) ;
let file = Path ::new ( TEMP_DB_NAME ) ;
let _ = fs ::remove_file ( & file ) . is_err ( ) ;
}
fn get_options ( & self ) -> Options {
let mut options = Options ::empty ( ) ;
if let Ok ( mut statement ) = self . db . prepare ( SQL_GET_OPTIONS ) {
while let State ::Row = statement . next ( ) . unwrap ( ) {
let name = statement . read ::< String > ( 0 ) . unwrap ( ) ;
let value = statement . read ::< String > ( 1 ) . unwrap ( ) ;
match name . as_ref ( ) {
"origin" = > options . origin = value ,
"version" = > options . version = value . parse ( ) . unwrap ( ) ,
_ = > { }
}
}
}
options
}
pub fn add_block ( & mut self , block : Block ) {
debug ! ( "Adding block:\n{:?}" , & block ) ;
self . blocks . push ( block . clone ( ) ) ;
let index = block . index ;
let timestamp = block . timestamp ;
self . last_block = Some ( block . clone ( ) ) ;
if block . transaction . is_some ( ) {
self . last_full_block = Some ( block . clone ( ) ) ;
@ -137,9 +155,37 @@ impl Chain {
let transaction = block . transaction . clone ( ) ;
if self . add_block_to_table ( block ) . is_ok ( ) {
if let Some ( transaction ) = transaction {
self . add_transaction_to_table ( & transaction ) . expect ( "Error adding transaction" ) ;
self . add_transaction_to_table ( index , timestamp , & transaction ) . expect ( "Error adding transaction" ) ;
}
}
}
pub fn replace_block ( & mut self , index : u64 , block : Block ) -> sqlite ::Result < ( ) > {
debug ! ( "Replacing block {} with:\n{:?}" , index , & block ) ;
let old_block = self . get_block ( index ) . unwrap ( ) ;
if old_block . transaction . is_some ( ) {
let mut statement = self . db . prepare ( SQL_DELETE_DOMAIN ) ? ;
statement . bind ( 1 , index as i64 ) ? ;
statement . next ( ) ? ;
let mut statement = self . db . prepare ( SQL_DELETE_ZONE ) ? ;
statement . bind ( 1 , index as i64 ) ? ;
statement . next ( ) ? ;
}
let index = block . index ;
let timestamp = block . timestamp ;
self . last_block = Some ( block . clone ( ) ) ;
if block . transaction . is_some ( ) {
self . last_full_block = Some ( block . clone ( ) ) ;
}
let transaction = block . transaction . clone ( ) ;
if self . replace_block_in_table ( block ) . is_ok ( ) {
if let Some ( transaction ) = transaction {
self . add_transaction_to_table ( index , timestamp , & transaction ) . expect ( "Error adding transaction" ) ;
}
}
Ok ( ( ) )
}
/// Adds block to blocks table
@ -164,14 +210,43 @@ impl Chain {
statement . next ( )
}
/// Replaces block in blocks table on arrival of better block from some fork
fn replace_block_in_table ( & mut self , block : Block ) -> sqlite ::Result < State > {
let mut statement = self . db . prepare ( SQL_REPLACE_BLOCK ) ? ;
statement . bind ( 1 , block . timestamp as i64 ) ? ;
statement . bind ( 2 , block . version as i64 ) ? ;
statement . bind ( 3 , block . difficulty as i64 ) ? ;
statement . bind ( 4 , block . random as i64 ) ? ;
statement . bind ( 5 , block . nonce as i64 ) ? ;
match & block . transaction {
None = > { statement . bind ( 6 , "" ) ? ; }
Some ( transaction ) = > {
statement . bind ( 6 , transaction . to_string ( ) . as_str ( ) ) ? ;
}
}
statement . bind ( 7 , & * * block . prev_block_hash ) ? ;
statement . bind ( 8 , & * * block . hash ) ? ;
statement . bind ( 9 , & * * block . pub_key ) ? ;
statement . bind ( 10 , & * * block . signature ) ? ;
statement . bind ( 11 , block . index as i64 ) ? ;
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 ) ? ;
statement . bind ( 2 , & * * t . confirmation ) ? ;
statement . bind ( 3 , t . method . as_ref ( ) as & str ) ? ;
statement . bind ( 4 , t . data . as_ref ( ) as & str ) ? ;
statement . bind ( 5 , & * * t . pub_key ) ? ;
fn add_transaction_to_table ( & mut self , index : u64 , timestamp : i64 , t : & Transaction ) -> sqlite ::Result < State > {
let sql = match t . class . as_ref ( ) {
"domain" = > SQL_ADD_DOMAIN ,
"zone" = > SQL_ADD_ZONE ,
_ = > return Err ( sqlite ::Error { code : None , message : None } )
} ;
let mut statement = self . db . prepare ( sql ) ? ;
statement . bind ( 1 , index as i64 ) ? ;
statement . bind ( 2 , timestamp ) ? ;
statement . bind ( 3 , & * * t . identity ) ? ;
statement . bind ( 4 , & * * t . confirmation ) ? ;
statement . bind ( 5 , t . data . as_ref ( ) as & str ) ? ;
statement . bind ( 6 , & * * t . pub_key ) ? ;
statement . next ( )
}
@ -244,7 +319,7 @@ impl Chain {
return false ;
}
let identity_hash = hash_identity ( domain , None ) ;
if ! self . is_id_available ( & identity_hash , & keystore . get_public ( ) ) {
if ! self . is_id_available ( & identity_hash , & keystore . get_public ( ) , false ) {
return false ;
}
@ -260,8 +335,13 @@ impl Chain {
}
/// 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 ) -> bool {
let mut statement = self . db . prepare ( SQL_GET_PUBLIC_KEY_BY_ID ) . unwrap ( ) ;
pub fn is_id_available ( & self , 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" ) ;
while let State ::Row = statement . next ( ) . unwrap ( ) {
let pub_key = Bytes ::from_bytes ( & statement . read ::< Vec < u8 > > ( 0 ) . unwrap ( ) ) ;
@ -274,7 +354,7 @@ impl Chain {
pub fn get_zones ( & self ) -> Vec < ZoneData > {
let mut map = HashMap ::new ( ) ;
match self . db . prepare ( SQL_GET_ TRANSACTIONS_WITH_ ZONE) {
match self . db . prepare ( SQL_GET_ ZONES ) {
Ok ( mut statement ) = > {
while statement . next ( ) . unwrap ( ) = = State ::Row {
let data = statement . read ::< String > ( 0 ) . unwrap ( ) ;
@ -300,7 +380,7 @@ impl Chain {
// Checking for existing zone in DB
let identity_hash = hash_identity ( zone , None ) ;
if self . is_id_in_blockchain ( & identity_hash ) {
if self . is_id_in_blockchain ( & identity_hash , true ) {
// If there is such a zone
self . zones . borrow_mut ( ) . insert ( zone . to_owned ( ) ) ;
return true ;
@ -309,9 +389,13 @@ impl Chain {
}
/// Checks if some id exists in our blockchain
pub fn is_id_in_blockchain ( & self , id : & Bytes ) -> bool {
pub fn is_id_in_blockchain ( & self , 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_GET_ID_BY_ID ) . unwrap ( ) ;
let mut statement = self . db . prepare ( sql ) . unwrap ( ) ;
statement . bind ( 1 , & * * * id ) . expect ( "Error in bind" ) ;
while let State ::Row = statement . next ( ) . unwrap ( ) {
// If there is such a zone
@ -320,7 +404,7 @@ impl Chain {
false
}
pub fn can_mine_domain ( & self , domain : & str , records: & str , pub_key: & Bytes ) -> MineResult {
pub fn can_mine_domain ( & self , domain : & str , pub_key: & Bytes ) -> MineResult {
let name = domain . to_lowercase ( ) ;
if ! check_domain ( & name , true ) {
return WrongName ;
@ -334,13 +418,10 @@ impl Chain {
return NotOwned ;
}
}
if serde_json ::from_str ::< Vec < DnsRecord > > ( & records ) . is_err ( ) {
return WrongData ;
}
let identity_hash = hash_identity ( & name , None ) ;
if let Some ( last ) = self . get_last_full_block ( Some ( & pub_key ) ) {
let new_id = ! self . is_id_in_blockchain ( & identity_hash );
let time = last . timestamp + FULL_BLOCK S_INTERVAL - Utc ::now ( ) . timestamp ( ) ;
let new_id = ! self . is_id_in_blockchain ( & identity_hash , false ) ;
let time = last . timestamp + NEW_DOMAIN S_INTERVAL - Utc ::now ( ) . timestamp ( ) ;
if new_id & & time > 0 {
return Cooldown { time }
}
@ -356,15 +437,20 @@ impl Chain {
}
let identity_hash = hash_identity ( domain , None ) ;
let mut statement = self . db . prepare ( SQL_GET_ TRANSACTIO N_BY_ID) . unwrap ( ) ;
let mut statement = self . db . prepare ( SQL_GET_ DOMAI N_BY_ID) . unwrap ( ) ;
statement . bind ( 1 , & * * identity_hash ) . expect ( "Error in bind" ) ;
while let State ::Row = statement . next ( ) . unwrap ( ) {
let identity = Bytes ::from_bytes ( & statement . read ::< Vec < u8 > > ( 1 ) . unwrap ( ) ) ;
let confirmation = Bytes ::from_bytes ( & statement . read ::< Vec < u8 > > ( 2 ) . unwrap ( ) ) ;
let method = statement . read ::< String > ( 3 ) . unwrap ( ) ;
let data = statement . read ::< String > ( 4 ) . unwrap ( ) ;
let pub_key = Bytes ::from_bytes ( & statement . read ::< Vec < u8 > > ( 5 ) . unwrap ( ) ) ;
let transaction = Transaction { identity , confirmation , method , data , pub_key } ;
let timestamp = statement . read ::< i64 > ( 1 ) . unwrap ( ) ;
if timestamp < Utc ::now ( ) . timestamp ( ) - DOMAIN_LIFETIME {
// This domain is too old
return None ;
}
let identity = Bytes ::from_bytes ( & statement . read ::< Vec < u8 > > ( 2 ) . unwrap ( ) ) ;
let confirmation = Bytes ::from_bytes ( & statement . read ::< Vec < u8 > > ( 3 ) . unwrap ( ) ) ;
let method = statement . read ::< String > ( 4 ) . unwrap ( ) ;
let data = statement . read ::< String > ( 5 ) . unwrap ( ) ;
let pub_key = Bytes ::from_bytes ( & statement . read ::< Vec < u8 > > ( 6 ) . unwrap ( ) ) ;
let transaction = Transaction { identity , confirmation , class : method , data , pub_key } ;
debug ! ( "Found transaction for domain {}: {:?}" , domain , & transaction ) ;
if transaction . check_identity ( domain ) {
return Some ( transaction ) ;
@ -381,18 +467,13 @@ impl Chain {
}
pub fn get_zone_difficulty ( & self , zone : & str ) -> u32 {
match self . get_domain_transaction ( zone ) {
None = > { u32 ::max_value ( ) }
Some ( transaction ) = > {
match serde_json ::from_str ::< ZoneData > ( & transaction . data ) {
Ok ( data ) = > { data . difficulty }
Err ( _ ) = > {
warn ! ( "Wrong data for zone {}!" , zone ) ;
u32 ::max_value ( )
}
}
let zones = self . get_zones ( ) ;
for z in zones . iter ( ) {
if z . name . eq ( zone ) {
return z . difficulty ;
}
}
u32 ::max_value ( )
}
pub fn last_block ( & self ) -> Option < Block > {
@ -441,7 +522,7 @@ impl Chain {
/// Check if this block can be added to our blockchain
pub fn check_new_block ( & self , block : & Block ) -> BlockQuality {
let timestamp = Utc ::now ( ) . timestamp ( ) ;
if block . timestamp > timestamp {
if block . timestamp > timestamp + 60 {
warn ! ( "Ignoring block from the future:\n{:?}" , & block ) ;
return Bad ;
}
@ -449,15 +530,21 @@ impl Chain {
warn ! ( "Ignoring block with weak public key:\n{:?}" , & block ) ;
return Bad ;
}
let difficulty = match block . transaction {
None = > { LOCKER_DIFFICULTY }
Some ( _ ) = > { BLOCK_DIFFICULTY }
let difficulty = match & block . transaction {
None = > {
if block . index = = 1 {
ZONE_DIFFICULTY
} else {
LOCKER_DIFFICULTY
}
}
Some ( t ) = > { self . get_difficulty_for_transaction ( & t ) }
} ;
if block . difficulty < difficulty {
warn ! ( "Block difficulty is lower than needed" ) ;
return Bad ;
}
if ! hash_is_good ( & block . hash , block . difficulty as usize ) {
if hash_ difficulty ( & block . hash ) < block . difficulty {
warn ! ( "Ignoring block with low difficulty:\n{:?}" , & block ) ;
return Bad ;
}
@ -470,23 +557,18 @@ impl Chain {
return Bad ;
}
if let Some ( transaction ) = & block . transaction {
if ! self . is_id_available ( & transaction . identity , & block . pub_key ) {
// TODO check for zone transaction
if ! self . is_id_available ( & transaction . identity , & block . pub_key , false ) {
warn ! ( "Block {:?} is trying to spoof an identity!" , & block ) ;
return Bad ;
}
if let Some ( last ) = self . get_last_full_block ( Some ( & block . pub_key ) ) {
let new_id = ! self . is_id_in_blockchain ( & transaction . identity );
if new_id & & last . timestamp + FULL_BLOCK S_INTERVAL > block . timestamp {
let new_id = ! self . is_id_in_blockchain ( & transaction . identity , false );
if new_id & & last . timestamp + NEW_DOMAIN S_INTERVAL > block . timestamp {
warn ! ( "Block {:?} is mined too early!" , & block ) ;
return Bad ;
}
}
if let Ok ( data ) = serde_json ::from_str ::< DomainData > ( & transaction . data ) {
if self . get_zone_difficulty ( & data . zone ) > block . difficulty {
warn ! ( "Block {:?} is mined with too low difficulty!" , & block ) ;
return Bad ;
}
}
}
match & self . last_block {
None = > {
@ -505,25 +587,25 @@ impl Chain {
return Bad ;
}
if last_block . index + 1 < block . index {
warn ! ( "Block is from the future, how is this possible?" ) ;
warn ! ( "Block {} arrived too early.", block . index ) ;
return Future ;
}
if block . index > LOCKER_BLOCK_START {
if block . index > = LOCKER_BLOCK_START {
// If this block is locked part of blockchain
if let Some ( full_block ) = & self . last_full_block {
let locker_blocks = self . height ( ) - full_block . index ;
if locker_blocks < LOCKER_BLOCK_SIGNS {
// Last full block is not locked enough
if block . transaction . is_some ( ) {
warn ! ( " Someone mined full block over full block" ) ;
warn ! ( " Not enough signing blocks over full {} block!", full_block . index ) ;
return Bad ;
} else {
if self . check_block_for_ lock ( & block , full_block ) = = Bad {
if self . check_block_for_ signing ( & block , full_block ) = = Bad {
return Bad ;
}
}
} else if locker_blocks < LOCKER_BLOCK_LOCKERS & & block . transaction . is_none ( ) {
if self . check_block_for_ lock ( & block , full_block ) = = Bad {
if self . check_block_for_ signing ( & block , full_block ) = = Bad {
return Bad ;
}
}
@ -531,7 +613,7 @@ impl Chain {
}
if block . index < = last_block . index {
if last_block. hash = = block . hash {
if block. index = = last_block . index & & last_block. hash = = block . hash {
debug ! ( "Ignoring block {}, we already have it" , block . index ) ;
return Twin ;
}
@ -552,27 +634,47 @@ impl Chain {
Good
}
fn check_block_for_lock ( & self , block : & Block , full_block : & Block ) -> BlockQuality {
fn get_difficulty_for_transaction ( & self , transaction : & Transaction ) -> u32 {
match transaction . class . as_ref ( ) {
"domain" = > {
return match serde_json ::from_str ::< DomainData > ( & transaction . data ) {
Ok ( data ) = > {
for zone in self . get_zones ( ) . iter ( ) {
if zone . name = = data . zone {
return zone . difficulty ;
}
}
u32 ::max_value ( )
}
Err ( _ ) = > { u32 ::max_value ( ) }
}
}
"zone" = > { ZONE_DIFFICULTY }
_ = > { u32 ::max_value ( ) }
}
}
fn check_block_for_signing ( & self , block : & Block , full_block : & Block ) -> BlockQuality {
// If we got a locker/signing block
let lockers : HashSet < Bytes > = self . get_block_lockers ( full_block ) . into_iter ( ) . collect ( ) ;
if ! lockers . contains ( & block . pub_key ) {
warn ! ( "Ignoring block {}, as wrong locker" , block . index ) ;
let sign ers: HashSet < Bytes > = self . get_block_ sign ers( full_block ) . into_iter ( ) . collect ( ) ;
if ! sign ers. contains ( & block . pub_key ) {
warn ! ( "Ignoring block {} from '{:?}', as wrong signer! ", block . index , & block . pub_key ) ;
return Bad ;
}
// If this locker's public key has already locked/signed that block we return error
// If this signers' public key has already locked/signed that block we return error
for i in ( full_block . index + 1 ) .. block . index {
let locker = self . get_block ( i ) . expect ( "Error in DB!" ) ;
if locker . pub_key = = block . pub_key {
warn ! ( "Ignoring block {}, already locked by this key" , block . index ) ;
let sign er = self . get_block ( i ) . expect ( "Error in DB!" ) ;
if sign er. pub_key = = block . pub_key {
warn ! ( "Ignoring block {} from '{:?}', already sign ed by this key", block . index , & block . pub_key ) ;
return Bad ;
}
}
Good
}
/// Gets a public key of a node that needs to mine "locker" block above this block
/// Gets public keys of a node that needs to mine "signature " block above this block
/// block - last full block
pub fn get_block_lockers ( & self , block : & Block ) -> Vec < Bytes > {
pub fn get_block_ sign ers( & self , block : & Block ) -> Vec < Bytes > {
let mut result = Vec ::new ( ) ;
if block . index < LOCKER_BLOCK_START {
return result ;
@ -592,7 +694,7 @@ impl Chain {
count + = 1 ;
}
}
trace ! ( "Got lock ers for block {}: {:?}", block . index , & result ) ;
trace ! ( "Got sign ers for block {}: {:?}", block . index , & result ) ;
result
}