First commit of 0.5.* branch.

pull/112/head
Revertron 3 years ago
parent 8bb2d9af9f
commit 9949d13e62

@ -1,6 +1,6 @@
[package] [package]
name = "alfis" name = "alfis"
version = "0.4.37" version = "0.5.0"
authors = ["Revertron <alfis@revertron.com>"] authors = ["Revertron <alfis@revertron.com>"]
edition = "2018" edition = "2018"
build = "build.rs" build = "build.rs"

@ -4,7 +4,7 @@
Alternative Free Identity System Alternative Free Identity System
This project represents a minimal blockchain without cryptocurrency, capable of sustaining any number of domain name zones and domains. This project represents a minimal blockchain without cryptocurrency, capable of sustaining any number of domain names in a bunch of original alternative zones.
![Screenshot](img/keys.png) ![Screenshot](img/keys.png)

@ -32,4 +32,5 @@ onion
i2p i2p
meshname meshname
meship meship
mem
test test

@ -1,6 +1,8 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::{HashSet, HashMap}; use std::cmp::max;
use std::collections::{HashMap, HashSet};
use std::fs; use std::fs;
use std::ops::Deref;
use std::path::Path; use std::path::Path;
use chrono::Utc; use chrono::Utc;
@ -8,38 +10,32 @@ use chrono::Utc;
use log::{debug, error, info, trace, warn}; use log::{debug, error, info, trace, warn};
use sqlite::{Connection, State, Statement}; use sqlite::{Connection, State, Statement};
use crate::{Block, Bytes, Keystore, Transaction, check_domain, get_domain_zone, is_yggdrasil_record}; use crate::{Block, Bytes, check_domain, get_domain_zone, is_yggdrasil_record, Keystore, Transaction};
use crate::blockchain::transaction::TransactionType;
use crate::commons::constants::*;
use crate::blockchain::types::{BlockQuality, MineResult, Options};
use crate::blockchain::types::BlockQuality::*;
use crate::blockchain::hash_utils::*; use crate::blockchain::hash_utils::*;
use crate::settings::Settings; use crate::blockchain::transaction::DomainData;
use crate::keys::check_public_key_strength; use crate::blockchain::types::{BlockQuality, MineResult, Options, ZoneData};
use std::cmp::max; use crate::blockchain::types::BlockQuality::*;
use crate::blockchain::transaction::{ZoneData, DomainData};
use std::ops::Deref;
use crate::blockchain::types::MineResult::*; use crate::blockchain::types::MineResult::*;
use crate::commons::constants::*;
use crate::keys::check_public_key_strength;
use crate::settings::Settings;
const TEMP_DB_NAME: &str = "temp.db"; const TEMP_DB_NAME: &str = ":memory:";
const SQL_CREATE_TABLES: &str = include_str!("sql/create_db.sql"); const SQL_CREATE_TABLES: &str = include_str!("data/create_db.sql");
const ZONES_TXT: &str = include_str!("data/zones.txt");
const SQL_ADD_BLOCK: &str = "INSERT INTO blocks (id, timestamp, version, difficulty, random, nonce, 'transaction',\ const SQL_ADD_BLOCK: &str = "INSERT INTO blocks (id, timestamp, version, difficulty, random, nonce, 'transaction',\
prev_block_hash, hash, pub_key, signature) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"; prev_block_hash, hash, pub_key, signature) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);";
const SQL_GET_LAST_BLOCK: &str = "SELECT * FROM blocks ORDER BY id DESC LIMIT 1;"; const SQL_GET_LAST_BLOCK: &str = "SELECT * FROM blocks ORDER BY id DESC LIMIT 1;";
const SQL_TRUNCATE_BLOCKS: &str = "DELETE FROM blocks WHERE id >= ?;"; const SQL_TRUNCATE_BLOCKS: &str = "DELETE FROM blocks WHERE id >= ?;";
const SQL_TRUNCATE_DOMAINS: &str = "DELETE FROM domains WHERE id >= ?;"; const SQL_TRUNCATE_DOMAINS: &str = "DELETE FROM domains WHERE id >= ?;";
const SQL_TRUNCATE_ZONES: &str = "DELETE FROM zones WHERE id >= ?;";
const SQL_ADD_DOMAIN: &str = "INSERT INTO domains (id, timestamp, identity, confirmation, data, pub_key) VALUES (?, ?, ?, ?, ?, ?)"; const SQL_ADD_DOMAIN: &str = "INSERT INTO domains (id, timestamp, identity, confirmation, data, owner) VALUES (?, ?, ?, ?, ?, ?)";
const SQL_ADD_ZONE: &str = "INSERT INTO zones (id, timestamp, identity, confirmation, data, pub_key) VALUES (?, ?, ?, ?, ?, ?)";
const SQL_GET_BLOCK_BY_ID: &str = "SELECT * FROM blocks WHERE id=? LIMIT 1;"; const SQL_GET_BLOCK_BY_ID: &str = "SELECT * FROM blocks WHERE id=? LIMIT 1;";
const SQL_GET_LAST_FULL_BLOCK: &str = "SELECT * FROM blocks WHERE id < ? AND `transaction`<>'' ORDER BY id DESC LIMIT 1;"; const SQL_GET_LAST_FULL_BLOCK: &str = "SELECT * FROM blocks WHERE id < ? AND `transaction`<>'' ORDER BY id DESC LIMIT 1;";
const SQL_GET_LAST_FULL_BLOCK_FOR_KEY: &str = "SELECT * FROM blocks WHERE id < ? AND `transaction`<>'' AND pub_key = ? ORDER BY id DESC LIMIT 1;"; const SQL_GET_LAST_FULL_BLOCK_FOR_KEY: &str = "SELECT * FROM blocks WHERE id < ? AND `transaction`<>'' AND pub_key = ? ORDER BY id DESC LIMIT 1;";
const SQL_GET_DOMAIN_PUBLIC_KEY_BY_ID: &str = "SELECT pub_key FROM domains WHERE id < ? AND identity = ? LIMIT 1;"; const SQL_GET_DOMAIN_OWNER_BY_ID: &str = "SELECT owner FROM domains WHERE id < ? AND identity = ? LIMIT 1;";
const SQL_GET_ZONE_PUBLIC_KEY_BY_ID: &str = "SELECT pub_key FROM zones WHERE id < ? AND identity = ? LIMIT 1;";
const SQL_GET_DOMAIN_BY_ID: &str = "SELECT * FROM domains WHERE identity = ? ORDER BY id DESC LIMIT 1;"; const SQL_GET_DOMAIN_BY_ID: &str = "SELECT * FROM domains WHERE identity = ? ORDER BY id DESC LIMIT 1;";
const SQL_GET_DOMAINS_BY_KEY: &str = "SELECT * FROM domains WHERE pub_key = ?;"; const SQL_GET_DOMAINS_BY_KEY: &str = "SELECT * FROM domains WHERE owner = ?;";
const SQL_GET_ZONES: &str = "SELECT data FROM zones;";
const SQL_GET_OPTIONS: &str = "SELECT * FROM options;"; const SQL_GET_OPTIONS: &str = "SELECT * FROM options;";
@ -52,7 +48,7 @@ pub struct Chain {
last_full_block: Option<Block>, last_full_block: Option<Block>,
max_height: u64, max_height: u64,
db: Connection, db: Connection,
zones: RefCell<HashSet<String>>, zones: Vec<ZoneData>,
signers: RefCell<SignersCache>, signers: RefCell<SignersCache>,
} }
@ -61,7 +57,7 @@ impl Chain {
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 zones = RefCell::new(HashSet::new()); let zones = Self::load_zones();
let mut chain = Chain { origin, last_block: None, last_full_block: None, max_height: 0, db, zones, signers: SignersCache::new() }; let mut chain = Chain { origin, last_block: None, last_full_block: None, max_height: 0, db, zones, signers: SignersCache::new() };
chain.init_db(); chain.init_db();
chain chain
@ -164,10 +160,6 @@ impl Chain {
let mut statement = self.db.prepare(SQL_TRUNCATE_DOMAINS)?; let mut statement = self.db.prepare(SQL_TRUNCATE_DOMAINS)?;
statement.bind(1, index as i64)?; statement.bind(1, index as i64)?;
statement.next()?;
let mut statement = self.db.prepare(SQL_TRUNCATE_ZONES)?;
statement.bind(1, index as i64)?;
statement.next() statement.next()
} }
@ -365,7 +357,6 @@ impl Chain {
fn add_transaction_to_table(&mut self, index: u64, timestamp: i64, t: &Transaction) -> sqlite::Result<State> { fn add_transaction_to_table(&mut self, index: u64, timestamp: i64, t: &Transaction) -> sqlite::Result<State> {
let sql = match t.class.as_ref() { let sql = match t.class.as_ref() {
"domain" => SQL_ADD_DOMAIN, "domain" => SQL_ADD_DOMAIN,
"zone" => SQL_ADD_ZONE,
_ => return Err(sqlite::Error { code: None, message: None }) _ => return Err(sqlite::Error { code: None, message: None })
}; };
@ -375,7 +366,7 @@ impl Chain {
statement.bind(3, &**t.identity)?; statement.bind(3, &**t.identity)?;
statement.bind(4, &**t.confirmation)?; statement.bind(4, &**t.confirmation)?;
statement.bind(5, t.data.as_ref() as &str)?; statement.bind(5, t.data.as_ref() as &str)?;
statement.bind(6, &**t.pub_key)?; statement.bind(6, &**t.owner)?;
statement.next() statement.next()
} }
@ -453,7 +444,7 @@ impl Chain {
return false; return false;
} }
let identity_hash = hash_identity(domain, None); let identity_hash = hash_identity(domain, None);
if !self.is_id_available(height, &identity_hash, &keystore.get_public(), false) { if !self.is_id_available(height, &identity_hash, &keystore.get_public()) {
return false; return false;
} }
@ -463,19 +454,15 @@ impl Chain {
if parts.last().unwrap().contains(".") { if parts.last().unwrap().contains(".") {
return false; return false;
} }
return self.is_zone_in_blockchain(height, parts.first().unwrap()); return self.is_available_zone(parts.first().unwrap());
} }
true true
} }
/// 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, height: u64, identity: &Bytes, public_key: &Bytes, zone: bool) -> bool { pub fn is_id_available(&self, height: u64, identity: &Bytes, public_key: &Bytes) -> bool {
let sql = match zone { // TODO check for `owner` field
true => { SQL_GET_ZONE_PUBLIC_KEY_BY_ID } let mut statement = self.db.prepare(SQL_GET_DOMAIN_OWNER_BY_ID).unwrap();
false => { SQL_GET_DOMAIN_PUBLIC_KEY_BY_ID }
};
let mut statement = self.db.prepare(sql).unwrap();
statement.bind(1, height as i64).expect("Error in bind"); statement.bind(1, height as i64).expect("Error in bind");
statement.bind(2, &***identity).expect("Error in bind"); statement.bind(2, &***identity).expect("Error in bind");
while let State::Row = statement.next().unwrap() { while let State::Row = statement.next().unwrap() {
@ -487,50 +474,39 @@ impl Chain {
true true
} }
pub fn get_zones(&self) -> Vec<ZoneData> { pub fn get_zones(&self) -> &Vec<ZoneData> {
let mut map = HashMap::new(); &self.zones
match self.db.prepare(SQL_GET_ZONES) { }
Ok(mut statement) => {
while statement.next().unwrap() == State::Row { fn load_zones() -> Vec<ZoneData> {
let data = statement.read::<String>(0).unwrap(); let mut result: Vec<ZoneData> = Vec::new();
//debug!("Got zone data {}", &data); let zones: Vec<_> = ZONES_TXT.split("\n").collect();
if let Ok(zone_data) = serde_json::from_str::<ZoneData>(&data) { for zone in zones {
map.insert(zone_data.name.clone(), zone_data); let yggdrasil = zone == "ygg" || zone == "anon";
} result.push(ZoneData {name: zone.to_owned(), yggdrasil})
}
}
Err(e) => {
warn!("Can't get zones from DB {}", e);
}
} }
let result: Vec<ZoneData> = map.drain().map(|(_, value)| value).collect();
result result
} }
pub fn get_zones_hash() -> Bytes {
Bytes::from_bytes(hash_sha256(&ZONES_TXT.as_bytes()).as_slice())
}
/// Checks if some zone exists in our blockchain /// Checks if some zone exists in our blockchain
pub fn is_zone_in_blockchain(&self, height: u64, zone: &str) -> bool { pub fn is_available_zone(&self, zone: &str) -> bool {
if self.zones.borrow().contains(zone) { for z in &self.zones {
return true; if z.name == zone {
return true;
}
} }
// Checking for existing zone in DB
let identity_hash = hash_identity(zone, None);
if self.is_id_in_blockchain(height, &identity_hash, true) {
// If there is such a zone
self.zones.borrow_mut().insert(zone.to_owned());
return true;
}
false false
} }
/// Checks if some id exists in our blockchain /// Checks if some id exists in our blockchain
pub fn is_id_in_blockchain(&self, height: u64, id: &Bytes, zone: bool) -> bool { pub fn is_domain_in_blockchain(&self, height: u64, id: &Bytes) -> bool {
let sql = match zone { // Checking for existing domain in DB
true => { SQL_GET_ZONE_PUBLIC_KEY_BY_ID } let mut statement = self.db.prepare(SQL_GET_DOMAIN_OWNER_BY_ID).unwrap();
false => { SQL_GET_DOMAIN_PUBLIC_KEY_BY_ID }
};
// Checking for existing zone in DB
let mut statement = self.db.prepare(sql).unwrap();
statement.bind(1, height as i64).expect("Error in bind"); statement.bind(1, height as i64).expect("Error in bind");
statement.bind(2, &***id).expect("Error in bind"); statement.bind(2, &***id).expect("Error in bind");
while let State::Row = statement.next().unwrap() { while let State::Row = statement.next().unwrap() {
@ -546,17 +522,18 @@ impl Chain {
return WrongName; return WrongName;
} }
let zone = get_domain_zone(&name); let zone = get_domain_zone(&name);
if !self.is_zone_in_blockchain(height, &zone) { if !self.is_available_zone(&zone) {
return WrongZone; return WrongZone;
} }
if let Some(transaction) = self.get_domain_transaction(&name) { if let Some(transaction) = self.get_domain_transaction(&name) {
if transaction.pub_key.ne(pub_key) { if transaction.owner.ne(pub_key) {
return NotOwned; return NotOwned;
} }
} }
let identity_hash = hash_identity(&name, None); let identity_hash = hash_identity(&name, None);
// TODO extract method
if let Some(last) = self.get_last_full_block(MAX, Some(&pub_key)) { if let Some(last) = self.get_last_full_block(MAX, Some(&pub_key)) {
let new_id = !self.is_id_in_blockchain(height, &identity_hash, false); let new_id = !self.is_domain_in_blockchain(height, &identity_hash);
let time = last.timestamp + NEW_DOMAINS_INTERVAL - Utc::now().timestamp(); let time = last.timestamp + NEW_DOMAINS_INTERVAL - Utc::now().timestamp();
if new_id && time > 0 { if new_id && time > 0 {
return Cooldown { time } return Cooldown { time }
@ -586,7 +563,7 @@ impl Chain {
let class = String::from("domain"); let class = String::from("domain");
let data = statement.read::<String>(4).unwrap(); let data = statement.read::<String>(4).unwrap();
let pub_key = Bytes::from_bytes(&statement.read::<Vec<u8>>(5).unwrap()); let pub_key = Bytes::from_bytes(&statement.read::<Vec<u8>>(5).unwrap());
let transaction = Transaction { identity, confirmation, class, data, pub_key }; let transaction = Transaction { identity, confirmation, class, data, owner: pub_key };
debug!("Found transaction for domain {}: {:?}", domain, &transaction); debug!("Found transaction for domain {}: {:?}", domain, &transaction);
if transaction.check_identity(domain) { if transaction.check_identity(domain) {
return Some(transaction); return Some(transaction);
@ -619,8 +596,8 @@ impl Chain {
let confirmation = Bytes::from_bytes(&statement.read::<Vec<u8>>(3).unwrap()); let confirmation = Bytes::from_bytes(&statement.read::<Vec<u8>>(3).unwrap());
let class = String::from("domain"); let class = String::from("domain");
let data = statement.read::<String>(4).unwrap(); let data = statement.read::<String>(4).unwrap();
let pub_key = Bytes::from_bytes(&statement.read::<Vec<u8>>(5).unwrap()); let owner = Bytes::from_bytes(&statement.read::<Vec<u8>>(5).unwrap());
let transaction = Transaction { identity: identity.clone(), confirmation: confirmation.clone(), class, data, pub_key }; let transaction = Transaction { identity: identity.clone(), confirmation: confirmation.clone(), class, data, owner };
//debug!("Found transaction for domain {}: {:?}", domain, &transaction); //debug!("Found transaction for domain {}: {:?}", domain, &transaction);
if let Some(data) = transaction.get_domain_data() { if let Some(data) = transaction.get_domain_data() {
let mut domain = keystore.decrypt(data.domain.as_slice(), &confirmation.as_slice()[..12]); let mut domain = keystore.decrypt(data.domain.as_slice(), &confirmation.as_slice()[..12]);
@ -646,16 +623,6 @@ impl Chain {
result result
} }
pub fn get_zone_difficulty(&self, zone: &str) -> u32 {
let zones = self.get_zones();
for z in zones.iter() {
if z.name.eq(zone) {
return z.difficulty;
}
}
u32::MAX
}
pub fn last_block(&self) -> Option<Block> { pub fn last_block(&self) -> Option<Block> {
self.last_block.clone() self.last_block.clone()
} }
@ -732,7 +699,7 @@ impl Chain {
let difficulty = match &block.transaction { let difficulty = match &block.transaction {
None => { None => {
if block.index == 1 { if block.index == 1 {
ZONE_DIFFICULTY ORIGIN_DIFFICULTY
} else { } else {
SIGNER_DIFFICULTY SIGNER_DIFFICULTY
} }
@ -767,28 +734,20 @@ impl Chain {
} }
} }
} }
if matches!(Transaction::get_type(&block.transaction), TransactionType::Zone) {
if self.get_zones().len() >= MAXIMUM_ZONES {
warn!("Ignoring excess zone block");
return Bad;
}
}
if let Some(transaction) = &block.transaction { if let Some(transaction) = &block.transaction {
let current_height = match last_block { let current_height = match last_block {
None => { 0 } None => { 0 }
Some(block) => { block.index } Some(block) => { block.index }
}; };
// TODO check for zone transaction // If this domain is available to this public key
let is_domain_available = self.is_id_available(current_height, &transaction.identity, &block.pub_key, false); if !self.is_id_available(current_height, &transaction.identity, &block.pub_key) {
let is_zone_available = self.is_id_available(current_height, &transaction.identity, &block.pub_key, true);
if !is_domain_available || !is_zone_available {
warn!("Block {:?} is trying to spoof an identity!", &block); warn!("Block {:?} is trying to spoof an identity!", &block);
return Bad; return Bad;
} }
if let Some(last) = self.get_last_full_block(block.index, Some(&block.pub_key)) { if let Some(last) = self.get_last_full_block(block.index, Some(&block.pub_key)) {
if last.index < block.index { if last.index < block.index {
let new_id = !self.is_id_in_blockchain(block.index, &transaction.identity, false); let new_id = !self.is_domain_in_blockchain(block.index, &transaction.identity);
if new_id && last.timestamp + NEW_DOMAINS_INTERVAL > block.timestamp { if new_id && last.timestamp + NEW_DOMAINS_INTERVAL > block.timestamp {
warn!("Block {:?} is mined too early!", &block); warn!("Block {:?} is mined too early!", &block);
return Bad; return Bad;
@ -798,7 +757,7 @@ impl Chain {
// Check if yggdrasil only property of zone is not violated // Check if yggdrasil only property of zone is not violated
if let Some(block_data) = transaction.get_domain_data() { if let Some(block_data) = transaction.get_domain_data() {
let zones = self.get_zones(); let zones = self.get_zones();
for z in &zones { for z in zones {
if z.name == block_data.zone { if z.name == block_data.zone {
if z.yggdrasil { if z.yggdrasil {
for record in &block_data.records { for record in &block_data.records {
@ -911,15 +870,10 @@ impl Chain {
fn get_difficulty_for_transaction(&self, transaction: &Transaction) -> u32 { fn get_difficulty_for_transaction(&self, transaction: &Transaction) -> u32 {
match transaction.class.as_ref() { match transaction.class.as_ref() {
"domain" => { CLASS_DOMAIN => {
return match serde_json::from_str::<DomainData>(&transaction.data) { return match serde_json::from_str::<DomainData>(&transaction.data) {
Ok(data) => { Ok(_) => {
for zone in self.get_zones().iter() { DOMAIN_DIFFICULTY
if zone.name == data.zone {
return zone.difficulty;
}
}
u32::MAX
} }
Err(_) => { Err(_) => {
warn!("Error parsing DomainData from {:?}", transaction); warn!("Error parsing DomainData from {:?}", transaction);
@ -927,7 +881,7 @@ impl Chain {
} }
} }
} }
"zone" => { ZONE_DIFFICULTY } CLASS_ORIGIN => { ORIGIN_DIFFICULTY }
_ => { u32::MAX } _ => { u32::MAX }
} }
} }
@ -1005,9 +959,10 @@ impl SignersCache {
#[cfg(test)] #[cfg(test)]
pub mod tests { pub mod tests {
use crate::{Chain, Settings};
use simplelog::{ConfigBuilder, TermLogger, TerminalMode, ColorChoice};
use log::LevelFilter; use log::LevelFilter;
use simplelog::{ColorChoice, ConfigBuilder, TerminalMode, TermLogger};
use crate::{Chain, Settings};
fn init_logger() { fn init_logger() {
let config = ConfigBuilder::new() let config = ConfigBuilder::new()

@ -20,17 +20,8 @@ CREATE TABLE domains (
'identity' BINARY, 'identity' BINARY,
'confirmation' BINARY, 'confirmation' BINARY,
'data' TEXT, 'data' TEXT,
'pub_key' BINARY 'owner' BINARY
); );
CREATE INDEX ids ON domains ('identity'); CREATE INDEX ids ON domains ('identity');
CREATE TABLE zones (
'id' BIGINT NOT NULL PRIMARY KEY,
'timestamp' BIGINT NOT NULL,
'identity' BINARY,
'confirmation' BINARY,
'data' TEXT,
'pub_key' BINARY
);
CREATE TABLE options ('name' TEXT NOT NULL, 'value' TEXT NOT NULL); CREATE TABLE options ('name' TEXT NOT NULL, 'value' TEXT NOT NULL);

@ -0,0 +1,10 @@
anon
btn
conf
index
merch
mirror
mob
screen
srv
ygg

@ -48,7 +48,7 @@ impl DnsFilter for BlockchainFilter {
let zone = parts[0].to_owned(); let zone = parts[0].to_owned();
match data { match data {
None => { None => {
if self.context.lock().unwrap().chain.is_zone_in_blockchain(i64::MAX as u64, &zone) { if self.context.lock().unwrap().chain.is_available_zone(&zone) {
trace!("Not found data for domain {}", &search); trace!("Not found data for domain {}", &search);
// Create DnsPacket // Create DnsPacket
let mut packet = DnsPacket::new(); let mut packet = DnsPacket::new();
@ -194,7 +194,7 @@ impl BlockchainFilter {
} }
fn get_zone_response(&self, zone: &str, serial: u32, mut packet: &mut DnsPacket) -> bool { fn get_zone_response(&self, zone: &str, serial: u32, mut packet: &mut DnsPacket) -> bool {
let have_zone = self.context.lock().unwrap().chain.is_zone_in_blockchain(i64::MAX as u64, zone); let have_zone = self.context.lock().unwrap().chain.is_available_zone(zone);
if have_zone { if have_zone {
BlockchainFilter::add_soa_record(zone.to_owned(), serial, &mut packet); BlockchainFilter::add_soa_record(zone.to_owned(), serial, &mut packet);
} }

@ -1,4 +1,5 @@
use std::fmt; use std::fmt;
use std::fmt::{Display, Formatter};
use serde::{Deserialize, Serialize, Serializer}; use serde::{Deserialize, Serialize, Serializer};
use serde::ser::SerializeStruct; use serde::ser::SerializeStruct;
@ -6,29 +7,37 @@ use serde::ser::SerializeStruct;
use crate::blockchain::hash_utils::*; use crate::blockchain::hash_utils::*;
use crate::bytes::Bytes; use crate::bytes::Bytes;
use crate::dns::protocol::DnsRecord; use crate::dns::protocol::DnsRecord;
use std::fmt::{Display, Formatter}; use crate::{CLASS_ORIGIN, CLASS_DOMAIN};
extern crate serde; extern crate serde;
extern crate serde_json; extern crate serde_json;
#[derive(Clone, Deserialize, PartialEq)] #[derive(Clone, Deserialize, PartialEq)]
pub struct Transaction { pub struct Transaction {
#[serde(default, skip_serializing_if = "Bytes::is_zero")]
pub identity: Bytes, pub identity: Bytes,
#[serde(default, skip_serializing_if = "Bytes::is_zero")]
pub confirmation: Bytes, pub confirmation: Bytes,
pub class: String, pub class: String,
pub data: String, pub data: String,
pub pub_key: Bytes, #[serde(default, skip_serializing_if = "Bytes::is_zero")]
pub owner: Bytes,
} }
impl Transaction { impl Transaction {
pub fn from_str(identity: String, method: String, data: String, pub_key: Bytes) -> Self { pub fn from_str(identity: String, method: String, data: String, owner: Bytes) -> Self {
let hash = hash_identity(&identity, None); let hash = hash_identity(&identity, None);
let confirmation = hash_identity(&identity, Some(&pub_key)); let confirmation = hash_identity(&identity, Some(&owner));
return Self::new(hash, confirmation, method, data, pub_key); return Self::new(hash, confirmation, method, data, owner);
} }
pub fn new(identity: Bytes, confirmation: Bytes, method: String, data: String, pub_key: Bytes) -> Self { pub fn new(identity: Bytes, confirmation: Bytes, method: String, data: String, owner: Bytes) -> Self {
Transaction { identity, confirmation, class: method, data, pub_key } Transaction { identity, confirmation, class: method, data, owner }
}
pub fn origin(hash: Bytes, owner: Bytes) -> Self {
let data = serde_json::to_string(&Origin { zones: hash }).unwrap();
Transaction { identity: Bytes::default(), confirmation: Bytes::default(), class: String::from(CLASS_ORIGIN), data, owner }
} }
pub fn from_json(json: &str) -> Option<Self> { pub fn from_json(json: &str) -> Option<Self> {
@ -50,13 +59,13 @@ impl Transaction {
pub fn check_identity(&self, domain: &str) -> bool { pub fn check_identity(&self, domain: &str) -> bool {
let hash = hash_identity(&domain, None); let hash = hash_identity(&domain, None);
let confirmation = hash_identity(&domain, Some(&self.pub_key)); let confirmation = hash_identity(&domain, Some(&self.owner));
self.identity.eq(&hash) && self.confirmation.eq(&confirmation) self.identity.eq(&hash) && self.confirmation.eq(&confirmation)
} }
/// Returns [DomainData] from this transaction if it has it /// Returns [DomainData] from this transaction if it has it
pub fn get_domain_data(&self) -> Option<DomainData> { pub fn get_domain_data(&self) -> Option<DomainData> {
if self.class == "domain" { if self.class == CLASS_DOMAIN {
if let Ok(data) = serde_json::from_str::<DomainData>(&self.data) { if let Ok(data) = serde_json::from_str::<DomainData>(&self.data) {
return Some(data) return Some(data)
} }
@ -69,12 +78,9 @@ impl Transaction {
match what { match what {
None => { TransactionType::Signing } None => { TransactionType::Signing }
Some(transaction) => { Some(transaction) => {
if let Some(_) = transaction.get_domain_data() { if transaction.class == CLASS_DOMAIN {
return TransactionType::Domain; return TransactionType::Domain;
} }
if let Ok(_) = serde_json::from_str::<ZoneData>(&transaction.data) {
return TransactionType::Zone;
}
TransactionType::Unknown TransactionType::Unknown
} }
} }
@ -88,7 +94,7 @@ impl fmt::Debug for Transaction {
.field("confirmation", &self.confirmation) .field("confirmation", &self.confirmation)
.field("class", &self.class) .field("class", &self.class)
.field("data", &self.data) .field("data", &self.data)
.field("pub_key", &&self.pub_key) .field("pub_key", &&self.owner)
.finish() .finish()
} }
} }
@ -100,7 +106,7 @@ impl Serialize for Transaction {
structure.serialize_field("confirmation", &self.confirmation)?; structure.serialize_field("confirmation", &self.confirmation)?;
structure.serialize_field("class", &self.class)?; structure.serialize_field("class", &self.class)?;
structure.serialize_field("data", &self.data)?; structure.serialize_field("data", &self.data)?;
structure.serialize_field("pub_key", &self.pub_key)?; structure.serialize_field("pub_key", &self.owner)?;
structure.end() structure.end()
} }
} }
@ -109,38 +115,28 @@ pub enum TransactionType {
Unknown, Unknown,
Signing, Signing,
Domain, Domain,
Zone,
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct DomainData { pub struct DomainData {
pub domain: Bytes, pub domain: Bytes,
pub zone: String, pub zone: String,
pub info: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub records: Vec<DnsRecord>, pub records: Vec<DnsRecord>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub contacts: Vec<ContactsData>, pub contacts: Vec<ContactsData>,
#[serde(default)]
pub owners: Vec<Bytes>
} }
impl DomainData { impl DomainData {
pub fn new(domain: Bytes, zone: String, records: Vec<DnsRecord>, contacts: Vec<ContactsData>, owners: Vec<Bytes>) -> Self { pub fn new(domain: Bytes, zone: String, info: String, records: Vec<DnsRecord>, contacts: Vec<ContactsData>) -> Self {
Self { domain, zone, records, contacts, owners } Self { domain, zone, info, records, contacts }
} }
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct ZoneData { pub struct Origin {
pub name: String, zones: Bytes
pub difficulty: u32,
pub yggdrasil: bool,
#[serde(default)]
pub owners: Vec<Bytes>
}
impl Display for ZoneData {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.write_str(&format!("{} ({})", self.name, self.difficulty))
}
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]

@ -1,3 +1,6 @@
use std::fmt::{Display, Formatter};
use serde::{Deserialize, Serialize};
/// Represents a result of block check on block's arrival /// Represents a result of block check on block's arrival
#[derive(PartialEq)] #[derive(PartialEq)]
pub enum BlockQuality { pub enum BlockQuality {
@ -34,4 +37,16 @@ impl Options {
pub fn empty() -> Self { pub fn empty() -> Self {
Options { origin: String::new(), version: 0 } Options { origin: String::new(), version: 0 }
} }
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct ZoneData {
pub name: String,
pub yggdrasil: bool,
}
impl Display for ZoneData {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.write_str(&format!("{}, yggdrasil: {}", self.name, self.yggdrasil))
}
}

@ -1,10 +1,10 @@
use std::time::Duration; use std::time::Duration;
pub const DB_VERSION: u32 = 0; pub const DB_VERSION: u32 = 0;
pub const CHAIN_VERSION: u32 = 0; pub const CHAIN_VERSION: u32 = 1;
pub const ZONE_DIFFICULTY: u32 = 28; pub const ORIGIN_DIFFICULTY: u32 = 30;
pub const ZONE_MIN_DIFFICULTY: u32 = 22; pub const DOMAIN_DIFFICULTY: u32 = 24;
pub const SIGNER_DIFFICULTY: u32 = 16; pub const SIGNER_DIFFICULTY: u32 = 16;
pub const KEYSTORE_DIFFICULTY: u32 = 23; pub const KEYSTORE_DIFFICULTY: u32 = 23;
@ -37,7 +37,7 @@ pub const ZONE_MAX_LENGTH: usize = 10;
pub const MAX_RECONNECTS: u32 = 5; pub const MAX_RECONNECTS: u32 = 5;
pub const DB_NAME: &str = "blockchain.db"; pub const DB_NAME: &str = "blockchain.db";
pub const CLASS_ZONE: &str = "zone"; pub const CLASS_ORIGIN: &str = "origin";
pub const CLASS_DOMAIN: &str = "domain"; pub const CLASS_DOMAIN: &str = "domain";
pub const ALFIS_DEBUG: &str = "ALFIS_DEBUG"; pub const ALFIS_DEBUG: &str = "ALFIS_DEBUG";
@ -46,7 +46,7 @@ pub const LISTEN_PORT: u16 = 4244;
pub const UI_REFRESH_DELAY_MS: u128 = 250; pub const UI_REFRESH_DELAY_MS: u128 = 250;
pub const LOG_REFRESH_DELAY_SEC: u64 = 60; pub const LOG_REFRESH_DELAY_SEC: u64 = 60;
pub const POLL_TIMEOUT: Option<Duration> = Some(Duration::from_millis(250)); pub const POLL_TIMEOUT: Option<Duration> = Some(Duration::from_millis(25000));
pub const MAX_PACKET_SIZE: usize = 1 * 1024 * 1024; // 1 Mb pub const MAX_PACKET_SIZE: usize = 1 * 1024 * 1024; // 1 Mb
pub const MAX_READ_BLOCK_TIME: u128 = 500; pub const MAX_READ_BLOCK_TIME: u128 = 500;
pub const MAX_IDLE_SECONDS: u64 = 180; pub const MAX_IDLE_SECONDS: u64 = 180;

@ -32,6 +32,13 @@ pub fn check_domain(name: &str, allow_dots: bool) -> bool {
if name.starts_with('.') || name.starts_with('-') || name.ends_with('.') || name.ends_with('-') { if name.starts_with('.') || name.starts_with('-') || name.ends_with('.') || name.ends_with('-') {
return false; return false;
} }
let parts: Vec<&str> = name.rsplitn(2, ".").collect();
if parts.len() == 2 {
if parts[1].len() < 3 && is_numeric(parts[1]) {
return false;
}
}
let mut last_dot = false; let mut last_dot = false;
let mut last_hyphen = false; let mut last_hyphen = false;
for char in name.chars() { for char in name.chars() {
@ -60,6 +67,15 @@ pub fn check_domain(name: &str, allow_dots: bool) -> bool {
true true
} }
pub fn is_numeric(str: &str) -> bool {
for char in str.chars() {
if !char.is_numeric() {
return false;
}
}
true
}
pub fn get_domain_zone(domain: &str) -> String { pub fn get_domain_zone(domain: &str) -> String {
let parts: Vec<&str> = domain.rsplitn(2, ".").collect(); let parts: Vec<&str> = domain.rsplitn(2, ".").collect();
if !parts.is_empty() { if !parts.is_empty() {
@ -160,6 +176,9 @@ mod test {
assert!(!check_domain("ab.c-", true)); assert!(!check_domain("ab.c-", true));
assert!(!check_domain(".ab.c", true)); assert!(!check_domain(".ab.c", true));
assert!(!check_domain("ab.c-", true)); assert!(!check_domain("ab.c-", true));
assert!(check_domain("777.com", true));
assert!(!check_domain("77.com", true));
assert!(!check_domain("7.com", true));
} }
#[test] #[test]

@ -169,14 +169,6 @@ impl VectorPacketBuffer {
} }
impl PacketBuffer for VectorPacketBuffer { impl PacketBuffer for VectorPacketBuffer {
fn find_label(&self, label: &str) -> Option<usize> {
self.label_lookup.get(label).cloned()
}
fn save_label(&mut self, label: &str, pos: usize) {
self.label_lookup.insert(label.to_string(), pos);
}
fn read(&mut self) -> Result<u8> { fn read(&mut self) -> Result<u8> {
let res = self.buffer[self.pos]; let res = self.buffer[self.pos];
self.pos += 1; self.pos += 1;
@ -220,42 +212,33 @@ impl PacketBuffer for VectorPacketBuffer {
Ok(()) Ok(())
} }
fn find_label(&self, label: &str) -> Option<usize> {
self.label_lookup.get(label).cloned()
}
fn save_label(&mut self, label: &str, pos: usize) {
self.label_lookup.insert(label.to_string(), pos);
}
} }
pub struct StreamPacketBuffer<'a, T> pub struct StreamPacketBuffer<'a, T> where T: Read {
where
T: Read,
{
pub stream: &'a mut T, pub stream: &'a mut T,
pub buffer: Vec<u8>, pub buffer: Vec<u8>,
pub pos: usize, pub pos: usize,
} }
impl<'a, T> StreamPacketBuffer<'a, T> impl<'a, T> StreamPacketBuffer<'a, T> where T: Read + 'a {
where
T: Read + 'a,
{
pub fn new(stream: &'a mut T) -> StreamPacketBuffer<'_, T> { pub fn new(stream: &'a mut T) -> StreamPacketBuffer<'_, T> {
StreamPacketBuffer { StreamPacketBuffer {
stream: stream, stream,
buffer: Vec::new(), buffer: Vec::new(),
pos: 0, pos: 0,
} }
} }
} }
impl<'a, T> PacketBuffer for StreamPacketBuffer<'a, T> impl<'a, T> PacketBuffer for StreamPacketBuffer<'a, T> where T: Read + 'a {
where
T: Read + 'a,
{
fn find_label(&self, _: &str) -> Option<usize> {
None
}
fn save_label(&mut self, _: &str, _: usize) {
unimplemented!();
}
fn read(&mut self) -> Result<u8> { fn read(&mut self) -> Result<u8> {
while self.pos >= self.buffer.len() { while self.pos >= self.buffer.len() {
let mut local_buffer = [0; 1]; let mut local_buffer = [0; 1];
@ -310,6 +293,14 @@ where
self.pos += steps; self.pos += steps;
Ok(()) Ok(())
} }
fn find_label(&self, _: &str) -> Option<usize> {
None
}
fn save_label(&mut self, _: &str, _: usize) {
unimplemented!();
}
} }
pub struct BytePacketBuffer { pub struct BytePacketBuffer {
@ -333,12 +324,6 @@ impl Default for BytePacketBuffer {
} }
impl PacketBuffer for BytePacketBuffer { impl PacketBuffer for BytePacketBuffer {
fn find_label(&self, _: &str) -> Option<usize> {
None
}
fn save_label(&mut self, _: &str, _: usize) {}
fn read(&mut self) -> Result<u8> { fn read(&mut self) -> Result<u8> {
if self.pos >= 512 { if self.pos >= 512 {
return Err(BufferError::EndOfBuffer); return Err(BufferError::EndOfBuffer);
@ -393,6 +378,12 @@ impl PacketBuffer for BytePacketBuffer {
Ok(()) Ok(())
} }
fn find_label(&self, _: &str) -> Option<usize> {
None
}
fn save_label(&mut self, _: &str, _: usize) {}
} }
#[cfg(test)] #[cfg(test)]

@ -8,7 +8,6 @@ pub enum Event {
KeyCreated { path: String, public: String, hash: String }, KeyCreated { path: String, public: String, hash: String },
KeyLoaded { path: String, public: String, hash: String }, KeyLoaded { path: String, public: String, hash: String },
KeySaved { path: String, public: String, hash: String }, KeySaved { path: String, public: String, hash: String },
ZonesChanged,
NewBlockReceived, NewBlockReceived,
BlockchainChanged { index: u64 }, BlockchainChanged { index: u64 },
ActionStopMining, ActionStopMining,

@ -16,7 +16,7 @@ use simplelog::*;
#[cfg(windows)] #[cfg(windows)]
use winapi::um::wincon::{ATTACH_PARENT_PROCESS, AttachConsole, FreeConsole}; use winapi::um::wincon::{ATTACH_PARENT_PROCESS, AttachConsole, FreeConsole};
use alfis::{Block, Bytes, Chain, Miner, Context, Network, Settings, dns_utils, Keystore, ZONE_DIFFICULTY, ALFIS_DEBUG, DB_NAME}; use alfis::{Block, Bytes, Chain, Miner, Context, Network, Settings, dns_utils, Keystore, ORIGIN_DIFFICULTY, ALFIS_DEBUG, DB_NAME, Transaction};
use std::fs::OpenOptions; use std::fs::OpenOptions;
use std::process::exit; use std::process::exit;
use std::io::{Seek, SeekFrom}; use std::io::{Seek, SeekFrom};
@ -208,7 +208,8 @@ fn create_genesis_if_needed(context: &Arc<Mutex<Context>>, miner: &Arc<Mutex<Min
if origin.is_empty() && last_block.is_none() { if origin.is_empty() && last_block.is_none() {
if let Some(keystore) = &context.keystore { if let Some(keystore) = &context.keystore {
// If blockchain is empty, we are going to mine a Genesis block // If blockchain is empty, we are going to mine a Genesis block
let block = Block::new(None, context.get_keystore().unwrap().get_public(), Bytes::default(), ZONE_DIFFICULTY); let transaction = Transaction::origin(Chain::get_zones_hash(), context.get_keystore().unwrap().get_public());
let block = Block::new(Some(transaction), context.get_keystore().unwrap().get_public(), Bytes::default(), ORIGIN_DIFFICULTY);
miner.lock().unwrap().add_block(block, keystore.clone()); miner.lock().unwrap().add_block(block, keystore.clone());
} }
} }

@ -349,7 +349,7 @@ fn find_hash(context: Arc<Mutex<Context>>, mut block: Block, running: Arc<Atomic
let elapsed = time.elapsed().as_millis(); let elapsed = time.elapsed().as_millis();
if elapsed >= 1000 { if elapsed >= 1000 {
block.timestamp = Utc::now().timestamp(); block.timestamp = Utc::now().timestamp();
if elapsed > 5000 { if elapsed > 10000 {
let speed = (nonce - prev_nonce) / (elapsed as u64 / 1000); let speed = (nonce - prev_nonce) / (elapsed as u64 / 1000);
//debug!("Mining speed {} H/s, max difficulty {}", speed, max_diff); //debug!("Mining speed {} H/s, max difficulty {}", speed, max_diff);
if let Ok(mut context) = context.try_lock() { if let Ok(mut context) = context.try_lock() {

@ -17,8 +17,7 @@ use mio::event::Event;
use mio::net::{TcpListener, TcpStream}; use mio::net::{TcpListener, TcpStream};
use rand::random; use rand::random;
use crate::{Block, Context, p2p::Message, p2p::Peer, p2p::Peers, p2p::State, Transaction}; use crate::{Block, Context, p2p::Message, p2p::Peer, p2p::Peers, p2p::State};
use crate::blockchain::transaction::TransactionType;
use crate::blockchain::types::BlockQuality; use crate::blockchain::types::BlockQuality;
use crate::commons::*; use crate::commons::*;
@ -539,13 +538,9 @@ fn handle_block(context: Arc<Mutex<Context>>, peers: &mut Peers, token: &Token,
let max_height = context.chain.max_height(); let max_height = context.chain.max_height();
match context.chain.check_new_block(&block) { match context.chain.check_new_block(&block) {
BlockQuality::Good => { BlockQuality::Good => {
let zone = matches!(Transaction::get_type(&block.transaction), TransactionType::Zone);
context.chain.add_block(block); context.chain.add_block(block);
let my_height = context.chain.get_height(); let my_height = context.chain.get_height();
context.bus.post(crate::event::Event::BlockchainChanged { index: my_height }); context.bus.post(crate::event::Event::BlockchainChanged { index: my_height });
if zone {
context.bus.post(crate::event::Event::ZonesChanged);
}
// 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 {
context.bus.post(crate::event::Event::SyncFinished); context.bus.post(crate::event::Event::SyncFinished);
@ -573,13 +568,9 @@ fn handle_block(context: Arc<Mutex<Context>>, peers: &mut Peers, token: &Token,
debug!("Got forked block {} with hash {:?}", block.index, block.hash); debug!("Got forked block {} with hash {:?}", block.index, block.hash);
let last_block = context.chain.last_block().unwrap(); let last_block = context.chain.last_block().unwrap();
if block.is_better_than(&last_block) { if block.is_better_than(&last_block) {
let zone = matches!(Transaction::get_type(&block.transaction), TransactionType::Zone);
context.chain.replace_block(block).expect("Error replacing block with fork"); context.chain.replace_block(block).expect("Error replacing block with fork");
let index = context.chain.get_height(); let index = context.chain.get_height();
context.bus.post(crate::event::Event::BlockchainChanged { index }); context.bus.post(crate::event::Event::BlockchainChanged { index });
if zone {
context.bus.post(crate::event::Event::ZonesChanged);
}
} else { } else {
debug!("Fork in not better than our block, dropping."); debug!("Fork in not better than our block, dropping.");
} }

@ -14,18 +14,18 @@ use log::{debug, error, info, LevelFilter, trace, warn};
use serde::Deserialize; use serde::Deserialize;
use web_view::Content; use web_view::Content;
use alfis::{Block, Bytes, Context, get_domain_zone, Keystore, Transaction, ZONE_MIN_DIFFICULTY, is_yggdrasil_record}; use alfis::{Block, Bytes, Context, Keystore, Transaction};
use alfis::{check_domain, keys}; use alfis::keys;
use alfis::blockchain::transaction::{DomainData, ZoneData}; use alfis::blockchain::hash_utils::hash_identity;
use alfis::blockchain::transaction::DomainData;
use alfis::blockchain::types::MineResult; use alfis::blockchain::types::MineResult;
use alfis::commons::{ZONE_DIFFICULTY, ZONE_MAX_LENGTH, CLASS_DOMAIN, CLASS_ZONE}; use alfis::commons::*;
use alfis::dns::protocol::DnsRecord; use alfis::dns::protocol::DnsRecord;
use alfis::event::Event; use alfis::event::Event;
use alfis::miner::Miner; use alfis::miner::Miner;
use Cmd::*; use Cmd::*;
use self::web_view::{Handle, WebView}; use self::web_view::{Handle, WebView};
use alfis::blockchain::hash_utils::hash_identity;
pub fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) { pub fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
let file_content = include_str!("webview/index.html"); let file_content = include_str!("webview/index.html");
@ -57,10 +57,6 @@ pub fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
action_create_domain(Arc::clone(&context), Arc::clone(&miner), web_view, name, data); action_create_domain(Arc::clone(&context), Arc::clone(&miner), web_view, name, data);
} }
TransferDomain { .. } => {} TransferDomain { .. } => {}
CheckZone { name } => { action_check_zone(&context, web_view, name); }
MineZone { name, data } => {
action_create_zone(Arc::clone(&context), Arc::clone(&miner), web_view, name, data);
}
StopMining => { context.lock().unwrap().bus.post(Event::ActionStopMining); } StopMining => { context.lock().unwrap().bus.post(Event::ActionStopMining); }
Open { link } => { Open { link } => {
if open::that(&link).is_err() { if open::that(&link).is_err() {
@ -110,19 +106,6 @@ fn run_interface_loop(context: &mut Arc<Mutex<Context>>, interface: &mut WebView
} }
} }
fn action_check_zone(context: &Arc<Mutex<Context>>, web_view: &mut WebView<()>, name: String) {
let name = name.to_lowercase();
if name.len() > ZONE_MAX_LENGTH || !check_domain(&name, false) || context.lock().unwrap().x_zones.has_zone(&name) {
web_view.eval("zoneAvailable(false)").expect("Error evaluating!");
} else {
let c = context.lock().unwrap();
if let Some(keystore) = c.get_keystore() {
let available = c.get_chain().is_domain_available(c.get_chain().get_height(), &name, &keystore);
web_view.eval(&format!("zoneAvailable({})", available)).expect("Error evaluating!");
}
}
}
fn action_check_record(web_view: &mut WebView<()>, data: String) { fn action_check_record(web_view: &mut WebView<()>, data: String) {
match serde_json::from_str::<DnsRecord>(&data) { match serde_json::from_str::<DnsRecord>(&data) {
Ok(_) => { web_view.eval("recordOkay(true)").expect("Error evaluating!"); } Ok(_) => { web_view.eval("recordOkay(true)").expect("Error evaluating!"); }
@ -296,15 +279,6 @@ fn action_loaded(context: &Arc<Mutex<Context>>, web_view: &mut WebView<()>) {
format!("setLeftStatusBarText('Idle'); setRightStatusBarText('Nodes: {}, Blocks: {}')", nodes, blocks) format!("setLeftStatusBarText('Idle'); setRightStatusBarText('Nodes: {}, Blocks: {}')", nodes, blocks)
} }
} }
Event::ZonesChanged => {
info!("New zone arrived");
if let Ok(zones) = serde_json::to_string(&context.chain.get_zones()) {
let _ = handle.dispatch(move |web_view|{
web_view.eval(&format!("zonesChanged('{}');", &zones))
});
}
String::new() // Nothing
}
Event::BlockchainChanged {index} => { Event::BlockchainChanged {index} => {
debug!("Current blockchain height is {}", index); debug!("Current blockchain height is {}", index);
event_handle_info(&handle, &format!("Blockchain changed, current block count is {} now.", index)); event_handle_info(&handle, &format!("Blockchain changed, current block count is {} now.", index));
@ -331,9 +305,9 @@ fn action_loaded(context: &Arc<Mutex<Context>>, web_view: &mut WebView<()>) {
let index = c.chain.get_height(); let index = c.chain.get_height();
if index > 0 { if index > 0 {
c.bus.post(Event::BlockchainChanged { index }); c.bus.post(Event::BlockchainChanged { index });
if let Ok(zones) = serde_json::to_string(&c.chain.get_zones()) { }
let _ = web_view.eval(&format!("zonesChanged('{}');", &zones)); if let Ok(zones) = serde_json::to_string(&c.chain.get_zones()) {
} let _ = web_view.eval(&format!("zonesChanged('{}');", &zones));
} }
event_info(web_view, "Application loaded"); event_info(web_view, "Application loaded");
} }
@ -384,7 +358,7 @@ fn action_create_domain(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>,
}; };
// Check if yggdrasil only quality of zone is not violated // Check if yggdrasil only quality of zone is not violated
let zones = context.chain.get_zones(); let zones = context.chain.get_zones();
for z in &zones { for z in zones {
if z.name == data.zone { if z.name == data.zone {
if z.yggdrasil { if z.yggdrasil {
for record in &data.records { for record in &data.records {
@ -399,10 +373,8 @@ fn action_create_domain(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>,
} }
match context.chain.can_mine_domain(context.chain.get_height(), &name, &pub_key) { match context.chain.can_mine_domain(context.chain.get_height(), &name, &pub_key) {
MineResult::Fine => { MineResult::Fine => {
let zone = get_domain_zone(&name);
let difficulty = context.chain.get_zone_difficulty(&zone);
std::mem::drop(context); std::mem::drop(context);
create_domain(c, miner, CLASS_DOMAIN, &name, data, difficulty, &keystore); create_domain(c, miner, CLASS_DOMAIN, &name, data, DOMAIN_DIFFICULTY, &keystore);
let _ = web_view.eval("domainMiningStarted();"); let _ = web_view.eval("domainMiningStarted();");
event_info(web_view, &format!("Mining of domain \\'{}\\' has started", &name)); event_info(web_view, &format!("Mining of domain \\'{}\\' has started", &name));
} }
@ -434,72 +406,6 @@ fn action_create_domain(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>,
} }
} }
fn action_create_zone(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>, web_view: &mut WebView<()>, name: String, data: String) {
if context.lock().unwrap().chain.is_waiting_signers() {
show_warning(web_view, "Waiting for last full block to be signed. Try again later.");
info!("Waiting for last full block to be signed. Try again later.");
return;
}
let name = name.to_lowercase();
if name.len() > ZONE_MAX_LENGTH || !check_domain(&name, false) || context.lock().unwrap().x_zones.has_zone(&name) {
warn!("This zone is unavailable for mining!");
show_warning(web_view, "This zone is unavailable for mining!");
return;
}
let data = data.to_lowercase();
let mut data = match serde_json::from_str::<ZoneData>(&data) {
Ok(zone) => {
if zone.difficulty < ZONE_MIN_DIFFICULTY {
warn!("Zone difficulty cannot be lower than {}!", ZONE_MIN_DIFFICULTY);
show_warning(web_view, &format!("Zone difficulty cannot be lower than {}!", ZONE_MIN_DIFFICULTY));
return;
}
if name != zone.name {
warn!("Something wrong with zone data!");
show_warning(web_view, "Something wrong with zone data!");
return;
}
zone
}
Err(_) => {
warn!("Something wrong with zone data!");
show_warning(web_view, "Something wrong with zone data!");
return;
}
};
let (keystore, transaction) = {
let context = context.lock().unwrap();
(context.get_keystore(), context.chain.get_domain_transaction(&name))
};
if let Some(keystore) = keystore {
data.owners = if data.owners.is_empty() {
vec!(keystore.get_public())
} else {
data.owners
};
let data = serde_json::to_string(&data).unwrap();
match transaction {
None => {
create_zone(Arc::clone(&context), miner.clone(), CLASS_ZONE, &name, &data, ZONE_DIFFICULTY, &keystore);
event_info(web_view, &format!("Mining of zone \\'{}\\' has started", &name));
}
Some(transaction) => {
if transaction.pub_key == keystore.get_public() {
create_zone(Arc::clone(&context), miner.clone(), CLASS_ZONE, &name, &data, ZONE_DIFFICULTY, &keystore);
event_info(web_view, &format!("Mining of zone \\'{}\\' has started", &name));
} else {
warn!("Tried to mine not owned domain!");
show_warning(web_view, "You cannot change domain that you don't own!");
}
}
}
} else {
warn!("Can not mine without keys!");
show_warning(web_view, "You don't have keys loaded!<br>Load or mine the keys and try again.");
}
}
fn show_warning(web_view: &mut WebView<()>, text: &str) { fn show_warning(web_view: &mut WebView<()>, text: &str) {
let str = text.replace('\'', "\\'"); let str = text.replace('\'', "\\'");
match web_view.eval(&format!("showWarning('{}');", &str)) { match web_view.eval(&format!("showWarning('{}');", &str)) {
@ -574,18 +480,6 @@ fn format_event_now(kind: &str, message: &str) -> String {
format!("addEvent('{}', '{}', '{}');", kind, time.format("%d.%m.%y %X"), message) format!("addEvent('{}', '{}', '{}');", kind, time.format("%d.%m.%y %X"), message)
} }
fn create_zone(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>, class: &str, name: &str, data: &str, difficulty: u32, keystore: &Keystore) {
let name = name.to_owned();
info!("Generating domain or zone {}", &name);
if context.lock().unwrap().x_zones.has_zone(&name) {
error!("Unable to mine IANA/OpenNIC/etc zone {}!", &name);
return;
}
let transaction = Transaction::from_str(name, class.to_owned(), data.to_owned(), keystore.get_public().clone());
let block = Block::new(Some(transaction), keystore.get_public(), Bytes::default(), difficulty);
miner.lock().unwrap().add_block(block, keystore.clone());
}
fn create_domain(_context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>, class: &str, name: &str, mut data: DomainData, difficulty: u32, keystore: &Keystore) { fn create_domain(_context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>, class: &str, name: &str, mut data: DomainData, difficulty: u32, keystore: &Keystore) {
let name = name.to_owned(); let name = name.to_owned();
let confirmation = hash_identity(&name, Some(&keystore.get_public())); let confirmation = hash_identity(&name, Some(&keystore.get_public()));
@ -604,8 +498,6 @@ pub enum Cmd {
LoadKey, LoadKey,
CreateKey, CreateKey,
SaveKey, SaveKey,
CheckZone { name: String },
MineZone { name: String, data: String },
CheckRecord { data: String }, CheckRecord { data: String },
CheckDomain { name: String }, CheckDomain { name: String },
MineDomain { name: String, data: String }, MineDomain { name: String, data: String },

@ -29,14 +29,6 @@
<span>Domains</span> <span>Domains</span>
</a> </a>
</li> </li>
<li class="tab">
<a onclick="openTab(this, 'tab_zones')">
<span class="icon">
<svg viewBox="0 0 24 24" style="width: 20px; height: 20px;"><path d="M18.25,22L15.5,19L16.66,17.82L18.25,19.41L21.84,15.82L23,17.23M20.5,3A0.5,0.5 0 0,1 21,3.5V13.36C20.36,13.13 19.69,13 19,13C17.46,13 16.06,13.6 15,14.56V7.1L9,5V16.9L13.04,18.3C13,18.54 13,18.77 13,19C13,19.46 13.06,19.92 13.16,20.36L9,18.9L3.66,20.97C3.59,21 3.55,21 3.5,21A0.5,0.5 0 0,1 3,20.5V5.38C3,5.15 3.16,4.97 3.35,4.9L9,3L15,5.1L20.33,3"></path></svg>
</span>
<span>Zones</span>
</a>
</li>
<li class="tab"> <li class="tab">
<a onclick="openTab(this, 'tab_events')"> <a onclick="openTab(this, 'tab_events')">
<span class="icon"> <span class="icon">
@ -71,7 +63,7 @@
<button class="button is-link" id="new_key_button" onclick="createKey();" title="Generate new keypair, suitable to mine domains">Mine new key</button> <button class="button is-link" id="new_key_button" onclick="createKey();" title="Generate new keypair, suitable to mine domains">Mine new key</button>
</div> </div>
</div> </div>
<p class="help">To mine domains (or zones) you need to mine a strong pair of keys.</p> <p class="help">To mine domains you need to mine a strong pair of keys.</p>
</div> </div>
<!-- Domain mining --> <!-- Domain mining -->
@ -82,32 +74,6 @@
<button class="button is-link mt-2" onclick="showNewDomainDialog()" style="max-width: 200px;">New domain</button> <button class="button is-link mt-2" onclick="showNewDomainDialog()" style="max-width: 200px;">New domain</button>
</div> </div>
<!-- Zone mining -->
<div class="tab row page is-hidden" id="tab_zones">
<div class="field is-grouped">
<div class="control is-expanded has-icons-left">
<input class="input" type="text" placeholder="ygg" id="new_zone" oninput="onZoneChange()" title="The name of your zone, like com, net or 007, who knows?">
<span class="icon is-small is-left">
<svg viewBox="0 0 24 24" style="width: 20px; height: 20px;"><path d="M18.25,22L15.5,19L16.66,17.82L18.25,19.41L21.84,15.82L23,17.23M20.5,3A0.5,0.5 0 0,1 21,3.5V13.36C20.36,13.13 19.69,13 19,13C17.46,13 16.06,13.6 15,14.56V7.1L9,5V16.9L13.04,18.3C13,18.54 13,18.77 13,19C13,19.46 13.06,19.92 13.16,20.36L9,18.9L3.66,20.97C3.59,21 3.55,21 3.5,21A0.5,0.5 0 0,1 3,20.5V5.38C3,5.15 3.16,4.97 3.35,4.9L9,3L15,5.1L20.33,3"></path></svg>
</span>
</div>
<div class="control has-icons-left">
<input class="input" type="number" placeholder="Difficulty: 22-28" id="new_zone_difficulty" oninput="onZoneChange()" title="How difficult it will be to mine domains in this zone? Good numbers are 20-28.">
<span class="icon is-small is-left">
<svg viewBox="0 0 24 24" style="width: 24px; height: 24px;"><path d="M17.66 11.2C17.43 10.9 17.15 10.64 16.89 10.38C16.22 9.78 15.46 9.35 14.82 8.72C13.33 7.26 13 4.85 13.95 3C13 3.23 12.17 3.75 11.46 4.32C8.87 6.4 7.85 10.07 9.07 13.22C9.11 13.32 9.15 13.42 9.15 13.55C9.15 13.77 9 13.97 8.8 14.05C8.57 14.15 8.33 14.09 8.14 13.93C8.08 13.88 8.04 13.83 8 13.76C6.87 12.33 6.69 10.28 7.45 8.64C5.78 10 4.87 12.3 5 14.47C5.06 14.97 5.12 15.47 5.29 15.97C5.43 16.57 5.7 17.17 6 17.7C7.08 19.43 8.95 20.67 10.96 20.92C13.1 21.19 15.39 20.8 17.03 19.32C18.86 17.66 19.5 15 18.56 12.72L18.43 12.46C18.22 12 17.66 11.2 17.66 11.2M14.5 17.5C14.22 17.74 13.76 18 13.4 18.1C12.28 18.5 11.16 17.94 10.5 17.28C11.69 17 12.4 16.12 12.61 15.23C12.78 14.43 12.46 13.77 12.33 13C12.21 12.26 12.23 11.63 12.5 10.94C12.69 11.32 12.89 11.7 13.13 12C13.9 13 15.11 13.44 15.37 14.8C15.41 14.94 15.43 15.08 15.43 15.23C15.46 16.05 15.1 16.95 14.5 17.5H14.5Z"></path></svg>
</span>
</div>
<div class="buttons has-addons">
<button class="button is-link" id="new_zone_button" onclick="createZone();" title="Start mining">Mine zone</button>
</div>
</div>
<label class="checkbox mb-1" style="align-self: flex-start;" title="This zone's domains will have only IPv6 addresses from Yggdrasil network (200::/7).">
<input type="checkbox" id="yggdrasil_only">
Restrict this zone to <a onclick="open_link('https://yggdrasil-network.github.io');">Yggdrasil</a> only.
</label>
<p class="help">If you feel that we need another zone you can mine that too. Just select a name, a difficulty for domains in that zone, and hit "Mine zone".</p>
</div>
<!-- Events and notifications --> <!-- Events and notifications -->
<div class="tab row page is-hidden list" id="tab_events" style="margin-bottom: 10pt;"> <div class="tab row page is-hidden list" id="tab_events" style="margin-bottom: 10pt;">
<!-- Events are getting here --> <!-- Events are getting here -->
@ -221,7 +187,7 @@
</div> </div>
</div> </div>
</div> </div>
<p class="help">Enter domain name, add some DNS-records, then hit the "Mine domain" button!</p> <p class="help">Enter domain name, choose domain zone, add some DNS-records, then hit the "Mine domain" button! Note: zones with * are restricted to <a onclick="open_link('https://yggdrasil-network.github.io');">Yggdrasil</a> only.</p>
<div class="list mt-2" id="domain_records"> <div class="list mt-2" id="domain_records">
<!-- Here will be our domain records, added by dialog --> <!-- Here will be our domain records, added by dialog -->

@ -246,7 +246,6 @@ function domainMiningStarted() {
document.getElementById("domain_records").disabled = true; document.getElementById("domain_records").disabled = true;
document.getElementById("add_record_button").disabled = true; document.getElementById("add_record_button").disabled = true;
document.getElementById("new_domain_button").disabled = true; document.getElementById("new_domain_button").disabled = true;
document.getElementById("new_zone_button").disabled = true;
document.getElementById("new_key_button").disabled = true; document.getElementById("new_key_button").disabled = true;
} }
@ -258,23 +257,9 @@ function domainMiningUnavailable() {
document.getElementById("domain_records").disabled = true; document.getElementById("domain_records").disabled = true;
document.getElementById("add_record_button").disabled = true; document.getElementById("add_record_button").disabled = true;
document.getElementById("new_domain_button").disabled = true; document.getElementById("new_domain_button").disabled = true;
document.getElementById("new_zone_button").disabled = true;
document.getElementById("new_key_button").disabled = true; document.getElementById("new_key_button").disabled = true;
} }
function createZone() {
var new_zone = document.getElementById("new_zone").value;
var difficulty = document.getElementById("new_zone_difficulty").value;
var yggdrasil = document.getElementById("yggdrasil_only").checked;
var obj = {};
obj.name = new_zone;
obj.difficulty = parseInt(difficulty);
obj.yggdrasil = yggdrasil;
obj.owners = []; // TODO make a dialog to fill them
data = JSON.stringify(obj);
external.invoke(JSON.stringify({cmd: 'mineZone', name: new_zone, data: data}));
}
function sendAction(param) { function sendAction(param) {
external.invoke(JSON.stringify(param)); external.invoke(JSON.stringify(param));
} }
@ -301,40 +286,6 @@ function domainAvailable(available) {
} }
} }
function onZoneChange() {
var button = document.getElementById("new_zone_button");
var diff = document.getElementById("new_zone_difficulty");
d = parseInt(diff.value);
// Checking for NaN first
if (d != d || d < 15 || d > 30) {
button.disabled = true;
diff.className = "input is-danger";
} else {
diff.className = "input";
var input = document.getElementById("new_zone");
external.invoke(JSON.stringify({cmd: 'checkZone', name: input.value}));
}
}
function zoneAvailable(available) {
var input = document.getElementById("new_zone");
var button = document.getElementById("new_zone_button");
if (available) {
input.className = "input";
button.disabled = false;
var diff = document.getElementById("new_zone_difficulty");
d = parseInt(diff.value);
// Checking for NaN first
if (d != d || d < 15 || d > 30) {
button.disabled = true;
diff.className = "input is-danger";
}
} else {
input.className = "input is-danger";
button.disabled = true;
}
}
function showModalDialog(text, callback) { function showModalDialog(text, callback) {
var message = document.getElementById("modal_text"); var message = document.getElementById("modal_text");
message.textContent = text; message.textContent = text;
@ -443,7 +394,6 @@ function showMiningIndicator(visible, blue) {
document.getElementById("domain_records").disabled = false; document.getElementById("domain_records").disabled = false;
document.getElementById("add_record_button").disabled = false; document.getElementById("add_record_button").disabled = false;
document.getElementById("new_domain_button").disabled = false; document.getElementById("new_domain_button").disabled = false;
document.getElementById("new_zone_button").disabled = false;
document.getElementById("new_key_button").disabled = false; document.getElementById("new_key_button").disabled = false;
} }
} }
@ -493,11 +443,6 @@ function keystoreChanged(path, pub_key, hash) {
var new_domain = document.getElementById("new_domain"); var new_domain = document.getElementById("new_domain");
new_domain.disabled = false; new_domain.disabled = false;
var new_zone = document.getElementById("new_zone");
new_zone.disabled = false;
var new_zone_difficulty = document.getElementById("new_zone_difficulty");
new_zone_difficulty.disabled = false;
} }
function closeZonesDropdown() { function closeZonesDropdown() {
@ -520,7 +465,11 @@ function refreshZonesList() {
}); });
availableZones.forEach(function(value, index, array) { availableZones.forEach(function(value, index, array) {
var zone = value.name + " (" + value.difficulty + "🔥)"; var note = "";
if (value.yggdrasil) {
note = "*";
}
var zone = value.name + note;
var add_class = ""; var add_class = "";
if (typeof currentZone !== 'undefined' && currentZone.name == value.name) { if (typeof currentZone !== 'undefined' && currentZone.name == value.name) {
add_class = "is-active"; add_class = "is-active";
@ -535,7 +484,11 @@ function refreshZonesList() {
links.innerHTML = buf; links.innerHTML = buf;
if (typeof currentZone !== 'undefined') { if (typeof currentZone !== 'undefined') {
var cur_name = document.getElementById("zones-current-name"); var cur_name = document.getElementById("zones-current-name");
cur_name.innerHTML = "." + currentZone.name + " (" + currentZone.difficulty + "🔥)"; var name = "." + currentZone.name;
if (currentZone.yggdrasil) {
name = name + "*";
}
cur_name.innerHTML = name;
} }
} }

Loading…
Cancel
Save