First commit of 0.5.* branch.

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

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

@ -4,7 +4,7 @@
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)

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

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

@ -20,17 +20,8 @@ CREATE TABLE domains (
'identity' BINARY,
'confirmation' BINARY,
'data' TEXT,
'pub_key' BINARY
'owner' BINARY
);
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);

@ -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();
match data {
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);
// Create DnsPacket
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 {
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 {
BlockchainFilter::add_soa_record(zone.to_owned(), serial, &mut packet);
}

@ -1,4 +1,5 @@
use std::fmt;
use std::fmt::{Display, Formatter};
use serde::{Deserialize, Serialize, Serializer};
use serde::ser::SerializeStruct;
@ -6,29 +7,37 @@ use serde::ser::SerializeStruct;
use crate::blockchain::hash_utils::*;
use crate::bytes::Bytes;
use crate::dns::protocol::DnsRecord;
use std::fmt::{Display, Formatter};
use crate::{CLASS_ORIGIN, CLASS_DOMAIN};
extern crate serde;
extern crate serde_json;
#[derive(Clone, Deserialize, PartialEq)]
pub struct Transaction {
#[serde(default, skip_serializing_if = "Bytes::is_zero")]
pub identity: Bytes,
#[serde(default, skip_serializing_if = "Bytes::is_zero")]
pub confirmation: Bytes,
pub class: String,
pub data: String,
pub pub_key: Bytes,
#[serde(default, skip_serializing_if = "Bytes::is_zero")]
pub owner: Bytes,
}
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 confirmation = hash_identity(&identity, Some(&pub_key));
return Self::new(hash, confirmation, method, data, pub_key);
let confirmation = hash_identity(&identity, Some(&owner));
return Self::new(hash, confirmation, method, data, owner);
}
pub fn new(identity: Bytes, confirmation: Bytes, method: String, data: String, pub_key: Bytes) -> Self {
Transaction { identity, confirmation, class: method, data, pub_key }
pub fn new(identity: Bytes, confirmation: Bytes, method: String, data: String, owner: Bytes) -> Self {
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> {
@ -50,13 +59,13 @@ impl Transaction {
pub fn check_identity(&self, domain: &str) -> bool {
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)
}
/// Returns [DomainData] from this transaction if it has it
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) {
return Some(data)
}
@ -69,12 +78,9 @@ impl Transaction {
match what {
None => { TransactionType::Signing }
Some(transaction) => {
if let Some(_) = transaction.get_domain_data() {
if transaction.class == CLASS_DOMAIN {
return TransactionType::Domain;
}
if let Ok(_) = serde_json::from_str::<ZoneData>(&transaction.data) {
return TransactionType::Zone;
}
TransactionType::Unknown
}
}
@ -88,7 +94,7 @@ impl fmt::Debug for Transaction {
.field("confirmation", &self.confirmation)
.field("class", &self.class)
.field("data", &self.data)
.field("pub_key", &&self.pub_key)
.field("pub_key", &&self.owner)
.finish()
}
}
@ -100,7 +106,7 @@ impl Serialize for Transaction {
structure.serialize_field("confirmation", &self.confirmation)?;
structure.serialize_field("class", &self.class)?;
structure.serialize_field("data", &self.data)?;
structure.serialize_field("pub_key", &self.pub_key)?;
structure.serialize_field("pub_key", &self.owner)?;
structure.end()
}
}
@ -109,38 +115,28 @@ pub enum TransactionType {
Unknown,
Signing,
Domain,
Zone,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct DomainData {
pub domain: Bytes,
pub zone: String,
pub info: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub records: Vec<DnsRecord>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub contacts: Vec<ContactsData>,
#[serde(default)]
pub owners: Vec<Bytes>
}
impl DomainData {
pub fn new(domain: Bytes, zone: String, records: Vec<DnsRecord>, contacts: Vec<ContactsData>, owners: Vec<Bytes>) -> Self {
Self { domain, zone, records, contacts, owners }
pub fn new(domain: Bytes, zone: String, info: String, records: Vec<DnsRecord>, contacts: Vec<ContactsData>) -> Self {
Self { domain, zone, info, records, contacts }
}
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct ZoneData {
pub name: String,
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))
}
pub struct Origin {
zones: Bytes
}
#[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
#[derive(PartialEq)]
pub enum BlockQuality {
@ -34,4 +37,16 @@ impl Options {
pub fn empty() -> Self {
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;
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 ZONE_MIN_DIFFICULTY: u32 = 22;
pub const ORIGIN_DIFFICULTY: u32 = 30;
pub const DOMAIN_DIFFICULTY: u32 = 24;
pub const SIGNER_DIFFICULTY: u32 = 16;
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 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 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 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_READ_BLOCK_TIME: u128 = 500;
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('-') {
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_hyphen = false;
for char in name.chars() {
@ -60,6 +67,15 @@ pub fn check_domain(name: &str, allow_dots: bool) -> bool {
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 {
let parts: Vec<&str> = domain.rsplitn(2, ".").collect();
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("777.com", true));
assert!(!check_domain("77.com", true));
assert!(!check_domain("7.com", true));
}
#[test]

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

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

@ -16,7 +16,7 @@ use simplelog::*;
#[cfg(windows)]
use winapi::um::wincon::{ATTACH_PARENT_PROCESS, AttachConsole, FreeConsole};
use alfis::{Block, Bytes, Chain, Miner, Context, Network, Settings, dns_utils, Keystore, ZONE_DIFFICULTY, ALFIS_DEBUG, 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::process::exit;
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 let Some(keystore) = &context.keystore {
// 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());
}
}

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

@ -17,8 +17,7 @@ use mio::event::Event;
use mio::net::{TcpListener, TcpStream};
use rand::random;
use crate::{Block, Context, p2p::Message, p2p::Peer, p2p::Peers, p2p::State, Transaction};
use crate::blockchain::transaction::TransactionType;
use crate::{Block, Context, p2p::Message, p2p::Peer, p2p::Peers, p2p::State};
use crate::blockchain::types::BlockQuality;
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();
match context.chain.check_new_block(&block) {
BlockQuality::Good => {
let zone = matches!(Transaction::get_type(&block.transaction), TransactionType::Zone);
context.chain.add_block(block);
let my_height = context.chain.get_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 my_height == max_height {
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);
let last_block = context.chain.last_block().unwrap();
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");
let index = context.chain.get_height();
context.bus.post(crate::event::Event::BlockchainChanged { index });
if zone {
context.bus.post(crate::event::Event::ZonesChanged);
}
} else {
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 web_view::Content;
use alfis::{Block, Bytes, Context, get_domain_zone, Keystore, Transaction, ZONE_MIN_DIFFICULTY, is_yggdrasil_record};
use alfis::{check_domain, keys};
use alfis::blockchain::transaction::{DomainData, ZoneData};
use alfis::{Block, Bytes, Context, Keystore, Transaction};
use alfis::keys;
use alfis::blockchain::hash_utils::hash_identity;
use alfis::blockchain::transaction::DomainData;
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::event::Event;
use alfis::miner::Miner;
use Cmd::*;
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>>) {
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);
}
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); }
Open { link } => {
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) {
match serde_json::from_str::<DnsRecord>(&data) {
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)
}
}
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} => {
debug!("Current blockchain height is {}", 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();
if index > 0 {
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");
}
@ -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
let zones = context.chain.get_zones();
for z in &zones {
for z in zones {
if z.name == data.zone {
if z.yggdrasil {
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) {
MineResult::Fine => {
let zone = get_domain_zone(&name);
let difficulty = context.chain.get_zone_difficulty(&zone);
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();");
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) {
let str = text.replace('\'', "\\'");
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)
}
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) {
let name = name.to_owned();
let confirmation = hash_identity(&name, Some(&keystore.get_public()));
@ -604,8 +498,6 @@ pub enum Cmd {
LoadKey,
CreateKey,
SaveKey,
CheckZone { name: String },
MineZone { name: String, data: String },
CheckRecord { data: String },
CheckDomain { name: String },
MineDomain { name: String, data: String },

@ -29,14 +29,6 @@
<span>Domains</span>
</a>
</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">
<a onclick="openTab(this, 'tab_events')">
<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>
</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>
<!-- Domain mining -->
@ -82,32 +74,6 @@
<button class="button is-link mt-2" onclick="showNewDomainDialog()" style="max-width: 200px;">New domain</button>
</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 -->
<div class="tab row page is-hidden list" id="tab_events" style="margin-bottom: 10pt;">
<!-- Events are getting here -->
@ -221,7 +187,7 @@
</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">
<!-- Here will be our domain records, added by dialog -->

@ -246,7 +246,6 @@ function domainMiningStarted() {
document.getElementById("domain_records").disabled = true;
document.getElementById("add_record_button").disabled = true;
document.getElementById("new_domain_button").disabled = true;
document.getElementById("new_zone_button").disabled = true;
document.getElementById("new_key_button").disabled = true;
}
@ -258,23 +257,9 @@ function domainMiningUnavailable() {
document.getElementById("domain_records").disabled = true;
document.getElementById("add_record_button").disabled = true;
document.getElementById("new_domain_button").disabled = true;
document.getElementById("new_zone_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) {
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) {
var message = document.getElementById("modal_text");
message.textContent = text;
@ -443,7 +394,6 @@ function showMiningIndicator(visible, blue) {
document.getElementById("domain_records").disabled = false;
document.getElementById("add_record_button").disabled = false;
document.getElementById("new_domain_button").disabled = false;
document.getElementById("new_zone_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");
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() {
@ -520,7 +465,11 @@ function refreshZonesList() {
});
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 = "";
if (typeof currentZone !== 'undefined' && currentZone.name == value.name) {
add_class = "is-active";
@ -535,7 +484,11 @@ function refreshZonesList() {
links.innerHTML = buf;
if (typeof currentZone !== 'undefined') {
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