Major refactoring. Changed mining algorithm to Blakeout. Changed keypair mining algorithm.
This commit is contained in:
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "alfis"
|
name = "alfis"
|
||||||
version = "0.2.1"
|
version = "0.3.0"
|
||||||
authors = ["Revertron <alfis@revertron.com>"]
|
authors = ["Revertron <alfis@revertron.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
@@ -15,6 +15,7 @@ log = "0.4.14"
|
|||||||
toml = "0.5.8"
|
toml = "0.5.8"
|
||||||
simple_logger = "1.11.0"
|
simple_logger = "1.11.0"
|
||||||
rust-crypto = "^0.2"
|
rust-crypto = "^0.2"
|
||||||
|
blakeout = "0.1.0"
|
||||||
num_cpus = "1.13.0"
|
num_cpus = "1.13.0"
|
||||||
byteorder = "1.3.2"
|
byteorder = "1.3.2"
|
||||||
web-view = { version = "0.7.2", features = [] }
|
web-view = { version = "0.7.2", features = [] }
|
||||||
@@ -24,7 +25,6 @@ serde_json = "1.0.42"
|
|||||||
num-bigint = "0.2"
|
num-bigint = "0.2"
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
bincode = "1.2.0"
|
bincode = "1.2.0"
|
||||||
groestl = "0.8.0"
|
|
||||||
base64 = "0.11.0"
|
base64 = "0.11.0"
|
||||||
chrono = { version = "0.4.13", features = ["serde"] }
|
chrono = { version = "0.4.13", features = ["serde"] }
|
||||||
rand = "0.7.2"
|
rand = "0.7.2"
|
||||||
|
|||||||
+1
-11
@@ -5,9 +5,7 @@ extern crate num_traits;
|
|||||||
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
use crypto::sha2::Sha256;
|
use crate::bytes::Bytes;
|
||||||
use crypto::digest::Digest;
|
|
||||||
use crate::keys::Bytes;
|
|
||||||
use crate::Transaction;
|
use crate::Transaction;
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize, PartialEq, Debug)]
|
#[derive(Clone, Serialize, Deserialize, PartialEq, Debug)]
|
||||||
@@ -71,11 +69,3 @@ impl Block {
|
|||||||
Vec::from(serde_json::to_string(&self).unwrap().as_bytes())
|
Vec::from(serde_json::to_string(&self).unwrap().as_bytes())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hash(data: &[u8]) -> Bytes {
|
|
||||||
let mut buf: [u8; 32] = [0; 32];
|
|
||||||
let mut digest = Sha256::new();
|
|
||||||
digest.input(data);
|
|
||||||
digest.result(&mut buf);
|
|
||||||
Bytes::new(buf.to_vec())
|
|
||||||
}
|
|
||||||
@@ -1,15 +1,17 @@
|
|||||||
|
use std::cell::RefCell;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
use chrono::Utc;
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use log::{debug, error, info, trace, warn};
|
||||||
use sqlite::{Connection, State, Statement};
|
use sqlite::{Connection, State, Statement};
|
||||||
|
|
||||||
use crate::{Block, Bytes, Keystore, Transaction, hash_is_good};
|
use crate::{Block, Bytes, Keystore, Transaction};
|
||||||
|
use crate::blockchain::constants::{BLOCK_DIFFICULTY, CHAIN_VERSION, LOCKER_BLOCK_COUNT, LOCKER_BLOCK_INTERVAL, LOCKER_BLOCK_START, LOCKER_DIFFICULTY};
|
||||||
|
use crate::blockchain::enums::BlockQuality;
|
||||||
|
use crate::blockchain::enums::BlockQuality::*;
|
||||||
|
use crate::blockchain::hash_utils::*;
|
||||||
use crate::settings::Settings;
|
use crate::settings::Settings;
|
||||||
#[allow(unused_imports)]
|
|
||||||
use log::{trace, debug, info, warn, error};
|
|
||||||
use std::collections::HashSet;
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use chrono::Utc;
|
|
||||||
use crate::blockchain::transaction::hash_identity;
|
|
||||||
use crate::blockchain::blockchain::BlockQuality::*;
|
|
||||||
use crate::blockchain::{BLOCK_DIFFICULTY, CHAIN_VERSION, 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 (
|
const SQL_CREATE_TABLES: &str = "CREATE TABLE blocks (
|
||||||
@@ -37,7 +39,7 @@ const SQL_GET_PUBLIC_KEY_BY_ID: &str = "SELECT pub_key FROM transactions WHERE i
|
|||||||
const SQL_GET_ID_BY_ID: &str = "SELECT identity 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_TRANSACTION_BY_ID: &str = "SELECT * FROM transactions WHERE identity = ? ORDER BY id DESC LIMIT 1;";
|
||||||
|
|
||||||
pub struct Blockchain {
|
pub struct Chain {
|
||||||
origin: Bytes,
|
origin: Bytes,
|
||||||
pub version: u32,
|
pub version: u32,
|
||||||
pub blocks: Vec<Block>,
|
pub blocks: Vec<Block>,
|
||||||
@@ -48,12 +50,12 @@ pub struct Blockchain {
|
|||||||
zones: RefCell<HashSet<String>>,
|
zones: RefCell<HashSet<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Blockchain {
|
impl Chain {
|
||||||
pub fn new(settings: &Settings) -> Self {
|
pub fn new(settings: &Settings) -> Self {
|
||||||
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 {
|
let mut chain = Chain {
|
||||||
origin,
|
origin,
|
||||||
version: CHAIN_VERSION,
|
version: CHAIN_VERSION,
|
||||||
blocks: Vec::new(),
|
blocks: Vec::new(),
|
||||||
@@ -63,8 +65,8 @@ impl Blockchain {
|
|||||||
db,
|
db,
|
||||||
zones: RefCell::new(HashSet::new()),
|
zones: RefCell::new(HashSet::new()),
|
||||||
};
|
};
|
||||||
blockchain.init_db();
|
chain.init_db();
|
||||||
blockchain
|
chain
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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
|
||||||
@@ -147,21 +149,21 @@ impl Blockchain {
|
|||||||
statement.bind(7, transaction.to_string().as_str())?;
|
statement.bind(7, transaction.to_string().as_str())?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
statement.bind(8, block.prev_block_hash.as_bytes())?;
|
statement.bind(8, block.prev_block_hash.as_slice())?;
|
||||||
statement.bind(9, block.hash.as_bytes())?;
|
statement.bind(9, block.hash.as_slice())?;
|
||||||
statement.bind(10, block.pub_key.as_bytes())?;
|
statement.bind(10, block.pub_key.as_slice())?;
|
||||||
statement.bind(11, block.signature.as_bytes())?;
|
statement.bind(11, block.signature.as_slice())?;
|
||||||
statement.next()
|
statement.next()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds transaction to transactions table
|
/// Adds transaction to transactions table
|
||||||
fn add_transaction_to_table(&mut self, t: &Transaction) -> sqlite::Result<State> {
|
fn add_transaction_to_table(&mut self, t: &Transaction) -> sqlite::Result<State> {
|
||||||
let mut statement = self.db.prepare(SQL_ADD_TRANSACTION)?;
|
let mut statement = self.db.prepare(SQL_ADD_TRANSACTION)?;
|
||||||
statement.bind(1, t.identity.as_bytes())?;
|
statement.bind(1, t.identity.as_slice())?;
|
||||||
statement.bind(2, t.confirmation.as_bytes())?;
|
statement.bind(2, t.confirmation.as_slice())?;
|
||||||
statement.bind(3, t.method.as_ref() as &str)?;
|
statement.bind(3, t.method.as_ref() as &str)?;
|
||||||
statement.bind(4, t.data.as_ref() as &str)?;
|
statement.bind(4, t.data.as_ref() as &str)?;
|
||||||
statement.bind(5, t.pub_key.as_bytes())?;
|
statement.bind(5, t.pub_key.as_slice())?;
|
||||||
statement.next()
|
statement.next()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,7 +241,7 @@ impl Blockchain {
|
|||||||
/// Checks if this identity is free or is owned by the same pub_key
|
/// 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 {
|
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();
|
let mut statement = self.db.prepare(SQL_GET_PUBLIC_KEY_BY_ID).unwrap();
|
||||||
statement.bind(1, identity.as_bytes()).expect("Error in bind");
|
statement.bind(1, identity.as_slice()).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());
|
||||||
if !pub_key.eq(public_key) {
|
if !pub_key.eq(public_key) {
|
||||||
@@ -258,7 +260,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(SQL_GET_ID_BY_ID).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_slice()).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
|
||||||
self.zones.borrow_mut().insert(zone.to_owned());
|
self.zones.borrow_mut().insert(zone.to_owned());
|
||||||
@@ -275,7 +277,7 @@ impl Blockchain {
|
|||||||
let identity_hash = hash_identity(domain, None);
|
let identity_hash = hash_identity(domain, None);
|
||||||
|
|
||||||
let mut statement = self.db.prepare(SQL_GET_TRANSACTION_BY_ID).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_slice()).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());
|
||||||
let confirmation = Bytes::from_bytes(statement.read::<Vec<u8>>(2).unwrap().as_slice());
|
let confirmation = Bytes::from_bytes(statement.read::<Vec<u8>>(2).unwrap().as_slice());
|
||||||
@@ -343,7 +345,7 @@ impl Blockchain {
|
|||||||
warn!("Block difficulty is lower than needed");
|
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_slice(), block.difficulty as usize) {
|
||||||
warn!("Ignoring block with low difficulty:\n{:?}", &block);
|
warn!("Ignoring block with low difficulty:\n{:?}", &block);
|
||||||
return Bad;
|
return Bad;
|
||||||
}
|
}
|
||||||
@@ -457,26 +459,3 @@ impl Blockchain {
|
|||||||
Some(Block::from_all_params(index, timestamp, version, difficulty, random, nonce, prev_block_hash, hash, pub_key, signature, transaction))
|
Some(Block::from_all_params(index, timestamp, version, difficulty, random, nonce, prev_block_hash, hash, pub_key, signature, transaction))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
|
||||||
pub enum BlockQuality {
|
|
||||||
Good,
|
|
||||||
Twin,
|
|
||||||
Future,
|
|
||||||
Bad,
|
|
||||||
Fork,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn check_block_hash(block: &Block) -> bool {
|
|
||||||
let mut copy: Block = block.clone();
|
|
||||||
copy.hash = Bytes::default();
|
|
||||||
copy.signature = Bytes::default();
|
|
||||||
let data = serde_json::to_string(©).unwrap();
|
|
||||||
crate::blockchain::block::hash(data.as_bytes()) == block.hash
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn check_block_signature(block: &Block) -> bool {
|
|
||||||
let mut copy = block.clone();
|
|
||||||
copy.signature = Bytes::default();
|
|
||||||
Keystore::check(©.as_bytes(), copy.pub_key.as_bytes(), block.signature.as_bytes())
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
|
pub const CHAIN_VERSION: u32 = 1;
|
||||||
|
|
||||||
pub const BLOCK_DIFFICULTY: u32 = 24;
|
pub const BLOCK_DIFFICULTY: u32 = 24;
|
||||||
pub const LOCKER_DIFFICULTY: u32 = 18;
|
pub const LOCKER_DIFFICULTY: u32 = 18;
|
||||||
pub const CHAIN_VERSION: u32 = 1;
|
pub const KEYSTORE_DIFFICULTY: usize = 25;
|
||||||
|
|
||||||
pub const LOCKER_BLOCK_START: u64 = 10;
|
pub const LOCKER_BLOCK_START: u64 = 10;
|
||||||
pub const LOCKER_BLOCK_COUNT: u64 = 3;
|
pub const LOCKER_BLOCK_COUNT: u64 = 3;
|
||||||
pub const LOCKER_BLOCK_INTERVAL: i64 = 300;
|
pub const LOCKER_BLOCK_INTERVAL: i64 = 300;
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
/// Represents a result of block check on block's arrival
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
pub enum BlockQuality {
|
||||||
|
Good,
|
||||||
|
Twin,
|
||||||
|
Future,
|
||||||
|
Bad,
|
||||||
|
Fork,
|
||||||
|
}
|
||||||
@@ -33,11 +33,11 @@ impl DnsFilter for BlockchainFilter {
|
|||||||
}
|
}
|
||||||
debug!("Searching domain {} and record {}", &search, &subdomain);
|
debug!("Searching domain {} and record {}", &search, &subdomain);
|
||||||
|
|
||||||
let data = self.context.lock().unwrap().blockchain.get_domain_info(&search);
|
let data = self.context.lock().unwrap().chain.get_domain_info(&search);
|
||||||
match data {
|
match data {
|
||||||
None => {
|
None => {
|
||||||
debug!("Not found data for domain {}", &search);
|
debug!("Not found data for domain {}", &search);
|
||||||
if self.context.lock().unwrap().blockchain.is_zone_in_blockchain(parts[0]) {
|
if self.context.lock().unwrap().chain.is_zone_in_blockchain(parts[0]) {
|
||||||
// Create DnsPacket
|
// Create DnsPacket
|
||||||
let mut packet = DnsPacket::new();
|
let mut packet = DnsPacket::new();
|
||||||
packet.questions.push(DnsQuestion::new(String::from(qname), qtype));
|
packet.questions.push(DnsQuestion::new(String::from(qname), qtype));
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
use blakeout::Blakeout;
|
||||||
|
use crypto::digest::Digest;
|
||||||
|
use crypto::sha2::Sha256;
|
||||||
|
use num_bigint::BigUint;
|
||||||
|
use num_traits::One;
|
||||||
|
|
||||||
|
use crate::{Block, Bytes, Keystore};
|
||||||
|
|
||||||
|
/// Creates needed hasher by current blockchain version
|
||||||
|
fn get_hasher_for_version(version: u32) -> Box<dyn Digest> {
|
||||||
|
match version {
|
||||||
|
2 => Box::new(Blakeout::default()),
|
||||||
|
_ => Box::new(Sha256::new())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks block's hash and returns true on valid hash or false otherwise
|
||||||
|
pub fn check_block_hash(block: &Block) -> bool {
|
||||||
|
let mut copy: Block = block.clone();
|
||||||
|
copy.hash = Bytes::default();
|
||||||
|
copy.signature = Bytes::default();
|
||||||
|
let data = serde_json::to_string(©).unwrap();
|
||||||
|
let mut hasher = get_hasher_for_version(block.version);
|
||||||
|
hash_data(&mut *hasher, data.as_bytes()) == block.hash
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hashes data by given hasher
|
||||||
|
pub fn hash_data(digest: &mut dyn Digest, data: &[u8]) -> Bytes {
|
||||||
|
let mut buf = match digest.output_bytes() {
|
||||||
|
32 => Bytes::zero32(),
|
||||||
|
64 => Bytes::zero64(),
|
||||||
|
_ => panic!("Supplied wrong digest!")
|
||||||
|
};
|
||||||
|
|
||||||
|
digest.input(data);
|
||||||
|
digest.result(buf.as_mut_slice());
|
||||||
|
buf
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks block's signature, returns true if the signature is valid, false otherwise
|
||||||
|
pub fn check_block_signature(block: &Block) -> bool {
|
||||||
|
let mut copy = block.clone();
|
||||||
|
copy.signature = Bytes::default();
|
||||||
|
Keystore::check(©.as_bytes(), copy.pub_key.as_slice(), block.signature.as_slice())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hashes some identity (domain in case of DNS). If you give it a public key, it will hash with it as well.
|
||||||
|
/// Giving public key is needed to create a confirmation field in [Transaction]
|
||||||
|
pub fn hash_identity(identity: &str, key: Option<&Bytes>) -> Bytes {
|
||||||
|
let mut buf: [u8; 32] = [0; 32];
|
||||||
|
let mut digest = Sha256::new();
|
||||||
|
digest.input_str(identity);
|
||||||
|
if let Some(key) = key {
|
||||||
|
digest.input(key.as_slice());
|
||||||
|
}
|
||||||
|
digest.result(&mut buf);
|
||||||
|
Bytes::from_bytes(&buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// There is no default PartialEq implementation for arrays > 32 in size
|
||||||
|
pub fn same_hash(left: &[u8], right: &[u8]) -> bool {
|
||||||
|
if left.len() != right.len() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// We iterate whole slices to eliminate timing attacks
|
||||||
|
let mut result = true;
|
||||||
|
for (x, y) in left.iter().zip(right) {
|
||||||
|
if x != y {
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if this hash contains enough zeroes
|
||||||
|
pub fn hash_is_good(hash: &[u8], difficulty: usize) -> bool {
|
||||||
|
let target = BigUint::one() << ((hash.len() << 3) - difficulty);
|
||||||
|
let hash_int = BigUint::from_bytes_be(&hash);
|
||||||
|
|
||||||
|
return hash_int < target;
|
||||||
|
}
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
pub mod transaction;
|
pub mod transaction;
|
||||||
pub mod block;
|
pub mod block;
|
||||||
pub mod blockchain;
|
pub mod chain;
|
||||||
pub mod filter;
|
pub mod filter;
|
||||||
pub mod constants;
|
pub mod constants;
|
||||||
|
pub mod hash_utils;
|
||||||
|
pub mod enums;
|
||||||
|
|
||||||
pub use transaction::Transaction;
|
pub use transaction::Transaction;
|
||||||
pub use block::Block;
|
pub use block::Block;
|
||||||
pub use blockchain::Blockchain;
|
pub use chain::Chain;
|
||||||
pub use constants::*;
|
pub use constants::*;
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
use crate::keys::*;
|
use std::fmt;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize, Serializer};
|
||||||
|
use serde::ser::SerializeStruct;
|
||||||
|
|
||||||
|
use crate::blockchain::hash_utils::*;
|
||||||
|
use crate::bytes::Bytes;
|
||||||
|
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
|
|
||||||
use serde::{Serialize, Deserialize, Serializer};
|
|
||||||
use serde::ser::SerializeStruct;
|
|
||||||
use std::fmt;
|
|
||||||
use crypto::sha2::Sha256;
|
|
||||||
use crypto::digest::Digest;
|
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, PartialEq)]
|
#[derive(Clone, Deserialize, PartialEq)]
|
||||||
pub struct Transaction {
|
pub struct Transaction {
|
||||||
pub identity: Bytes,
|
pub identity: Bytes,
|
||||||
@@ -76,14 +76,3 @@ impl Serialize for Transaction {
|
|||||||
structure.end()
|
structure.end()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hash_identity(identity: &str, key: Option<&Bytes>) -> Bytes {
|
|
||||||
let mut buf: [u8; 32] = [0; 32];
|
|
||||||
let mut digest = Sha256::new();
|
|
||||||
digest.input_str(identity);
|
|
||||||
if let Some(key) = key {
|
|
||||||
digest.input(key.as_bytes());
|
|
||||||
}
|
|
||||||
digest.result(&mut buf);
|
|
||||||
Bytes::from_bytes(&buf)
|
|
||||||
}
|
|
||||||
+170
@@ -0,0 +1,170 @@
|
|||||||
|
extern crate serde;
|
||||||
|
extern crate serde_json;
|
||||||
|
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
use std::convert::TryInto;
|
||||||
|
use std::fmt;
|
||||||
|
use std::fmt::{Formatter, Error};
|
||||||
|
|
||||||
|
use num_bigint::BigUint;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
// For deserialization
|
||||||
|
use serde::de::{Error as DeError, Visitor};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Bytes {
|
||||||
|
data: Vec<u8>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Bytes {
|
||||||
|
pub fn new(data: Vec<u8>) -> Self {
|
||||||
|
Bytes { data }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_bytes(data: &[u8]) -> Self {
|
||||||
|
Bytes { data: Vec::from(data) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn length(&self) -> usize {
|
||||||
|
self.data.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.data.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_zero(&self) -> bool {
|
||||||
|
if self.data.is_empty() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for x in self.data.iter() {
|
||||||
|
if *x != 0 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a byte slice of the hash contents.
|
||||||
|
pub fn as_slice(&self) -> &[u8] {
|
||||||
|
self.data.as_slice()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a mutable byte slice (to fill by hasher)
|
||||||
|
pub fn as_mut_slice(&mut self) -> &mut [u8] {
|
||||||
|
self.data.as_mut_slice()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_vec(&self) -> &Vec<u8> {
|
||||||
|
&self.data
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string(&self) -> String {
|
||||||
|
crate::utils::to_hex(&self.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_tail_u64(&self) -> u64 {
|
||||||
|
let index = self.data.len() - 8;
|
||||||
|
let bytes: [u8; 8] = self.data[index..].try_into().unwrap();
|
||||||
|
u64::from_be_bytes(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn zero32() -> Self {
|
||||||
|
Bytes { data: [0u8; 32].to_vec() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn zero64() -> Self {
|
||||||
|
Bytes { data: [0u8; 64].to_vec() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Bytes {
|
||||||
|
fn default() -> Bytes {
|
||||||
|
Bytes { data: Vec::new() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Bytes {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
crate::blockchain::hash_utils::same_hash(&self.data, &other.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ne(&self, other: &Self) -> bool {
|
||||||
|
!crate::blockchain::hash_utils::same_hash(&self.data, &other.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for Bytes {}
|
||||||
|
|
||||||
|
impl PartialOrd for Bytes {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
let self_hash_int = BigUint::from_bytes_be(&self.data);
|
||||||
|
let other_hash_int = BigUint::from_bytes_be(&other.data);
|
||||||
|
Some(self_hash_int.cmp(&other_hash_int))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for Bytes {
|
||||||
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
let self_hash_int = BigUint::from_bytes_be(&self.data);
|
||||||
|
let other_hash_int = BigUint::from_bytes_be(&other.data);
|
||||||
|
self_hash_int.cmp(&other_hash_int)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Bytes {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
fmt.write_str(&crate::utils::to_hex(&self.data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for Bytes {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error> where
|
||||||
|
S: Serializer {
|
||||||
|
serializer.serialize_str(&crate::utils::to_hex(&self.data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct BytesVisitor;
|
||||||
|
|
||||||
|
impl<'de> Visitor<'de> for BytesVisitor {
|
||||||
|
type Value = Bytes;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut Formatter) -> Result<(), Error> {
|
||||||
|
formatter.write_str("32 or 64 bytes")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> where E: DeError, {
|
||||||
|
if value.len() == 64 || value.len() == 128 {
|
||||||
|
Ok(Bytes::new(crate::from_hex(value).unwrap()))
|
||||||
|
} else {
|
||||||
|
Err(E::custom("Key must be 32 or 64 bytes!"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E> where E: DeError, {
|
||||||
|
if value.len() == 32 || value.len() == 64 {
|
||||||
|
Ok(Bytes::from_bytes(value))
|
||||||
|
} else {
|
||||||
|
Err(E::custom("Key must be 32 or 64 bytes!"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'dd> Deserialize<'dd> for Bytes {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'dd>>::Error> where D: Deserializer<'dd> {
|
||||||
|
deserializer.deserialize_str(BytesVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::bytes::Bytes;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn test_tail_bytes() {
|
||||||
|
let bytes = Bytes::new(vec![0, 255, 255, 255, 0, 255, 255, 255]);
|
||||||
|
assert_eq!(bytes.get_tail_u64(), 72057589759737855u64);
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
-7
@@ -1,20 +1,19 @@
|
|||||||
use crate::{Blockchain, Bus, Keystore};
|
use crate::{Chain, Bus, Keystore, Settings};
|
||||||
use crate::event::Event;
|
use crate::event::Event;
|
||||||
use crate::settings::Settings;
|
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use log::{trace, debug, info, warn, error};
|
use log::{trace, debug, info, warn, error};
|
||||||
|
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
pub settings: Settings,
|
pub settings: Settings,
|
||||||
pub keystore: Keystore,
|
pub keystore: Keystore,
|
||||||
pub blockchain: Blockchain,
|
pub chain: Chain,
|
||||||
pub bus: Bus<Event>,
|
pub bus: Bus<Event>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Context {
|
impl Context {
|
||||||
/// Creating an essential context to work with
|
/// Creating an essential context to work with
|
||||||
pub fn new(settings: Settings, keystore: Keystore, blockchain: Blockchain) -> Context {
|
pub fn new(settings: Settings, keystore: Keystore, chain: Chain) -> Context {
|
||||||
Context { settings, keystore, blockchain, bus: Bus::new() }
|
Context { settings, keystore, chain, bus: Bus::new() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Load keystore and return Context
|
/// Load keystore and return Context
|
||||||
@@ -39,7 +38,7 @@ impl Context {
|
|||||||
self.keystore = keystore;
|
self.keystore = keystore;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_blockchain(&self) -> &Blockchain {
|
pub fn get_chain(&self) -> &Chain {
|
||||||
&self.blockchain
|
&self.chain
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use crate::{Context, Settings};
|
||||||
|
use crate::blockchain::filter::BlockchainFilter;
|
||||||
|
use crate::dns::server::{DnsServer, DnsUdpServer, DnsTcpServer};
|
||||||
|
use crate::dns::context::{ServerContext, ResolveStrategy};
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use log::{debug, error, info, LevelFilter, trace, warn};
|
||||||
|
|
||||||
|
/// Starts UDP and TCP DNS-servers
|
||||||
|
pub fn start_dns_server(context: &Arc<Mutex<Context>>, settings: &Settings) {
|
||||||
|
let server_context = create_server_context(context.clone(), &settings);
|
||||||
|
|
||||||
|
if server_context.enable_udp {
|
||||||
|
let udp_server = DnsUdpServer::new(server_context.clone(), settings.dns.threads);
|
||||||
|
if let Err(e) = udp_server.run_server() {
|
||||||
|
error!("Failed to bind UDP listener: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if server_context.enable_tcp {
|
||||||
|
let tcp_server = DnsTcpServer::new(server_context.clone(), settings.dns.threads);
|
||||||
|
if let Err(e) = tcp_server.run_server() {
|
||||||
|
error!("Failed to bind TCP listener: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates DNS-context with all needed settings
|
||||||
|
fn create_server_context(context: Arc<Mutex<Context>>, settings: &Settings) -> Arc<ServerContext> {
|
||||||
|
let mut server_context = ServerContext::new();
|
||||||
|
server_context.allow_recursive = true;
|
||||||
|
server_context.dns_listen = settings.dns.listen.clone();
|
||||||
|
server_context.resolve_strategy = match settings.dns.forwarders.is_empty() {
|
||||||
|
true => { ResolveStrategy::Recursive }
|
||||||
|
false => { ResolveStrategy::Forward { upstreams: settings.dns.forwarders.clone() } }
|
||||||
|
};
|
||||||
|
server_context.filters.push(Box::new(BlockchainFilter::new(context)));
|
||||||
|
match server_context.initialize() {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(e) => { panic!("DNS server failed to initialize: {:?}", e); }
|
||||||
|
}
|
||||||
|
|
||||||
|
Arc::new(server_context)
|
||||||
|
}
|
||||||
+3
-3
@@ -6,9 +6,9 @@ pub enum Event {
|
|||||||
MinerStopped,
|
MinerStopped,
|
||||||
KeyGeneratorStarted,
|
KeyGeneratorStarted,
|
||||||
KeyGeneratorStopped,
|
KeyGeneratorStopped,
|
||||||
KeyCreated { path: String, public: String },
|
KeyCreated { path: String, public: String, hash: String },
|
||||||
KeyLoaded { path: String, public: String },
|
KeyLoaded { path: String, public: String, hash: String },
|
||||||
KeySaved { path: String, public: String },
|
KeySaved { path: String, public: String, hash: String },
|
||||||
NewBlockReceived,
|
NewBlockReceived,
|
||||||
BlockchainChanged,
|
BlockchainChanged,
|
||||||
ActionStopMining,
|
ActionStopMining,
|
||||||
|
|||||||
+96
-155
@@ -2,29 +2,39 @@ extern crate crypto;
|
|||||||
extern crate serde;
|
extern crate serde;
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
|
|
||||||
use crypto::ed25519::{keypair, signature, verify};
|
use std::thread;
|
||||||
use rand::{thread_rng, Rng};
|
|
||||||
use std::fmt;
|
|
||||||
use std::fmt::{Error, Formatter};
|
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use serde::{Serialize, Deserialize, Serializer, Deserializer};
|
use std::sync::{Arc, atomic, Mutex};
|
||||||
// For deserialization
|
use std::sync::atomic::{AtomicBool, AtomicUsize};
|
||||||
use serde::de::{Error as DeError, Visitor};
|
|
||||||
|
use crypto::ed25519::{keypair, signature, verify};
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use log::{trace, debug, info, warn, error};
|
use log::{debug, error, info, trace, warn};
|
||||||
use crate::hash_is_good;
|
use rand::{Rng, RngCore, thread_rng};
|
||||||
use std::cmp::Ordering;
|
use serde::{Deserialize, Serialize};
|
||||||
use num_bigint::BigUint;
|
#[cfg(not(target_os = "macos"))]
|
||||||
use std::convert::TryInto;
|
use thread_priority::{set_current_thread_priority, ThreadPriority};
|
||||||
|
|
||||||
|
use crate::blockchain::hash_utils::*;
|
||||||
|
use crate::Context;
|
||||||
|
use crate::event::Event;
|
||||||
|
use crate::blockchain::KEYSTORE_DIFFICULTY;
|
||||||
|
use crate::bytes::Bytes;
|
||||||
|
use blakeout::Blakeout;
|
||||||
|
use self::crypto::digest::Digest;
|
||||||
|
use std::time::Instant;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct Keystore {
|
pub struct Keystore {
|
||||||
private_key: Bytes,
|
private_key: Bytes,
|
||||||
public_key: Bytes,
|
public_key: Bytes,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
|
hash: RefCell<Bytes>,
|
||||||
|
#[serde(skip)]
|
||||||
path: String,
|
path: String,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
seed: Vec<u8>,
|
seed: Vec<u8>,
|
||||||
@@ -36,12 +46,12 @@ impl Keystore {
|
|||||||
let mut rng = thread_rng();
|
let mut rng = thread_rng();
|
||||||
rng.fill(&mut buf);
|
rng.fill(&mut buf);
|
||||||
let (private, public) = keypair(&buf);
|
let (private, public) = keypair(&buf);
|
||||||
Keystore { private_key: Bytes::from_bytes(&private), public_key: Bytes::from_bytes(&public), path: String::new(), seed: Vec::from(&buf[..]) }
|
Keystore { private_key: Bytes::from_bytes(&private), public_key: Bytes::from_bytes(&public), hash: RefCell::new(Bytes::default()), path: String::new(), seed: Vec::from(&buf[..]) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_bytes(seed: &[u8]) -> Self {
|
pub fn from_bytes(seed: &[u8]) -> Self {
|
||||||
let (private, public) = keypair(&seed);
|
let (private, public) = keypair(&seed);
|
||||||
Keystore { private_key: Bytes::from_bytes(&private), public_key: Bytes::from_bytes(&public), path: String::new(), seed: Vec::from(seed) }
|
Keystore { private_key: Bytes::from_bytes(&private), public_key: Bytes::from_bytes(&public), hash: RefCell::new(Bytes::default()), path: String::new(), seed: Vec::from(seed) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_file(filename: &str, _password: &str) -> Option<Self> {
|
pub fn from_file(filename: &str, _password: &str) -> Option<Self> {
|
||||||
@@ -82,159 +92,96 @@ impl Keystore {
|
|||||||
&self.path
|
&self.path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_hash(&self) -> Bytes {
|
||||||
|
if self.hash.borrow().is_empty() {
|
||||||
|
self.hash.replace(hash_data(&mut Blakeout::default(), &self.public_key.as_slice()));
|
||||||
|
}
|
||||||
|
self.hash.borrow().clone()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn sign(&self, message: &[u8]) -> [u8; 64] {
|
pub fn sign(&self, message: &[u8]) -> [u8; 64] {
|
||||||
signature(message, self.private_key.data.as_slice())
|
signature(message, self.private_key.as_slice())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check(message: &[u8], public_key: &[u8], signature: &[u8]) -> bool {
|
pub fn check(message: &[u8], public_key: &[u8], signature: &[u8]) -> bool {
|
||||||
verify(message, public_key, signature)
|
verify(message, public_key, signature)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hash_is_good(&self, difficulty: usize) -> bool {
|
|
||||||
hash_is_good(self.public_key.as_bytes(), difficulty)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
pub fn create_key(context: Arc<Mutex<Context>>) {
|
||||||
pub struct Bytes {
|
let mining = Arc::new(AtomicBool::new(true));
|
||||||
data: Vec<u8>
|
let miners_count = Arc::new(AtomicUsize::new(0));
|
||||||
}
|
{ context.lock().unwrap().bus.post(Event::KeyGeneratorStarted); }
|
||||||
|
for _ in 0..num_cpus::get() {
|
||||||
impl Bytes {
|
let context = context.clone();
|
||||||
pub fn new(data: Vec<u8>) -> Self {
|
let mining = mining.clone();
|
||||||
Bytes { data }
|
let miners_count = miners_count.clone();
|
||||||
}
|
thread::spawn(move || {
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
pub fn from_bytes(data: &[u8]) -> Self {
|
let _ = set_current_thread_priority(ThreadPriority::Min);
|
||||||
Bytes { data: Vec::from(data) }
|
miners_count.fetch_add(1, atomic::Ordering::SeqCst);
|
||||||
}
|
match generate_key(KEYSTORE_DIFFICULTY, mining.clone()) {
|
||||||
|
None => {
|
||||||
pub fn length(&self) -> usize {
|
debug!("Keystore mining finished");
|
||||||
self.data.len()
|
}
|
||||||
}
|
Some(keystore) => {
|
||||||
|
mining.store(false, atomic::Ordering::SeqCst);
|
||||||
pub fn is_empty(&self) -> bool {
|
let mut context = context.lock().unwrap();
|
||||||
self.data.is_empty()
|
let hash = keystore.get_hash().to_string();
|
||||||
}
|
info!("Key mined successfully: {:?}, hash: {}", &keystore.get_public(), &hash);
|
||||||
|
context.bus.post(Event::KeyCreated { path: keystore.get_path().to_owned(), public: keystore.get_public().to_string(), hash });
|
||||||
pub fn is_zero(&self) -> bool {
|
context.set_keystore(keystore);
|
||||||
if self.data.is_empty() {
|
}
|
||||||
return true;
|
|
||||||
}
|
|
||||||
for x in self.data.iter() {
|
|
||||||
if *x != 0 {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
let miners = miners_count.fetch_sub(1, atomic::Ordering::SeqCst) - 1;
|
||||||
return true;
|
if miners == 0 {
|
||||||
|
context.lock().unwrap().bus.post(Event::KeyGeneratorStopped);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
context.lock().unwrap().bus.register(move |_uuid, e| {
|
||||||
/// Returns a byte slice of the hash contents.
|
if e == Event::ActionStopMining {
|
||||||
pub fn as_bytes(&self) -> &[u8] {
|
info!("Stopping keystore miner");
|
||||||
self.data.as_slice()
|
mining.store(false, atomic::Ordering::SeqCst);
|
||||||
}
|
false
|
||||||
|
|
||||||
pub fn to_string(&self) -> String {
|
|
||||||
crate::utils::to_hex(&self.data)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_tail_u64(&self) -> u64 {
|
|
||||||
let index = self.data.len() - 8;
|
|
||||||
let bytes: [u8; 8] = self.data[index..].try_into().unwrap();
|
|
||||||
u64::from_be_bytes(bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn zero32() -> Self {
|
|
||||||
Bytes { data: [0u8; 32].to_vec() }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn zero64() -> Self {
|
|
||||||
Bytes { data: [0u8; 64].to_vec() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Bytes {
|
|
||||||
fn default() -> Bytes {
|
|
||||||
Bytes { data: Vec::new() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for Bytes {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
crate::utils::same_hash(&self.data, &other.data)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ne(&self, other: &Self) -> bool {
|
|
||||||
!crate::utils::same_hash(&self.data, &other.data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for Bytes {}
|
|
||||||
|
|
||||||
impl PartialOrd for Bytes {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
||||||
let self_hash_int = BigUint::from_bytes_be(&self.data);
|
|
||||||
let other_hash_int = BigUint::from_bytes_be(&other.data);
|
|
||||||
Some(self_hash_int.cmp(&other_hash_int))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ord for Bytes {
|
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
|
||||||
let self_hash_int = BigUint::from_bytes_be(&self.data);
|
|
||||||
let other_hash_int = BigUint::from_bytes_be(&other.data);
|
|
||||||
self_hash_int.cmp(&other_hash_int)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for Bytes {
|
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
fmt.write_str(&crate::utils::to_hex(&self.data))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Serialize for Bytes {
|
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error> where
|
|
||||||
S: Serializer {
|
|
||||||
serializer.serialize_str(&crate::utils::to_hex(&self.data))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct BytesVisitor;
|
|
||||||
|
|
||||||
impl<'de> Visitor<'de> for BytesVisitor {
|
|
||||||
type Value = Bytes;
|
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut Formatter) -> Result<(), Error> {
|
|
||||||
formatter.write_str("32 or 64 bytes")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> where E: DeError, {
|
|
||||||
if value.len() == 64 || value.len() == 128 {
|
|
||||||
Ok(Bytes::new(crate::from_hex(value).unwrap()))
|
|
||||||
} else {
|
} else {
|
||||||
Err(E::custom("Key must be 32 or 64 bytes!"))
|
true
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E> where E: DeError, {
|
|
||||||
if value.len() == 32 || value.len() == 64 {
|
|
||||||
Ok(Bytes::from_bytes(value))
|
|
||||||
} else {
|
|
||||||
Err(E::custom("Key must be 32 or 64 bytes!"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'dd> Deserialize<'dd> for Bytes {
|
fn generate_key(difficulty: usize, mining: Arc<AtomicBool>) -> Option<Keystore> {
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'dd>>::Error> where D: Deserializer<'dd> {
|
let mut rng = rand::thread_rng();
|
||||||
deserializer.deserialize_str(BytesVisitor)
|
let mut buf = [0u8; 32];
|
||||||
|
let mut digest = Blakeout::default();
|
||||||
|
let mut time = Instant::now();
|
||||||
|
let mut count = 0u128;
|
||||||
|
loop {
|
||||||
|
rng.fill_bytes(&mut buf);
|
||||||
|
let keystore = Keystore::from_bytes(&buf);
|
||||||
|
digest.reset();
|
||||||
|
digest.input(keystore.public_key.as_slice());
|
||||||
|
digest.result(&mut buf);
|
||||||
|
if hash_is_good(&buf, difficulty) {
|
||||||
|
info!("Generated keypair: {:?}", &keystore);
|
||||||
|
return Some(keystore);
|
||||||
|
}
|
||||||
|
if !mining.load(atomic::Ordering::SeqCst) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let elapsed = time.elapsed().as_millis();
|
||||||
|
if elapsed >= 60000 {
|
||||||
|
debug!("Mining speed {} H/s", count / 60);
|
||||||
|
time = Instant::now();
|
||||||
|
count = 0;
|
||||||
|
}
|
||||||
|
count += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{Keystore, Bytes};
|
use crate::{Bytes, Keystore};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn test_signature() {
|
pub fn test_signature() {
|
||||||
@@ -242,12 +189,6 @@ mod tests {
|
|||||||
let data = b"{ identity: 178135D209C697625E3EC71DA5C760382E54936F824EE5083908DA66B14ECE18,\
|
let data = b"{ identity: 178135D209C697625E3EC71DA5C760382E54936F824EE5083908DA66B14ECE18,\
|
||||||
confirmation: A4A0AFECD1A511825226F0D3437C6C6BDAE83554040AA7AEB49DEFEAB0AE9EA4 }";
|
confirmation: A4A0AFECD1A511825226F0D3437C6C6BDAE83554040AA7AEB49DEFEAB0AE9EA4 }";
|
||||||
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_slice(), &signature), "Wrong signature!")
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
pub fn test_tail_bytes() {
|
|
||||||
let bytes = Bytes::new(vec![0, 255, 255, 255, 0, 255, 255, 255]);
|
|
||||||
assert_eq!(bytes.get_tail_u64(), 72057589759737855u64);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+7
-3
@@ -1,10 +1,12 @@
|
|||||||
pub use blockchain::block::Block;
|
pub use blockchain::block::Block;
|
||||||
pub use blockchain::transaction::Transaction;
|
pub use blockchain::transaction::Transaction;
|
||||||
|
|
||||||
pub use crate::blockchain::Blockchain;
|
pub use crate::blockchain::Chain;
|
||||||
pub use crate::context::Context;
|
pub use crate::context::Context;
|
||||||
pub use settings::Settings;
|
pub use crate::miner::Miner;
|
||||||
pub use crate::keys::Bytes;
|
pub use crate::p2p::Network;
|
||||||
|
pub use crate::settings::Settings;
|
||||||
|
pub use crate::bytes::Bytes;
|
||||||
pub use crate::keys::Keystore;
|
pub use crate::keys::Keystore;
|
||||||
pub use crate::simplebus::*;
|
pub use crate::simplebus::*;
|
||||||
pub use crate::utils::*;
|
pub use crate::utils::*;
|
||||||
@@ -18,5 +20,7 @@ pub mod context;
|
|||||||
pub mod event;
|
pub mod event;
|
||||||
pub mod p2p;
|
pub mod p2p;
|
||||||
pub mod dns;
|
pub mod dns;
|
||||||
|
pub mod dns_utils;
|
||||||
pub mod settings;
|
pub mod settings;
|
||||||
|
pub mod bytes;
|
||||||
|
|
||||||
|
|||||||
+18
-412
@@ -2,44 +2,25 @@
|
|||||||
// This is silently ignored on non-windows systems.
|
// This is silently ignored on non-windows systems.
|
||||||
// See https://msdn.microsoft.com/en-us/library/4cc7ya5b.aspx for more details.
|
// See https://msdn.microsoft.com/en-us/library/4cc7ya5b.aspx for more details.
|
||||||
#![windows_subsystem = "windows"]
|
#![windows_subsystem = "windows"]
|
||||||
extern crate web_view;
|
|
||||||
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};
|
||||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::Duration;
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
use winapi::um::wincon::{AttachConsole, FreeConsole, ATTACH_PARENT_PROCESS};
|
|
||||||
|
|
||||||
use rand::RngCore;
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
|
||||||
use thread_priority::*;
|
|
||||||
use serde::Deserialize;
|
|
||||||
use web_view::*;
|
|
||||||
use getopts::Options;
|
use getopts::Options;
|
||||||
use simple_logger::{SimpleLogger};
|
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use log::{trace, debug, info, warn, error, LevelFilter};
|
use log::{debug, error, info, LevelFilter, trace, warn};
|
||||||
|
use simple_logger::SimpleLogger;
|
||||||
|
#[cfg(windows)]
|
||||||
|
use winapi::um::wincon::{ATTACH_PARENT_PROCESS, AttachConsole, FreeConsole};
|
||||||
|
|
||||||
use alfis::{Blockchain, Bytes, Context, Keystore, Transaction, check_domain, Block};
|
use alfis::{Block, Bytes, Chain, Miner, Context, Network, Settings, dns_utils, Keystore};
|
||||||
use alfis::event::Event;
|
|
||||||
use alfis::miner::Miner;
|
mod web_ui;
|
||||||
use alfis::p2p::Network;
|
|
||||||
use alfis::settings::Settings;
|
|
||||||
use alfis::dns::context::{ServerContext, ResolveStrategy};
|
|
||||||
use alfis::dns::server::{DnsServer, DnsUdpServer, DnsTcpServer};
|
|
||||||
use alfis::dns::protocol::DnsRecord;
|
|
||||||
use alfis::blockchain::filter::BlockchainFilter;
|
|
||||||
|
|
||||||
const KEYSTORE_DIFFICULTY: usize = 24;
|
|
||||||
const SETTINGS_FILENAME: &str = "alfis.toml";
|
const SETTINGS_FILENAME: &str = "alfis.toml";
|
||||||
const LOG_TARGET_MAIN: &str = "alfis::Main";
|
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
|
||||||
@@ -95,23 +76,23 @@ fn main() {
|
|||||||
}
|
}
|
||||||
Some(keystore) => { keystore }
|
Some(keystore) => { keystore }
|
||||||
};
|
};
|
||||||
let blockchain: Blockchain = Blockchain::new(&settings);
|
let chain: Chain = Chain::new(&settings);
|
||||||
if opt_matches.opt_present("l") {
|
if opt_matches.opt_present("l") {
|
||||||
for i in 1..(blockchain.height() + 1) {
|
for i in 1..(chain.height() + 1) {
|
||||||
if let Some(block) = blockchain.get_block(i) {
|
if let Some(block) = chain.get_block(i) {
|
||||||
info!(target: LOG_TARGET_MAIN, "{:?}", &block);
|
info!(target: LOG_TARGET_MAIN, "{:?}", &block);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
match blockchain.get_block(1) {
|
match chain.get_block(1) {
|
||||||
None => { info!(target: LOG_TARGET_MAIN, "No blocks found in DB"); }
|
None => { info!(target: LOG_TARGET_MAIN, "No blocks found in DB"); }
|
||||||
Some(block) => { trace!(target: LOG_TARGET_MAIN, "Loaded DB with origin {:?}", &block.hash); }
|
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, chain)));
|
||||||
start_dns_server(&context, &settings_copy);
|
dns_utils::start_dns_server(&context, &settings_copy);
|
||||||
|
|
||||||
let mut miner_obj = Miner::new(context.clone());
|
let mut miner_obj = Miner::new(context.clone());
|
||||||
miner_obj.start_mining_thread();
|
miner_obj.start_mining_thread();
|
||||||
@@ -127,7 +108,7 @@ fn main() {
|
|||||||
thread::sleep(sleep);
|
thread::sleep(sleep);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
run_interface(context.clone(), miner.clone());
|
web_ui::run_interface(context.clone(), miner.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Without explicitly detaching the console cmd won't redraw it's prompt.
|
// Without explicitly detaching the console cmd won't redraw it's prompt.
|
||||||
@@ -137,393 +118,18 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_dns_server(context: &Arc<Mutex<Context>>, settings: &Settings) {
|
|
||||||
let server_context = create_server_context(context.clone(), &settings);
|
|
||||||
|
|
||||||
if server_context.enable_udp {
|
|
||||||
let udp_server = DnsUdpServer::new(server_context.clone(), settings.dns.threads);
|
|
||||||
if let Err(e) = udp_server.run_server() {
|
|
||||||
error!(target: LOG_TARGET_MAIN, "Failed to bind UDP listener: {:?}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if server_context.enable_tcp {
|
|
||||||
let tcp_server = DnsTcpServer::new(server_context.clone(), settings.dns.threads);
|
|
||||||
if let Err(e) = tcp_server.run_server() {
|
|
||||||
error!(target: LOG_TARGET_MAIN, "Failed to bind TCP listener: {:?}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_genesis_if_needed(context: &Arc<Mutex<Context>>, miner: &Arc<Mutex<Miner>>) {
|
fn create_genesis_if_needed(context: &Arc<Mutex<Context>>, miner: &Arc<Mutex<Miner>>) {
|
||||||
// If there is no origin in settings and no blockchain in DB, generate genesis block
|
// If there is no origin in settings and no blockchain in DB, generate genesis block
|
||||||
let context = context.lock().unwrap();
|
let context = context.lock().unwrap();
|
||||||
let last_block = context.get_blockchain().last_block();
|
let last_block = context.get_chain().last_block();
|
||||||
let origin = context.settings.origin.clone();
|
let origin = context.settings.origin.clone();
|
||||||
if origin.eq("") && last_block.is_none() {
|
if origin.eq("") && last_block.is_none() {
|
||||||
// If blockchain is empty, we are going to mine a Genesis block
|
// If blockchain is empty, we are going to mine a Genesis block
|
||||||
create_genesis(miner.clone(), &context.get_keystore());
|
let block = Block::new(None, context.get_keystore().get_public(), Bytes::default());
|
||||||
|
miner.lock().unwrap().add_block(block);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
|
|
||||||
let file_content = include_str!("webview/index.html");
|
|
||||||
let mut styles = inline_style(include_str!("webview/bulma.css"));
|
|
||||||
styles.push_str(&inline_style(include_str!("webview/busy_indicator.css")));
|
|
||||||
let scripts = inline_script(include_str!("webview/scripts.js"));
|
|
||||||
|
|
||||||
let html = Content::Html(file_content.to_owned().replace("{styles}", &styles).replace("{scripts}", &scripts));
|
|
||||||
let title = format!("ALFIS {}", env!("CARGO_PKG_VERSION"));
|
|
||||||
let mut interface = web_view::builder()
|
|
||||||
.title(&title)
|
|
||||||
.content(html)
|
|
||||||
.size(1023, 720)
|
|
||||||
.min_size(895, 350)
|
|
||||||
.resizable(true)
|
|
||||||
.debug(false)
|
|
||||||
.user_data(())
|
|
||||||
.invoke_handler(|web_view, arg| {
|
|
||||||
use Cmd::*;
|
|
||||||
debug!(target: LOG_TARGET_UI, "Command {}", arg);
|
|
||||||
match serde_json::from_str(arg).unwrap() {
|
|
||||||
Loaded => {
|
|
||||||
web_view.eval("showMiningIndicator(false, false);").expect("Error evaluating!");
|
|
||||||
let handle = web_view.handle();
|
|
||||||
let mut status = Status::new();
|
|
||||||
let mut c = context.lock().unwrap();
|
|
||||||
c.bus.register(move |_uuid, e| {
|
|
||||||
debug!(target: LOG_TARGET_UI, "Got event from bus {:?}", &e);
|
|
||||||
let eval = match e {
|
|
||||||
Event::KeyCreated { path, public } => { format!("keystoreChanged('{}', '{}');", &path, &public) }
|
|
||||||
Event::KeyLoaded { path, public } => { format!("keystoreChanged('{}', '{}');", &path, &public) }
|
|
||||||
Event::KeySaved { path, public } => { format!("keystoreChanged('{}', '{}');", &path, &public) }
|
|
||||||
Event::MinerStarted => {
|
|
||||||
status.mining = true;
|
|
||||||
String::from("setLeftStatusBarText('Mining...'); showMiningIndicator(true, false);")
|
|
||||||
}
|
|
||||||
Event::KeyGeneratorStarted => {
|
|
||||||
status.mining = true;
|
|
||||||
String::from("setLeftStatusBarText('Mining...'); showMiningIndicator(true, false);")
|
|
||||||
}
|
|
||||||
Event::MinerStopped | Event::KeyGeneratorStopped => {
|
|
||||||
status.mining = false;
|
|
||||||
if status.syncing {
|
|
||||||
String::from("setLeftStatusBarText('Syncing...'); showMiningIndicator(true, true);")
|
|
||||||
} else {
|
|
||||||
String::from("setLeftStatusBarText('Idle'); showMiningIndicator(false, false);")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Event::Syncing { have, height } => {
|
|
||||||
status.syncing = true;
|
|
||||||
status.synced_blocks = have;
|
|
||||||
status.sync_height = height;
|
|
||||||
if status.mining {
|
|
||||||
String::from("setLeftStatusBarText('Mining...'); showMiningIndicator(true, false);")
|
|
||||||
} else {
|
|
||||||
format!("setLeftStatusBarText('Synchronizing {}/{}'); showMiningIndicator(true, true);", have, height)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Event::SyncFinished => {
|
|
||||||
status.syncing = false;
|
|
||||||
if status.mining {
|
|
||||||
String::from("setLeftStatusBarText('Mining...'); showMiningIndicator(true, false);")
|
|
||||||
} else {
|
|
||||||
format!("setLeftStatusBarText('Idle'); showMiningIndicator(false, false);")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Event::NetworkStatus { nodes, blocks } => {
|
|
||||||
if status.mining || status.syncing || nodes < 3 {
|
|
||||||
format!("setRightStatusBarText('Nodes: {}, Blocks: {}')", nodes, blocks)
|
|
||||||
} else {
|
|
||||||
format!("setLeftStatusBarText('Idle'); setRightStatusBarText('Nodes: {}, Blocks: {}')", nodes, blocks)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => { String::new() }
|
|
||||||
};
|
|
||||||
|
|
||||||
if !eval.is_empty() {
|
|
||||||
debug!(target: LOG_TARGET_UI, "Evaluating {}", &eval);
|
|
||||||
handle.dispatch(move |web_view| {
|
|
||||||
web_view.eval(&eval.replace("\\", "\\\\"))
|
|
||||||
}).expect("Error dispatching!");
|
|
||||||
}
|
|
||||||
true
|
|
||||||
});
|
|
||||||
let eval = format!("keystoreChanged('{}', '{}');", c.keystore.get_path(), &c.keystore.get_public().to_string());
|
|
||||||
debug!(target: LOG_TARGET_UI, "Evaluating {}", &eval);
|
|
||||||
web_view.eval(&eval.replace("\\", "\\\\")).expect("Error evaluating!");
|
|
||||||
}
|
|
||||||
LoadKey {} => {
|
|
||||||
let result = tfd::open_file_dialog("Open keys file", "", Some((&["*.key"], "*.key")));
|
|
||||||
match result {
|
|
||||||
None => {}
|
|
||||||
Some(file_name) => {
|
|
||||||
match Keystore::from_file(&file_name, "") {
|
|
||||||
None => {
|
|
||||||
error!(target: LOG_TARGET_UI, "Error loading keystore '{}'!", &file_name);
|
|
||||||
}
|
|
||||||
Some(keystore) => {
|
|
||||||
info!(target: LOG_TARGET_UI, "Loaded keystore with key: {:?}", &keystore.get_public());
|
|
||||||
let mut c = context.lock().unwrap();
|
|
||||||
c.bus.post(Event::KeyLoaded { path: keystore.get_path().to_owned(), public: keystore.get_public().to_string() });
|
|
||||||
c.set_keystore(keystore);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CreateKey {} => {
|
|
||||||
create_key(context.clone());
|
|
||||||
}
|
|
||||||
SaveKey {} => {
|
|
||||||
let result = tfd::save_file_dialog_with_filter("Save keys file", "", &["*.key"], "Key files (*.key)");
|
|
||||||
match result {
|
|
||||||
None => {}
|
|
||||||
Some(new_path) => {
|
|
||||||
let mut c = context.lock().unwrap();
|
|
||||||
let path = new_path.clone();
|
|
||||||
let public = c.keystore.get_public().to_string();
|
|
||||||
c.keystore.save(&new_path, "");
|
|
||||||
info!(target: LOG_TARGET_UI, "Key file saved to {}", &path);
|
|
||||||
c.bus.post(Event::KeySaved { path, public });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CheckDomain { name } => {
|
|
||||||
let name = name.to_lowercase();
|
|
||||||
let c = context.lock().unwrap();
|
|
||||||
let available = c.get_blockchain().is_domain_available(&name, &c.get_keystore());
|
|
||||||
web_view.eval(&format!("domainAvailable({})", available)).expect("Error evaluating!");
|
|
||||||
}
|
|
||||||
CreateDomain { name, records, .. } => {
|
|
||||||
debug!(target: LOG_TARGET_UI, "Got records: {}", records);
|
|
||||||
let name = name.to_lowercase();
|
|
||||||
if !check_domain(&name, true) {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
if serde_json::from_str::<Vec<DnsRecord>>(&records).is_ok() {
|
|
||||||
let (keystore, transaction) = {
|
|
||||||
let context = context.lock().unwrap();
|
|
||||||
(context.get_keystore(), context.blockchain.get_domain_transaction(&name))
|
|
||||||
};
|
|
||||||
match transaction {
|
|
||||||
None => {
|
|
||||||
create_domain(miner.clone(), name, records, &keystore);
|
|
||||||
}
|
|
||||||
Some(transaction) => {
|
|
||||||
if transaction.pub_key == keystore.get_public() {
|
|
||||||
create_domain(miner.clone(), name, records, &keystore);
|
|
||||||
} else {
|
|
||||||
warn!(target: LOG_TARGET_UI, "Tried to mine not owned domain!");
|
|
||||||
let _ = web_view.eval(&format!("showWarning('{}');", "You cannot change domain that you don't own!"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
warn!(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."));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ChangeDomain { .. } => {}
|
|
||||||
RenewDomain { .. } => {}
|
|
||||||
TransferDomain { .. } => {}
|
|
||||||
CheckZone { name } => {
|
|
||||||
let name = name.to_lowercase();
|
|
||||||
if !check_domain(&name, false) {
|
|
||||||
web_view.eval("zoneAvailable(false)").expect("Error evaluating!");
|
|
||||||
} else {
|
|
||||||
let c = context.lock().unwrap();
|
|
||||||
let available = c.get_blockchain().is_domain_available(&name, &c.get_keystore());
|
|
||||||
web_view.eval(&format!("zoneAvailable({})", available)).expect("Error evaluating!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CreateZone { name, data } => {
|
|
||||||
let name = name.to_lowercase();
|
|
||||||
let data = data.to_lowercase();
|
|
||||||
let (keystore, transaction) = {
|
|
||||||
let context = context.lock().unwrap();
|
|
||||||
(context.get_keystore(), context.blockchain.get_domain_transaction(&name))
|
|
||||||
};
|
|
||||||
match transaction {
|
|
||||||
None => {
|
|
||||||
create_domain(miner.clone(), name, data, &keystore);
|
|
||||||
}
|
|
||||||
Some(transaction) => {
|
|
||||||
if transaction.pub_key == keystore.get_public() {
|
|
||||||
create_domain(miner.clone(), name, data, &keystore);
|
|
||||||
} else {
|
|
||||||
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!"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
StopMining => {
|
|
||||||
context.lock().unwrap().bus.post(Event::ActionStopMining);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//dbg!(&signature);
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.build()
|
|
||||||
.expect("Error building GUI");
|
|
||||||
|
|
||||||
// We use this ugly loop to lower CPU usage a lot.
|
|
||||||
// If we use .run() or only .step() in a loop without sleeps it will try
|
|
||||||
// to support 60FPS and uses more CPU than it should.
|
|
||||||
let pause = Duration::from_millis(25);
|
|
||||||
let mut start = Instant::now();
|
|
||||||
loop {
|
|
||||||
match interface.step() {
|
|
||||||
None => {
|
|
||||||
info!(target: LOG_TARGET_UI, "Interface closed, exiting");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Some(result) => {
|
|
||||||
match result {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(_) => {
|
|
||||||
error!(target: LOG_TARGET_UI, "Something wrong with webview, exiting");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if start.elapsed().as_millis() > 1 {
|
|
||||||
thread::sleep(pause);
|
|
||||||
start = Instant::now();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
interface.exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_genesis(miner: Arc<Mutex<Miner>>, keystore: &Keystore) {
|
|
||||||
let block = Block::new(None, keystore.get_public(), Bytes::default());
|
|
||||||
let mut miner_guard = miner.lock().unwrap();
|
|
||||||
miner_guard.add_block(block);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_domain<S: Into<String>>(miner: Arc<Mutex<Miner>>, name: S, data: S, keystore: &Keystore) {
|
|
||||||
let name = name.into();
|
|
||||||
info!(target: LOG_TARGET_UI, "Generating domain or zone {}", name);
|
|
||||||
//let tags_vector: Vec<String> = tags.into().trim().split(",").map(|s| s.trim()).map(String::from).collect();
|
|
||||||
let transaction = Transaction::from_str(name.into(), "dns".into(), data.into(), keystore.get_public().clone());
|
|
||||||
let block = Block::new(Some(transaction), keystore.get_public(), Bytes::default());
|
|
||||||
let mut miner_guard = miner.lock().unwrap();
|
|
||||||
miner_guard.add_block(block);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_key(context: Arc<Mutex<Context>>) {
|
|
||||||
let mining = Arc::new(AtomicBool::new(true));
|
|
||||||
let miners_count = Arc::new(AtomicUsize::new(0));
|
|
||||||
{ context.lock().unwrap().bus.post(Event::KeyGeneratorStarted); }
|
|
||||||
for _ in 0..num_cpus::get() {
|
|
||||||
let context = context.clone();
|
|
||||||
let mining = mining.clone();
|
|
||||||
let miners_count = miners_count.clone();
|
|
||||||
thread::spawn(move || {
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
|
||||||
let _ = set_current_thread_priority(ThreadPriority::Min);
|
|
||||||
miners_count.fetch_add(1, Ordering::Relaxed);
|
|
||||||
match generate_key(KEYSTORE_DIFFICULTY, mining.clone()) {
|
|
||||||
None => {
|
|
||||||
debug!(target: LOG_TARGET_UI, "Keystore mining finished");
|
|
||||||
}
|
|
||||||
Some(keystore) => {
|
|
||||||
info!(target: LOG_TARGET_UI, "Key mined successfully: {:?}", &keystore.get_public());
|
|
||||||
let mut c = context.lock().unwrap();
|
|
||||||
mining.store(false, Ordering::Relaxed);
|
|
||||||
c.bus.post(Event::KeyCreated { path: keystore.get_path().to_owned(), public: keystore.get_public().to_string() });
|
|
||||||
c.set_keystore(keystore);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let miners = miners_count.fetch_sub(1, Ordering::Relaxed) - 1;
|
|
||||||
if miners == 0 {
|
|
||||||
context.lock().unwrap().bus.post(Event::KeyGeneratorStopped);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
context.lock().unwrap().bus.register(move |_uuid, e| {
|
|
||||||
if e == Event::ActionStopMining {
|
|
||||||
mining.store(false, Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
false
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate_key(difficulty: usize, mining: Arc<AtomicBool>) -> Option<Keystore> {
|
|
||||||
let mut rng = rand::thread_rng();
|
|
||||||
let mut buf = [0u8; 32];
|
|
||||||
loop {
|
|
||||||
rng.fill_bytes(&mut buf);
|
|
||||||
let keystore = Keystore::from_bytes(&buf);
|
|
||||||
if keystore.hash_is_good(difficulty) {
|
|
||||||
info!(target: LOG_TARGET_UI, "Generated keypair: {:?}", &keystore);
|
|
||||||
return Some(keystore);
|
|
||||||
}
|
|
||||||
if !mining.load(Ordering::Relaxed) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_server_context(context: Arc<Mutex<Context>>, settings: &Settings) -> Arc<ServerContext> {
|
|
||||||
let mut server_context = ServerContext::new();
|
|
||||||
server_context.allow_recursive = true;
|
|
||||||
server_context.dns_listen = settings.dns.listen.clone();
|
|
||||||
server_context.resolve_strategy = match settings.dns.forwarders.is_empty() {
|
|
||||||
true => { ResolveStrategy::Recursive }
|
|
||||||
false => { ResolveStrategy::Forward { upstreams: settings.dns.forwarders.clone() } }
|
|
||||||
};
|
|
||||||
server_context.filters.push(Box::new(BlockchainFilter::new(context)));
|
|
||||||
match server_context.initialize() {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(e) => { panic!("DNS server failed to initialize: {:?}", e); }
|
|
||||||
}
|
|
||||||
|
|
||||||
Arc::new(server_context)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
#[serde(tag = "cmd", rename_all = "camelCase")]
|
|
||||||
pub enum Cmd {
|
|
||||||
Loaded,
|
|
||||||
LoadKey {},
|
|
||||||
CreateKey {},
|
|
||||||
SaveKey {},
|
|
||||||
CheckZone { name: String },
|
|
||||||
CreateZone { name: String, data: String },
|
|
||||||
CheckDomain { name: String },
|
|
||||||
CreateDomain { name: String, records: String, tags: String },
|
|
||||||
ChangeDomain { name: String, records: String, tags: String },
|
|
||||||
RenewDomain { name: String, days: u16 },
|
|
||||||
TransferDomain { name: String, owner: String },
|
|
||||||
StopMining,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Status {
|
|
||||||
pub mining: bool,
|
|
||||||
pub syncing: bool,
|
|
||||||
pub synced_blocks: u64,
|
|
||||||
pub sync_height: u64,
|
|
||||||
pub nodes_connected: usize,
|
|
||||||
pub chain_height: u64
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Status {
|
|
||||||
fn new() -> Self {
|
|
||||||
Status { mining: false, syncing: false, synced_blocks: 0, sync_height: 0, nodes_connected: 0, chain_height: 0 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn inline_style(s: &str) -> String {
|
|
||||||
format!(r#"<style type="text/css">{}</style>"#, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn inline_script(s: &str) -> String {
|
|
||||||
format!(r#"<script type="text/javascript">{}</script>"#, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use alfis::dns::protocol::{DnsRecord, TransientTtl};
|
use alfis::dns::protocol::{DnsRecord, TransientTtl};
|
||||||
|
|||||||
+8
-7
@@ -12,9 +12,10 @@ use num_cpus;
|
|||||||
#[cfg(not(target_os = "macos"))]
|
#[cfg(not(target_os = "macos"))]
|
||||||
use thread_priority::*;
|
use thread_priority::*;
|
||||||
|
|
||||||
use crate::{Block, Bytes, Context, hash_is_good};
|
use crate::{Block, Bytes, Context};
|
||||||
use crate::blockchain::blockchain::BlockQuality;
|
|
||||||
use crate::blockchain::{BLOCK_DIFFICULTY, CHAIN_VERSION, LOCKER_DIFFICULTY};
|
use crate::blockchain::{BLOCK_DIFFICULTY, CHAIN_VERSION, LOCKER_DIFFICULTY};
|
||||||
|
use crate::blockchain::enums::BlockQuality;
|
||||||
|
use crate::blockchain::hash_utils::*;
|
||||||
use crate::event::Event;
|
use crate::event::Event;
|
||||||
|
|
||||||
pub struct Miner {
|
pub struct Miner {
|
||||||
@@ -112,7 +113,7 @@ impl Miner {
|
|||||||
info!("Mining locker block");
|
info!("Mining locker block");
|
||||||
block.difficulty = LOCKER_DIFFICULTY;
|
block.difficulty = LOCKER_DIFFICULTY;
|
||||||
block.pub_key = context.lock().unwrap().keystore.get_public();
|
block.pub_key = context.lock().unwrap().keystore.get_public();
|
||||||
match context.lock().unwrap().blockchain.last_block() {
|
match context.lock().unwrap().chain.last_block() {
|
||||||
None => {}
|
None => {}
|
||||||
Some(last_block) => {
|
Some(last_block) => {
|
||||||
info!("Last block found");
|
info!("Last block found");
|
||||||
@@ -127,8 +128,8 @@ impl Miner {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
block.difficulty = BLOCK_DIFFICULTY;
|
block.difficulty = BLOCK_DIFFICULTY;
|
||||||
block.index = context.lock().unwrap().blockchain.height() + 1;
|
block.index = context.lock().unwrap().chain.height() + 1;
|
||||||
block.prev_block_hash = match context.lock().unwrap().blockchain.last_block() {
|
block.prev_block_hash = match context.lock().unwrap().chain.last_block() {
|
||||||
None => { Bytes::default() }
|
None => { Bytes::default() }
|
||||||
Some(block) => { block.hash }
|
Some(block) => { block.hash }
|
||||||
};
|
};
|
||||||
@@ -161,13 +162,13 @@ impl Miner {
|
|||||||
let index = block.index;
|
let index = block.index;
|
||||||
let mut context = context.lock().unwrap();
|
let mut context = context.lock().unwrap();
|
||||||
block.signature = Bytes::from_bytes(&context.keystore.sign(&block.as_bytes()));
|
block.signature = Bytes::from_bytes(&context.keystore.sign(&block.as_bytes()));
|
||||||
if context.blockchain.check_new_block(&block) != BlockQuality::Good {
|
if context.chain.check_new_block(&block) != BlockQuality::Good {
|
||||||
warn!("Error adding mined block!");
|
warn!("Error adding mined block!");
|
||||||
if index == 0 {
|
if index == 0 {
|
||||||
error!("To mine genesis block you need to make 'origin' an empty string in config.");
|
error!("To mine genesis block you need to make 'origin' an empty string in config.");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
context.blockchain.add_block(block);
|
context.chain.add_block(block);
|
||||||
}
|
}
|
||||||
context.bus.post(Event::MinerStopped);
|
context.bus.post(Event::MinerStopped);
|
||||||
mining.store(false, Ordering::SeqCst);
|
mining.store(false, Ordering::SeqCst);
|
||||||
|
|||||||
+17
-17
@@ -15,7 +15,7 @@ use log::{trace, debug, info, warn, error};
|
|||||||
|
|
||||||
use crate::{Context, Block, p2p::Message, p2p::State, p2p::Peer, p2p::Peers};
|
use crate::{Context, Block, p2p::Message, p2p::State, p2p::Peer, p2p::Peers};
|
||||||
use std::net::{SocketAddr, IpAddr, SocketAddrV4, ToSocketAddrs};
|
use std::net::{SocketAddr, IpAddr, SocketAddrV4, ToSocketAddrs};
|
||||||
use crate::blockchain::blockchain::BlockQuality;
|
use crate::blockchain::enums::BlockQuality;
|
||||||
use crate::blockchain::CHAIN_VERSION;
|
use crate::blockchain::CHAIN_VERSION;
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
|
|
||||||
@@ -95,7 +95,7 @@ impl Network {
|
|||||||
if !handle_connection_event(context.clone(), &mut peers, &poll.registry(), &event) {
|
if !handle_connection_event(context.clone(), &mut peers, &poll.registry(), &event) {
|
||||||
let _ = peers.close_peer(poll.registry(), &token);
|
let _ = peers.close_peer(poll.registry(), &token);
|
||||||
let mut context = context.lock().unwrap();
|
let mut context = context.lock().unwrap();
|
||||||
let blocks_count = context.blockchain.height();
|
let blocks_count = context.chain.height();
|
||||||
context.bus.post(crate::event::Event::NetworkStatus { nodes: peers.get_peers_active_count(), blocks: blocks_count });
|
context.bus.post(crate::event::Event::NetworkStatus { nodes: peers.get_peers_active_count(), blocks: blocks_count });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -106,7 +106,7 @@ impl Network {
|
|||||||
// Send pings to idle peers
|
// Send pings to idle peers
|
||||||
let (height, hash) = {
|
let (height, hash) = {
|
||||||
let context = context.lock().unwrap();
|
let context = context.lock().unwrap();
|
||||||
(context.blockchain.height(), context.blockchain.last_hash())
|
(context.chain.height(), context.chain.last_hash())
|
||||||
};
|
};
|
||||||
mine_locker_block(context.clone());
|
mine_locker_block(context.clone());
|
||||||
peers.send_pings(poll.registry(), height, hash);
|
peers.send_pings(poll.registry(), height, hash);
|
||||||
@@ -193,7 +193,7 @@ fn handle_connection_event(context: Arc<Mutex<Context>>, peers: &mut Peers, regi
|
|||||||
if from.elapsed().as_secs() >= 30 {
|
if from.elapsed().as_secs() >= 30 {
|
||||||
let data: String = {
|
let data: String = {
|
||||||
let c = context.lock().unwrap();
|
let c = context.lock().unwrap();
|
||||||
let message = Message::ping(c.blockchain.height(), c.blockchain.last_hash());
|
let message = Message::ping(c.chain.height(), c.chain.last_hash());
|
||||||
serde_json::to_string(&message).unwrap()
|
serde_json::to_string(&message).unwrap()
|
||||||
};
|
};
|
||||||
send_message(peer.get_stream(), &data.into_bytes());
|
send_message(peer.get_stream(), &data.into_bytes());
|
||||||
@@ -267,7 +267,7 @@ fn handle_message(context: Arc<Mutex<Context>>, message: Message, peers: &mut Pe
|
|||||||
let (my_height, my_hash, my_origin, my_version) = {
|
let (my_height, my_hash, my_origin, my_version) = {
|
||||||
let context = context.lock().unwrap();
|
let context = context.lock().unwrap();
|
||||||
// TODO cache it somewhere
|
// TODO cache it somewhere
|
||||||
(context.blockchain.height(), context.blockchain.last_hash(), &context.settings.origin.clone(), CHAIN_VERSION)
|
(context.chain.height(), context.chain.last_hash(), &context.settings.origin.clone(), CHAIN_VERSION)
|
||||||
};
|
};
|
||||||
match message {
|
match message {
|
||||||
Message::Hand { origin, version, public } => {
|
Message::Hand { origin, version, public } => {
|
||||||
@@ -290,10 +290,10 @@ fn handle_message(context: Arc<Mutex<Context>>, message: Message, peers: &mut Pe
|
|||||||
peer.set_height(height);
|
peer.set_height(height);
|
||||||
peer.set_active(true);
|
peer.set_active(true);
|
||||||
let mut context = context.lock().unwrap();
|
let mut context = context.lock().unwrap();
|
||||||
let blocks_count = context.blockchain.height();
|
let blocks_count = context.chain.height();
|
||||||
context.bus.post(crate::event::Event::NetworkStatus { nodes: active_count + 1, blocks: blocks_count });
|
context.bus.post(crate::event::Event::NetworkStatus { nodes: active_count + 1, blocks: blocks_count });
|
||||||
if peer.is_higher(my_height) {
|
if peer.is_higher(my_height) {
|
||||||
context.blockchain.update_max_height(height);
|
context.chain.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 + 1 })
|
State::message(Message::GetBlock { index: my_height + 1 })
|
||||||
} else {
|
} else {
|
||||||
@@ -321,7 +321,7 @@ fn handle_message(context: Arc<Mutex<Context>>, message: Message, peers: &mut Pe
|
|||||||
let is_higher = peer.is_higher(my_height);
|
let is_higher = peer.is_higher(my_height);
|
||||||
|
|
||||||
let mut context = context.lock().unwrap();
|
let mut context = context.lock().unwrap();
|
||||||
let blocks_count = context.blockchain.height();
|
let blocks_count = context.chain.height();
|
||||||
context.bus.post(crate::event::Event::NetworkStatus { nodes: peers.get_peers_active_count(), blocks: blocks_count });
|
context.bus.post(crate::event::Event::NetworkStatus { nodes: peers.get_peers_active_count(), blocks: blocks_count });
|
||||||
|
|
||||||
if is_higher {
|
if is_higher {
|
||||||
@@ -342,7 +342,7 @@ fn handle_message(context: Arc<Mutex<Context>>, message: Message, peers: &mut Pe
|
|||||||
}
|
}
|
||||||
Message::GetBlock { index } => {
|
Message::GetBlock { index } => {
|
||||||
let context = context.lock().unwrap();
|
let context = context.lock().unwrap();
|
||||||
match context.blockchain.get_block(index) {
|
match context.chain.get_block(index) {
|
||||||
Some(block) => State::message(Message::block(block.index, serde_json::to_string(&block).unwrap())),
|
Some(block) => State::message(Message::block(block.index, serde_json::to_string(&block).unwrap())),
|
||||||
None => State::Error
|
None => State::Error
|
||||||
}
|
}
|
||||||
@@ -359,11 +359,11 @@ fn handle_message(context: Arc<Mutex<Context>>, message: Message, peers: &mut Pe
|
|||||||
let peers_count = peers.get_peers_active_count();
|
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.chain.max_height();
|
||||||
match context.blockchain.check_new_block(&block) {
|
match context.chain.check_new_block(&block) {
|
||||||
BlockQuality::Good => {
|
BlockQuality::Good => {
|
||||||
context.blockchain.add_block(block);
|
context.chain.add_block(block);
|
||||||
let my_height = context.blockchain.height();
|
let my_height = context.chain.height();
|
||||||
context.bus.post(crate::event::Event::BlockchainChanged);
|
context.bus.post(crate::event::Event::BlockchainChanged);
|
||||||
// If it was the last block to sync
|
// If it was the last block to sync
|
||||||
if my_height == max_height {
|
if my_height == max_height {
|
||||||
@@ -392,12 +392,12 @@ 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
|
/// Sends an Event to miner to start mining locker block if "locker" is our public key
|
||||||
fn mine_locker_block(context: Arc<Mutex<Context>>) {
|
fn mine_locker_block(context: Arc<Mutex<Context>>) {
|
||||||
let mut context = context.lock().unwrap();
|
let mut context = context.lock().unwrap();
|
||||||
if let Some(block) = context.blockchain.last_block() {
|
if let Some(block) = context.chain.last_block() {
|
||||||
if block.index < context.blockchain.max_height() {
|
if block.index < context.chain.max_height() {
|
||||||
info!("No locker mining while syncing");
|
info!("No locker mining while syncing");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
match context.blockchain.get_block_locker(&block, Utc::now().timestamp()) {
|
match context.chain.get_block_locker(&block, Utc::now().timestamp()) {
|
||||||
Some(key) => {
|
Some(key) => {
|
||||||
if key == context.keystore.get_public() {
|
if key == context.keystore.get_public() {
|
||||||
info!("We have an honor to mine locker block!");
|
info!("We have an honor to mine locker block!");
|
||||||
@@ -419,7 +419,7 @@ fn deal_with_fork(context: MutexGuard<Context>, peer: &mut Peer, block: Block) {
|
|||||||
if vector[0].index == 0 {
|
if vector[0].index == 0 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if let Some(prev_block) = context.blockchain.get_block(vector[0].index - 1) {
|
if let Some(prev_block) = context.chain.get_block(vector[0].index - 1) {
|
||||||
// If this block is not root of the fork (we need to go ~deeper~ more backwards)
|
// 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 {
|
if vector[0].prev_block_hash != prev_block.hash {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
use std::num;
|
use std::num;
|
||||||
use num_bigint::BigUint;
|
|
||||||
use num_traits::One;
|
|
||||||
|
|
||||||
/// Convert bytes array to HEX format
|
/// Convert bytes array to HEX format
|
||||||
pub fn to_hex(buf: &[u8]) -> String {
|
pub fn to_hex(buf: &[u8]) -> String {
|
||||||
@@ -56,23 +54,6 @@ fn split_n(s: &str, n: usize) -> Vec<&str> {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// There is no default PartialEq implementation for arrays > 32 in size
|
|
||||||
pub fn same_hash(left: &[u8], right: &[u8]) -> bool {
|
|
||||||
for (x, y) in left.iter().zip(right) {
|
|
||||||
if x != y {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hash_is_good(hash: &[u8], difficulty: usize) -> bool {
|
|
||||||
let target = BigUint::one() << ((hash.len() << 3) - difficulty);
|
|
||||||
let hash_int = BigUint::from_bytes_be(&hash);
|
|
||||||
|
|
||||||
return hash_int < target;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::check_domain;
|
use crate::check_domain;
|
||||||
|
|||||||
+304
@@ -0,0 +1,304 @@
|
|||||||
|
extern crate web_view;
|
||||||
|
extern crate tinyfiledialogs as tfd;
|
||||||
|
extern crate serde;
|
||||||
|
extern crate serde_json;
|
||||||
|
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
use std::thread;
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
use web_view::Content;
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use log::{debug, error, info, LevelFilter, trace, warn};
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use alfis::{Block, Bytes, Context, Keystore, Transaction};
|
||||||
|
use alfis::miner::Miner;
|
||||||
|
use alfis::{keys, check_domain};
|
||||||
|
use alfis::event::Event;
|
||||||
|
use alfis::dns::protocol::DnsRecord;
|
||||||
|
use Cmd::*;
|
||||||
|
|
||||||
|
pub fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
|
||||||
|
let file_content = include_str!("webview/index.html");
|
||||||
|
let mut styles = inline_style(include_str!("webview/bulma.css"));
|
||||||
|
styles.push_str(&inline_style(include_str!("webview/busy_indicator.css")));
|
||||||
|
let scripts = inline_script(include_str!("webview/scripts.js"));
|
||||||
|
|
||||||
|
let html = Content::Html(file_content.to_owned().replace("{styles}", &styles).replace("{scripts}", &scripts));
|
||||||
|
let title = format!("ALFIS {}", env!("CARGO_PKG_VERSION"));
|
||||||
|
let mut interface = web_view::builder()
|
||||||
|
.title(&title)
|
||||||
|
.content(html)
|
||||||
|
.size(1023, 720)
|
||||||
|
.min_size(895, 350)
|
||||||
|
.resizable(true)
|
||||||
|
.debug(false)
|
||||||
|
.user_data(())
|
||||||
|
.invoke_handler(|web_view, arg| {
|
||||||
|
debug!("Command {}", arg);
|
||||||
|
match serde_json::from_str(arg).unwrap() {
|
||||||
|
Loaded => {
|
||||||
|
web_view.eval("showMiningIndicator(false, false);").expect("Error evaluating!");
|
||||||
|
let handle = web_view.handle();
|
||||||
|
let mut status = Status::new();
|
||||||
|
let mut c = context.lock().unwrap();
|
||||||
|
c.bus.register(move |_uuid, e| {
|
||||||
|
debug!("Got event from bus {:?}", &e);
|
||||||
|
let eval = match e {
|
||||||
|
Event::KeyCreated { path, public, hash } => { format!("keystoreChanged('{}', '{}', '{}');", &path, &public, &hash) }
|
||||||
|
Event::KeyLoaded { path, public, hash } => { format!("keystoreChanged('{}', '{}', '{}');", &path, &public, &hash) }
|
||||||
|
Event::KeySaved { path, public, hash } => { format!("keystoreChanged('{}', '{}', '{}');", &path, &public, &hash) }
|
||||||
|
Event::MinerStarted => {
|
||||||
|
status.mining = true;
|
||||||
|
String::from("setLeftStatusBarText('Mining...'); showMiningIndicator(true, false);")
|
||||||
|
}
|
||||||
|
Event::KeyGeneratorStarted => {
|
||||||
|
status.mining = true;
|
||||||
|
String::from("setLeftStatusBarText('Mining...'); showMiningIndicator(true, false);")
|
||||||
|
}
|
||||||
|
Event::MinerStopped | Event::KeyGeneratorStopped => {
|
||||||
|
status.mining = false;
|
||||||
|
if status.syncing {
|
||||||
|
String::from("setLeftStatusBarText('Syncing...'); showMiningIndicator(true, true);")
|
||||||
|
} else {
|
||||||
|
String::from("setLeftStatusBarText('Idle'); showMiningIndicator(false, false);")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Event::Syncing { have, height } => {
|
||||||
|
status.syncing = true;
|
||||||
|
status.synced_blocks = have;
|
||||||
|
status.sync_height = height;
|
||||||
|
if status.mining {
|
||||||
|
String::from("setLeftStatusBarText('Mining...'); showMiningIndicator(true, false);")
|
||||||
|
} else {
|
||||||
|
format!("setLeftStatusBarText('Synchronizing {}/{}'); showMiningIndicator(true, true);", have, height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Event::SyncFinished => {
|
||||||
|
status.syncing = false;
|
||||||
|
if status.mining {
|
||||||
|
String::from("setLeftStatusBarText('Mining...'); showMiningIndicator(true, false);")
|
||||||
|
} else {
|
||||||
|
format!("setLeftStatusBarText('Idle'); showMiningIndicator(false, false);")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Event::NetworkStatus { nodes, blocks } => {
|
||||||
|
if status.mining || status.syncing || nodes < 3 {
|
||||||
|
format!("setRightStatusBarText('Nodes: {}, Blocks: {}')", nodes, blocks)
|
||||||
|
} else {
|
||||||
|
format!("setLeftStatusBarText('Idle'); setRightStatusBarText('Nodes: {}, Blocks: {}')", nodes, blocks)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => { String::new() }
|
||||||
|
};
|
||||||
|
|
||||||
|
if !eval.is_empty() {
|
||||||
|
debug!("Evaluating {}", &eval);
|
||||||
|
handle.dispatch(move |web_view| {
|
||||||
|
web_view.eval(&eval.replace("\\", "\\\\"))
|
||||||
|
}).expect("Error dispatching!");
|
||||||
|
}
|
||||||
|
true
|
||||||
|
});
|
||||||
|
let eval = format!("keystoreChanged('{}', '{}', '{}');", c.keystore.get_path(), &c.keystore.get_public().to_string(), &c.keystore.get_hash().to_string());
|
||||||
|
debug!("Evaluating {}", &eval);
|
||||||
|
web_view.eval(&eval.replace("\\", "\\\\")).expect("Error evaluating!");
|
||||||
|
}
|
||||||
|
LoadKey {} => {
|
||||||
|
let result = tfd::open_file_dialog("Open keys file", "", Some((&["*.key"], "*.key")));
|
||||||
|
match result {
|
||||||
|
None => {}
|
||||||
|
Some(file_name) => {
|
||||||
|
match Keystore::from_file(&file_name, "") {
|
||||||
|
None => {
|
||||||
|
error!("Error loading keystore '{}'!", &file_name);
|
||||||
|
}
|
||||||
|
Some(keystore) => {
|
||||||
|
info!("Loaded keystore with key: {:?}", &keystore.get_public());
|
||||||
|
let mut c = context.lock().unwrap();
|
||||||
|
let path = keystore.get_path().to_owned();
|
||||||
|
let public = keystore.get_public().to_string();
|
||||||
|
let hash = keystore.get_hash().to_string();
|
||||||
|
c.bus.post(Event::KeyLoaded { path, public, hash });
|
||||||
|
c.set_keystore(keystore);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CreateKey {} => {
|
||||||
|
keys::create_key(context.clone());
|
||||||
|
}
|
||||||
|
SaveKey {} => {
|
||||||
|
let result = tfd::save_file_dialog_with_filter("Save keys file", "", &["*.key"], "Key files (*.key)");
|
||||||
|
match result {
|
||||||
|
None => {}
|
||||||
|
Some(new_path) => {
|
||||||
|
let mut c = context.lock().unwrap();
|
||||||
|
let path = new_path.clone();
|
||||||
|
let public = c.keystore.get_public().to_string();
|
||||||
|
let hash = c.keystore.get_hash().to_string();
|
||||||
|
c.keystore.save(&new_path, "");
|
||||||
|
info!("Key file saved to {}", &path);
|
||||||
|
c.bus.post(Event::KeySaved { path, public, hash });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CheckDomain { name } => {
|
||||||
|
let name = name.to_lowercase();
|
||||||
|
let c = context.lock().unwrap();
|
||||||
|
let available = c.get_chain().is_domain_available(&name, &c.get_keystore());
|
||||||
|
web_view.eval(&format!("domainAvailable({})", available)).expect("Error evaluating!");
|
||||||
|
}
|
||||||
|
CreateDomain { name, records, .. } => {
|
||||||
|
debug!("Got records: {}", records);
|
||||||
|
let name = name.to_lowercase();
|
||||||
|
if !check_domain(&name, true) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
if serde_json::from_str::<Vec<DnsRecord>>(&records).is_ok() {
|
||||||
|
let (keystore, transaction) = {
|
||||||
|
let context = context.lock().unwrap();
|
||||||
|
(context.get_keystore(), context.chain.get_domain_transaction(&name))
|
||||||
|
};
|
||||||
|
match transaction {
|
||||||
|
None => {
|
||||||
|
create_domain(miner.clone(), name, records, &keystore);
|
||||||
|
}
|
||||||
|
Some(transaction) => {
|
||||||
|
if transaction.pub_key == keystore.get_public() {
|
||||||
|
create_domain(miner.clone(), name, records, &keystore);
|
||||||
|
} else {
|
||||||
|
warn!("Tried to mine not owned domain!");
|
||||||
|
let _ = web_view.eval(&format!("showWarning('{}');", "You cannot change domain that you don't own!"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
warn!("Error in DNS records for domain!");
|
||||||
|
let _ = web_view.eval(&format!("showWarning('{}');", "Something wrong with your records! Please, correct the error and try again."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ChangeDomain { .. } => {}
|
||||||
|
RenewDomain { .. } => {}
|
||||||
|
TransferDomain { .. } => {}
|
||||||
|
CheckZone { name } => {
|
||||||
|
let name = name.to_lowercase();
|
||||||
|
if !check_domain(&name, false) {
|
||||||
|
web_view.eval("zoneAvailable(false)").expect("Error evaluating!");
|
||||||
|
} else {
|
||||||
|
let c = context.lock().unwrap();
|
||||||
|
let available = c.get_chain().is_domain_available(&name, &c.get_keystore());
|
||||||
|
web_view.eval(&format!("zoneAvailable({})", available)).expect("Error evaluating!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CreateZone { name, data } => {
|
||||||
|
let name = name.to_lowercase();
|
||||||
|
let data = data.to_lowercase();
|
||||||
|
let (keystore, transaction) = {
|
||||||
|
let context = context.lock().unwrap();
|
||||||
|
(context.get_keystore(), context.chain.get_domain_transaction(&name))
|
||||||
|
};
|
||||||
|
match transaction {
|
||||||
|
None => {
|
||||||
|
create_domain(miner.clone(), name, data, &keystore);
|
||||||
|
}
|
||||||
|
Some(transaction) => {
|
||||||
|
if transaction.pub_key == keystore.get_public() {
|
||||||
|
create_domain(miner.clone(), name, data, &keystore);
|
||||||
|
} else {
|
||||||
|
warn!("Tried to mine not owned domain!");
|
||||||
|
let _ = web_view.eval(&format!("showWarning('{}');", "You cannot change domain that you don't own!"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StopMining => {
|
||||||
|
context.lock().unwrap().bus.post(Event::ActionStopMining);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//dbg!(&signature);
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
.expect("Error building GUI");
|
||||||
|
|
||||||
|
// We use this ugly loop to lower CPU usage a lot.
|
||||||
|
// If we use .run() or only .step() in a loop without sleeps it will try
|
||||||
|
// to support 60FPS and uses more CPU than it should.
|
||||||
|
let pause = Duration::from_millis(25);
|
||||||
|
let mut start = Instant::now();
|
||||||
|
loop {
|
||||||
|
match interface.step() {
|
||||||
|
None => {
|
||||||
|
info!("Interface closed, exiting");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Some(result) => {
|
||||||
|
match result {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(_) => {
|
||||||
|
error!("Something wrong with webview, exiting");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if start.elapsed().as_millis() > 1 {
|
||||||
|
thread::sleep(pause);
|
||||||
|
start = Instant::now();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
interface.exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_domain<S: Into<String>>(miner: Arc<Mutex<Miner>>, name: S, data: S, keystore: &Keystore) {
|
||||||
|
let name = name.into();
|
||||||
|
info!("Generating domain or zone {}", name);
|
||||||
|
//let tags_vector: Vec<String> = tags.into().trim().split(",").map(|s| s.trim()).map(String::from).collect();
|
||||||
|
let transaction = Transaction::from_str(name.into(), "dns".into(), data.into(), keystore.get_public().clone());
|
||||||
|
let block = Block::new(Some(transaction), keystore.get_public(), Bytes::default());
|
||||||
|
let mut miner_guard = miner.lock().unwrap();
|
||||||
|
miner_guard.add_block(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(tag = "cmd", rename_all = "camelCase")]
|
||||||
|
pub enum Cmd {
|
||||||
|
Loaded,
|
||||||
|
LoadKey {},
|
||||||
|
CreateKey {},
|
||||||
|
SaveKey {},
|
||||||
|
CheckZone { name: String },
|
||||||
|
CreateZone { name: String, data: String },
|
||||||
|
CheckDomain { name: String },
|
||||||
|
CreateDomain { name: String, records: String, tags: String },
|
||||||
|
ChangeDomain { name: String, records: String, tags: String },
|
||||||
|
RenewDomain { name: String, days: u16 },
|
||||||
|
TransferDomain { name: String, owner: String },
|
||||||
|
StopMining,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Status {
|
||||||
|
pub mining: bool,
|
||||||
|
pub syncing: bool,
|
||||||
|
pub synced_blocks: u64,
|
||||||
|
pub sync_height: u64,
|
||||||
|
pub nodes_connected: usize,
|
||||||
|
pub chain_height: u64
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Status {
|
||||||
|
fn new() -> Self {
|
||||||
|
Status { mining: false, syncing: false, synced_blocks: 0, sync_height: 0, nodes_connected: 0, chain_height: 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inline_style(s: &str) -> String {
|
||||||
|
format!(r#"<style type="text/css">{}</style>"#, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inline_script(s: &str) -> String {
|
||||||
|
format!(r#"<script type="text/javascript">{}</script>"#, s)
|
||||||
|
}
|
||||||
@@ -150,6 +150,11 @@
|
|||||||
<p id="key_public_key">Not loaded</p>
|
<p id="key_public_key">Not loaded</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Public key hash</label>
|
||||||
|
<p id="key_public_hash">Not loaded</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
<div class="field is-grouped">
|
<div class="field is-grouped">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
|
|||||||
@@ -281,12 +281,14 @@ function setRightStatusBarText(text) {
|
|||||||
bar.innerHTML = text;
|
bar.innerHTML = text;
|
||||||
}
|
}
|
||||||
|
|
||||||
function keystoreChanged(path, pub_key) {
|
function keystoreChanged(path, pub_key, hash) {
|
||||||
if (path == '') {
|
if (path == '') {
|
||||||
path = "In memory";
|
path = "In memory";
|
||||||
}
|
}
|
||||||
key_file_name = document.getElementById("key_file_name");
|
var key_file_name = document.getElementById("key_file_name");
|
||||||
key_file_name.innerHTML = path;
|
key_file_name.innerHTML = path;
|
||||||
key_file_key = document.getElementById("key_public_key");
|
var key_public_key = document.getElementById("key_public_key");
|
||||||
key_file_key.innerHTML = pub_key;
|
key_public_key.innerHTML = pub_key;
|
||||||
|
var key_public_hash = document.getElementById("key_public_hash");
|
||||||
|
key_public_hash.innerHTML = hash;
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user