Merge branch 'master' into master

pull/291/head
sshofbrain 2 years ago committed by GitHub
commit af571f31d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -55,7 +55,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
arch: [ amd64, i686, mips, mipsel, armhf, arm64 ]
arch: [ amd64, i686, mips, mipsel, armhf, armlf, arm64 ]
defaults:
run:
shell: bash

134
Cargo.lock generated

@ -93,7 +93,7 @@ dependencies = [
[[package]]
name = "alfis"
version = "0.7.8"
version = "0.8.3"
dependencies = [
"base64",
"bincode",
@ -102,7 +102,7 @@ dependencies = [
"chacha20poly1305",
"chrono",
"derive_more",
"digest 0.10.3",
"digest 0.10.5",
"ecies-ed25519",
"ed25519-dalek",
"getopts",
@ -122,12 +122,13 @@ dependencies = [
"serde_cbor",
"serde_derive",
"serde_json",
"sha2 0.10.2",
"sha2 0.10.6",
"signature",
"simplelog",
"spmc",
"sqlite",
"thread-priority",
"time 0.3.11",
"time 0.3.14",
"tinyfiledialogs",
"toml",
"ureq",
@ -138,6 +139,15 @@ dependencies = [
"x25519-dalek",
]
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]]
name = "atk-sys"
version = "0.10.0"
@ -298,15 +308,17 @@ dependencies = [
[[package]]
name = "chrono"
version = "0.4.19"
version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1"
dependencies = [
"libc",
"iana-time-zone",
"js-sys",
"num-integer",
"num-traits",
"serde",
"time 0.1.44",
"wasm-bindgen",
"winapi",
]
@ -340,6 +352,12 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
[[package]]
name = "core-foundation-sys"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
[[package]]
name = "cpufeatures"
version = "0.2.1"
@ -440,9 +458,9 @@ dependencies = [
[[package]]
name = "digest"
version = "0.10.3"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c"
dependencies = [
"block-buffer 0.10.0",
"crypto-common",
@ -708,6 +726,20 @@ dependencies = [
"digest 0.9.0",
]
[[package]]
name = "iana-time-zone"
version = "0.1.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c495f162af0bf17656d0014a0eded5f3cd2f365fdd204548c2869db89359dc7"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"js-sys",
"once_cell",
"wasm-bindgen",
"winapi",
]
[[package]]
name = "idna"
version = "0.2.3"
@ -736,9 +768,9 @@ dependencies = [
[[package]]
name = "js-sys"
version = "0.3.55"
version = "0.3.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84"
checksum = "258451ab10b34f8af53416d1fdab72c22e805f0c92a1136d59470ec0b11138b2"
dependencies = [
"wasm-bindgen",
]
@ -751,9 +783,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.123"
version = "0.2.132"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb691a747a7ab48abc15c5b42066eaafde10dc427e3b6ee2a1cf43db04c763bd"
checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5"
[[package]]
name = "log"
@ -776,9 +808,9 @@ dependencies = [
[[package]]
name = "lru"
version = "0.7.8"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a"
checksum = "b6e8aaa3f231bb4bd57b84b2d5dc3ae7f350265df8aa96492e0bc394a1571909"
dependencies = [
"hashbrown",
]
@ -886,9 +918,9 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.8.0"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0"
[[package]]
name = "opaque-debug"
@ -898,9 +930,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "open"
version = "3.0.2"
version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f23a407004a1033f53e93f9b45580d14de23928faad187384f891507c9b0c045"
checksum = "b4a3100141f1733ea40b53381b0ae3117330735ef22309a190ac57b9576ea716"
dependencies = [
"pathdiff",
"windows-sys",
@ -1119,18 +1151,18 @@ checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012"
[[package]]
name = "serde"
version = "1.0.140"
version = "1.0.144"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc855a42c7967b7c369eb5860f7164ef1f6f81c20c7cc1141f2a604e18723b03"
checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_bytes"
version = "0.11.6"
version = "0.11.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "212e73464ebcde48d723aa02eb270ba62eff38a9b732df31f33f1b4e145f3a54"
checksum = "cfc50e8183eeeb6178dcb167ae34a8051d63535023ae38b5d8d12beae193d37b"
dependencies = [
"serde",
]
@ -1147,9 +1179,9 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.140"
version = "1.0.144"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f2122636b9fe3b81f1cb25099fcf2d3f542cdb1d45940d56c713158884a05da"
checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00"
dependencies = [
"proc-macro2",
"quote",
@ -1158,9 +1190,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.82"
version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7"
checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44"
dependencies = [
"itoa",
"ryu",
@ -1182,20 +1214,20 @@ dependencies = [
[[package]]
name = "sha2"
version = "0.10.2"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676"
checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
dependencies = [
"cfg-if",
"cpufeatures",
"digest 0.10.3",
"digest 0.10.5",
]
[[package]]
name = "signature"
version = "1.5.0"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f054c6c1a6e95179d6f23ed974060dcefb2d9388bb7256900badad682c499de4"
checksum = "f0ea32af43239f0d353a7dd75a22d94c329c8cdaafdcb4c1c1335aa10c298a4a"
[[package]]
name = "simplelog"
@ -1205,7 +1237,7 @@ checksum = "48dfff04aade74dd495b007c831cd6f4e0cee19c344dd9dc0884c0289b70a786"
dependencies = [
"log",
"termcolor",
"time 0.3.11",
"time 0.3.14",
]
[[package]]
@ -1229,6 +1261,12 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "spmc"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02a8428da277a8e3a15271d79943e80ccc2ef254e78813a166a08d65e4c3ece5"
[[package]]
name = "sqlite"
version = "0.26.0"
@ -1352,9 +1390,9 @@ dependencies = [
[[package]]
name = "thread-priority"
version = "0.8.2"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "696668a68983ad737e08e11e9afb701e962cab9f07f2a4ff339316b2d5b0870d"
checksum = "5a8a950b52fd40d98ac6ed41c7fa9e8dd62b131f48b74f418e810476b01a7460"
dependencies = [
"cfg-if",
"libc",
@ -1375,9 +1413,9 @@ dependencies = [
[[package]]
name = "time"
version = "0.3.11"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72c91f41dcb2f096c05f0873d667dceec1087ce5bcf984ec8ffb19acddbb3217"
checksum = "3c3f9a28b618c3a6b9251b6908e9c99e04b9e5c02e6581ccbb67d59c34ef7f9b"
dependencies = [
"itoa",
"libc",
@ -1557,9 +1595,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.78"
version = "0.2.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce"
checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
@ -1567,13 +1605,13 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.78"
version = "0.2.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b"
checksum = "662cd44805586bd52971b9586b1df85cdbbd9112e4ef4d8f41559c334dc6ac3f"
dependencies = [
"bumpalo",
"lazy_static",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn",
@ -1582,9 +1620,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.78"
version = "0.2.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9"
checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@ -1592,9 +1630,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.78"
version = "0.2.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab"
checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da"
dependencies = [
"proc-macro2",
"quote",
@ -1605,9 +1643,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.78"
version = "0.2.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc"
checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a"
[[package]]
name = "web-sys"

@ -1,6 +1,6 @@
[package]
name = "alfis"
version = "0.7.8"
version = "0.8.4"
authors = ["Revertron <alfis@revertron.com>"]
edition = "2021"
build = "build.rs"
@ -15,58 +15,61 @@ getopts = "0.2.21"
log = "0.4.17"
simplelog = "0.12.0"
toml = "0.5.9"
digest = "0.10.2"
sha2 = "0.10.2"
digest = "0.10.5"
sha2 = "0.10.6"
ed25519-dalek = "1.0"
x25519-dalek = "1.2"
ecies-ed25519 = "0.5"
chacha20poly1305 = "0.9.1"
signature = "1.5"
signature = "1.6"
blakeout = "0.3.0"
num_cpus = "1.13.1"
byteorder = "1.4.3"
serde = { version = "1.0.140", features = ["derive"] }
serde_json = "1.0.82"
serde = { version = "1.0.144", features = ["derive"] }
serde_json = "1.0.85"
bincode = "1.3.3"
serde_cbor = "0.11.2"
base64 = "0.13.0"
num-bigint = "0.4.3"
num-traits = "0.2.15"
chrono = { version = "0.4.19", features = ["serde"] }
time = "0.3.11"
chrono = { version = "0.4.20", features = ["serde"] }
time = "0.3.14"
rand = { version = "0.8.5", package = "rand" }
rand-old = { package = "rand", version = "0.7.0" } # For ed25519-dalek
sqlite = "0.26.0"
uuid = { version = "1.1.2", features = ["serde", "v4"] }
mio = { version = "0.8.4", features = ["os-poll", "net"] }
ureq = { version = "2.5", optional = true }
lru = "0.7.8"
lru = "0.8.1"
derive_more = "0.99.17"
lazy_static = "1.4.0"
log-panics = { version = "2.1.0", features = ["with-backtrace"]}
spmc = "0.3.0"
# Optional dependencies regulated by features
web-view = { version = "0.7.3", features = [], optional = true }
tinyfiledialogs = { version = "3.9.1", optional = true }
open = { version = "3.0.2", optional = true }
open = { version = "3.0.3", optional = true }
[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3.9", features = ["impl-default", "wincon", "shellscalingapi"] }
thread-priority = "0.8.2"
thread-priority = "0.9.2"
[target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))'.dependencies]
thread-priority = "0.8.2"
thread-priority = "0.9.2"
[build-dependencies]
winres = "0.1.12"
[dev-dependencies]
serde_bytes = "0.11.6"
serde_bytes = "0.11.7"
serde_derive = "1.0.126"
[profile.release]
opt-level = 3
lto = true
strip = true # Automatically strip symbols from the binary.
[profile.dev]
opt-level = 2

File diff suppressed because it is too large Load Diff

@ -2,11 +2,11 @@
"nodes": {
"flake-utils": {
"locked": {
"lastModified": 1638122382,
"narHash": "sha256-sQzZzAbvKEqN9s0bzWuYmRaA03v40gaJ4+iL1LXjaeI=",
"lastModified": 1659877975,
"narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "74f7e4319258e287b0f9cb95426c9853b282730b",
"rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
"type": "github"
},
"original": {
@ -20,11 +20,11 @@
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1639947939,
"narHash": "sha256-pGsM8haJadVP80GFq4xhnSpNitYNQpaXk4cnA796Cso=",
"lastModified": 1659610603,
"narHash": "sha256-LYgASYSPYo7O71WfeUOaEUzYfzuXm8c8eavJcel+pfI=",
"owner": "nmattia",
"repo": "naersk",
"rev": "2fc8ce9d3c025d59fee349c1f80be9785049d653",
"rev": "c6a45e4277fa58abd524681466d3450f896dc094",
"type": "github"
},
"original": {
@ -35,11 +35,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1640234302,
"narHash": "sha256-dALA+cOam5jQ2KOYdWiv6H6Y2stcYG6eclWQQVGx/FI=",
"lastModified": 1660227034,
"narHash": "sha256-bXMlG/YU0IjAod6M625XT1YbUG+/3L9ypk9llYpKeuM=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "81cbfc8f2a1e218249b7bff74013b63150171496",
"rev": "964d60ff2e6bc76c0618962da52859603784fa78",
"type": "github"
},
"original": {
@ -49,11 +49,11 @@
},
"nixpkgs_2": {
"locked": {
"lastModified": 1640139330,
"narHash": "sha256-Nkp3wUOGwtoQ7EH28RLVJ7EqB/e0TU7VcsM7GLy+SdY=",
"lastModified": 1660162369,
"narHash": "sha256-pZukMP4zCA1FaBg0xHxf7KdE/Nv/C5YbDID7h2L8O7A=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "81cef6b70fb5d5cdba5a0fef3f714c2dadaf0d6d",
"rev": "3a11db5f408095b8f08b098ec2066947f4b72ce2",
"type": "github"
},
"original": {

@ -12,7 +12,7 @@ use sqlite::{Connection, State, Statement};
use lazy_static::lazy_static;
use crate::blockchain::hash_utils::*;
use crate::blockchain::transaction::DomainData;
use crate::blockchain::transaction::{DomainData, DomainState};
use crate::blockchain::types::BlockQuality::*;
use crate::blockchain::types::MineResult::*;
use crate::blockchain::types::{BlockQuality, MineResult, Options, ZoneData};
@ -141,12 +141,9 @@ impl Chain {
if WRONG_HASHES.contains(&block.hash) {
error!("Block {} has bad hash:\n{:?}", block.index, &block);
info!("Truncating database from block {}...", block.index);
match self.truncate_db_from_block(block.index) {
Ok(_) => {}
Err(e) => {
error!("{}", e);
panic!("Error truncating database! Please, delete 'blockchain.db' and restart.");
}
if let Err(e) = self.truncate_db_from_block(block.index) {
error!("{}", e);
panic!("Error truncating database! Please, delete 'blockchain.db' and restart.");
}
break;
}
@ -487,40 +484,62 @@ impl Chain {
None
}
/// Checks if any domain is available to mine for this client (pub_key)
pub fn is_domain_available(&self, height: u64, domain: &str, public_key: &Bytes) -> bool {
if domain.is_empty() {
return false;
pub fn can_mine_domain(&self, height: u64, domain: &str, pub_key: &Bytes) -> MineResult {
let name = domain.to_lowercase();
if !check_domain(&name, true) {
return WrongName;
}
let identity_hash = hash_identity(domain, None);
if !self.is_id_available(height, &identity_hash, public_key) {
warn!("Domain {} is not available!", domain);
return false;
let zone = get_domain_zone(&name);
if !self.is_available_zone(&zone) {
return WrongZone;
}
let parts: Vec<&str> = domain.rsplitn(2, '.').collect();
if parts.len() > 1 {
// We do not support third level domains
if parts.last().unwrap().contains('.') {
return false;
let (transaction, state) = self.get_domain_transaction_and_state(&name);
if let Some(transaction) = transaction {
let owner = transaction.signing.eq(pub_key);
match state {
DomainState::NotFound => {}
DomainState::Alive { .. } => if !owner {
return NotOwned;
},
DomainState::Expired { .. } => if !owner {
return NotOwned;
},
DomainState::Free { .. } => {}
}
return self.is_available_zone(parts.first().unwrap());
}
true
let identity_hash = hash_identity(&name, None);
self.can_mine_identity(&identity_hash, height, Utc::now().timestamp(), 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) -> bool {
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.as_slice()).expect("Error in bind");
while let State::Row = statement.next().unwrap() {
let pub_key = Bytes::from_bytes(&statement.read::<Vec<u8>>(0).unwrap());
if !pub_key.eq(public_key) {
return false;
fn can_mine_identity(&self, identity_hash: &Bytes, height: u64, time: i64, pub_key: &Bytes) -> MineResult {
if let Some(last) = self.get_last_full_block(height, Some(pub_key)) {
// If this domain/identity is new
let want_new_domain = !self.is_domain_in_blockchain(height, &identity_hash);
// And the user hasn't mined a domain in previous 24h, then we allow her to mine
let time = last.timestamp + NEW_DOMAINS_INTERVAL - time;
if want_new_domain && time > 0 {
return Cooldown { time };
}
}
true
Fine
}
/// Checks if this identity is free or is owned by the same pub_key
pub fn is_id_available(&self, height: u64, time: i64, identity: &Bytes, public_key: &Bytes) -> bool {
let (transaction, state) = self.get_identity_transaction_and_state(identity, height, time);
if transaction.is_none() {
return true;
}
if transaction.unwrap().signing.eq(public_key) {
return true;
}
match state {
DomainState::NotFound => true,
DomainState::Alive { .. } => false,
DomainState::Expired { .. } => false,
DomainState::Free { .. } => true
}
}
pub fn get_zones(&self) -> &Vec<ZoneData> {
@ -566,39 +585,12 @@ impl Chain {
false
}
pub fn can_mine_domain(&self, height: u64, domain: &str, pub_key: &Bytes) -> MineResult {
let name = domain.to_lowercase();
if !check_domain(&name, true) {
return WrongName;
}
let zone = get_domain_zone(&name);
if !self.is_available_zone(&zone) {
return WrongZone;
}
if let Some(transaction) = self.get_domain_transaction(&name) {
if transaction.signing.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_domain_in_blockchain(height, &identity_hash);
let time = last.timestamp + NEW_DOMAINS_INTERVAL - Utc::now().timestamp();
if new_id && time > 0 {
return Cooldown { time };
}
}
Fine
}
pub fn get_domain_renewal_time(&self, identity_hash: &Bytes) -> Option<i64> {
pub fn get_domain_renewal_time(&self, time: i64, identity_hash: &Bytes) -> Option<i64> {
let mut statement = self.db.prepare(SQL_GET_DOMAIN_UPDATE_TIME).unwrap();
statement.bind(1, identity_hash.as_slice()).expect("Error in bind");
if let State::Row = statement.next().unwrap() {
let timestamp = statement.read::<i64>(0).unwrap();
if timestamp < Utc::now().timestamp() - DOMAIN_LIFETIME {
if timestamp < time - DOMAIN_LIFETIME {
// This domain is too old
return None;
}
@ -622,21 +614,20 @@ impl Chain {
None
}
pub fn get_domain_transaction_by_id(&self, identity_hash: &Bytes, height: u64) -> Option<Transaction> {
if self.get_domain_renewal_time(identity_hash).is_none() {
// Domain has expired
return None;
}
pub fn get_identity_transaction_and_state(&self, identity_hash: &Bytes, height: u64, time: i64) -> (Option<Transaction>, DomainState) {
let mut statement = self.db.prepare(SQL_GET_DOMAIN_BY_ID).unwrap();
statement.bind(1, identity_hash.as_slice()).expect("Error in bind");
statement.bind(2, height as i64).expect("Error in bind");
if let State::Row = statement.next().unwrap() {
let timestamp = statement.read::<i64>(1).unwrap();
if timestamp < Utc::now().timestamp() - DOMAIN_LIFETIME {
// This domain is too old
return None;
}
// Determine current state of the domain
let state = if timestamp + DOMAIN_LIFETIME >= time {
DomainState::Alive { renewed_time: timestamp, until: timestamp + DOMAIN_LIFETIME }
} else if timestamp + DOMAIN_LIFETIME + DOMAIN_RENEW_TIME >= time {
DomainState::Expired { renewed_time: timestamp, until: timestamp + DOMAIN_LIFETIME + DOMAIN_RENEW_TIME }
} else {
DomainState::Free { renewed_time: timestamp }
};
let identity = Bytes::from_bytes(&statement.read::<Vec<u8>>(2).unwrap());
let confirmation = Bytes::from_bytes(&statement.read::<Vec<u8>>(3).unwrap());
let class = String::from(CLASS_DOMAIN);
@ -644,30 +635,37 @@ impl Chain {
let signing = Bytes::from_bytes(&statement.read::<Vec<u8>>(5).unwrap());
let encryption = Bytes::from_bytes(&statement.read::<Vec<u8>>(6).unwrap());
let transaction = Transaction { identity, confirmation, class, data, signing, encryption };
return Some(transaction);
return (Some(transaction), state);
}
None
(None, DomainState::NotFound)
}
/// Gets full Transaction info for any domain. Used by DNS part.
pub fn get_domain_transaction(&self, domain: &str) -> Option<Transaction> {
pub fn get_domain_transaction_and_state(&self, domain: &str) -> (Option<Transaction>, DomainState) {
if domain.is_empty() {
return None;
return (None, DomainState::NotFound);
}
let identity_hash = hash_identity(domain, None);
if let Some(transaction) = self.get_domain_transaction_by_id(&identity_hash, self.get_height()) {
let (transaction, state) = self.get_identity_transaction_and_state(&identity_hash, self.get_height(), Utc::now().timestamp());
if let Some(transaction) = transaction {
debug!("Found transaction for domain {}: {:?}", domain, &transaction);
if transaction.check_identity(domain) {
return Some(transaction);
return (Some(transaction), state);
}
}
None
(None, DomainState::NotFound)
}
pub fn get_domain_info(&self, domain: &str) -> Option<String> {
match self.get_domain_transaction(domain) {
None => None,
Some(transaction) => Some(transaction.data)
match self.get_domain_transaction_and_state(domain) {
(None, _) => None,
(Some(transaction), state) => {
if matches!(state, DomainState::Alive {..}) {
Some(transaction.data)
} else {
None
}
}
}
}
@ -715,7 +713,9 @@ impl Chain {
let signing = Bytes::from_bytes(&statement.read::<Vec<u8>>(3).unwrap());
// Get the last transaction for this id and check if it is still ours
if let Some(transaction) = self.get_domain_transaction_by_id(&identity, height) {
// TODO use state to show it in UI
let (transaction, _state) = self.get_identity_transaction_and_state(&identity, height, Utc::now().timestamp());
if let Some(transaction) = transaction {
if transaction.signing != signing {
trace!("Identity {:?} is not ours anymore, skipping", &identity);
continue;
@ -730,7 +730,7 @@ impl Chain {
domain = String::from("unknown");
}
// TODO optimize
match self.get_domain_renewal_time(&identity) {
match self.get_domain_renewal_time(timestamp, &identity) {
None => result.insert(identity, (domain, timestamp, data)),
Some(t) => result.insert(identity, (domain, t, data))
};
@ -855,18 +855,13 @@ impl Chain {
return Bad;
}
// If this domain is not available to this public key
if !self.is_id_available(block.index - 1, &transaction.identity, &block.pub_key) {
if !self.is_id_available(block.index - 1, timestamp, &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_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;
}
}
if self.can_mine_identity(&transaction.identity, block.index, block.timestamp, &block.pub_key) != Fine {
warn!("Block {:?} is mined too early!", &block);
return Bad;
}
// Check if yggdrasil only property of zone is not violated
if let Some(block_data) = transaction.get_domain_data() {
@ -1015,7 +1010,7 @@ impl Chain {
}
// Weeks since this domain was changed + 1, but not more than 7 weeks.
// So max discount will be 8 bits of difficulty.
(min((Utc::now().timestamp() - timestamp) / ONE_WEEK, 7i64) + 1) as u32
(min((time - timestamp) / ONE_WEEK, 7i64) + 1) as u32
}
}
}

@ -126,6 +126,18 @@ impl DomainData {
}
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub enum DomainState {
// Not in blockchain, free to mine
NotFound,
// Active, not expired domain
Alive { renewed_time: i64, until: i64 },
// Expired, but can be renewed only by owner
Expired { renewed_time: i64, until: i64 },
// Expired and can be recaptured by anyone
Free { renewed_time: i64 }
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct Origin {
zones: Bytes

@ -13,7 +13,7 @@ pub enum BlockQuality {
Fork
}
#[derive(Debug)]
#[derive(Debug, PartialEq)]
pub enum MineResult {
Fine,
WrongName,

@ -28,6 +28,8 @@ pub const BLOCK_SIGNERS_START_RANDOM: i64 = 90;
pub const NEW_DOMAINS_INTERVAL: i64 = 86400; // One day in seconds
pub const ONE_WEEK: i64 = 86400 * 7; // One week in seconds
pub const DOMAIN_LIFETIME: i64 = 86400 * 365; // One year
/// Time for the owner to remine his domain and not to loose it
pub const DOMAIN_RENEW_TIME: i64 = 86400 * 30; // One month
pub const MAX_RECORDS: usize = 30;
pub const MAX_DATA_LEN: usize = 255;
@ -49,4 +51,4 @@ pub const MIN_CONNECTED_NODES_START_SYNC: usize = 4;
pub const MAX_READ_BLOCK_TIME: u128 = 100;
pub const MAX_RECONNECTS: u32 = 5;
pub const MAX_IDLE_SECONDS: u64 = 180;
pub const MAX_NODES: usize = 20;
pub const MAX_NODES: usize = 15;

@ -7,6 +7,8 @@ use std::marker::{Send, Sync};
use std::net::{SocketAddr, TcpStream, ToSocketAddrs, UdpSocket};
#[cfg(feature = "doh")]
use std::net::IpAddr;
#[cfg(feature = "doh")]
use std::num::NonZeroUsize;
use std::sync::atomic::{AtomicUsize, Ordering, AtomicBool};
use std::sync::mpsc::{channel, Sender};
use std::sync::{Arc, Mutex};
@ -400,13 +402,13 @@ impl HttpsDnsClient {
.collect::<Vec<SocketAddr>>();
trace!("Using bootstraps: {:?}", &servers);
let cache: LruCache<String, Vec<SocketAddr>> = LruCache::new(10);
let cache: LruCache<String, Vec<SocketAddr>> = LruCache::new(NonZeroUsize::new(10).unwrap());
let cache = RwLock::new(cache);
let agent = ureq::AgentBuilder::new()
.user_agent(&client_name)
.timeout(std::time::Duration::from_secs(5))
.max_idle_connections_per_host(2)
.timeout(std::time::Duration::from_secs(3))
.max_idle_connections_per_host(4)
.max_idle_connections(16)
.resolver(move |addr: &str| {
let addr = match addr.find(':') {

@ -1,11 +1,11 @@
//! UDP and TCP server implementations for DNS
use std::collections::VecDeque;
use std::io::Write;
use std::net::{Shutdown, SocketAddr, TcpListener, TcpStream, UdpSocket};
use std::num::NonZeroUsize;
use std::sync::atomic::Ordering;
use std::sync::mpsc::{channel, Sender};
use std::sync::{Arc, Condvar, Mutex};
use std::sync::Arc;
use std::thread::Builder;
use derive_more::{Display, Error, From};
@ -161,14 +161,12 @@ pub fn execute_query(context: Arc<ServerContext>, request: &DnsPacket) -> DnsPac
/// a new thread is spawned to service the request asynchronously.
pub struct DnsUdpServer {
context: Arc<ServerContext>,
request_queue: Arc<Mutex<VecDeque<(SocketAddr, DnsPacket)>>>,
request_cond: Arc<Condvar>,
thread_count: usize
}
impl DnsUdpServer {
pub fn new(context: Arc<ServerContext>, thread_count: usize) -> DnsUdpServer {
DnsUdpServer { context, request_queue: Arc::new(Mutex::new(VecDeque::new())), request_cond: Arc::new(Condvar::new()), thread_count }
DnsUdpServer { context, thread_count }
}
}
@ -180,6 +178,8 @@ impl DnsServer for DnsUdpServer {
// Bind the socket
let socket = UdpSocket::bind(self.context.dns_listen.as_str())?;
let (mut sender, receiver) = spmc::channel::<(SocketAddr, DnsPacket)>();
// Spawn threads for handling requests
for thread_id in 0..self.thread_count {
let socket_clone = match socket.try_clone() {
@ -191,23 +191,16 @@ impl DnsServer for DnsUdpServer {
};
let context = Arc::clone(&self.context);
let request_cond = self.request_cond.clone();
let request_queue = self.request_queue.clone();
let receiver = receiver.clone();
let name = "DnsUdpServer-request-".to_string() + &thread_id.to_string();
let _ = Builder::new().name(name).spawn(move || {
loop {
// Acquire lock, and wait on the condition until data is
// available. Then proceed with popping an entry of the queue.
let (src, request) = match request_queue
.lock()
.ok()
.and_then(|x| request_cond.wait(x).ok())
.and_then(|mut x| x.pop_front())
{
Some(x) => x,
None => {
debug!("Not expected to happen!");
// Receive source address and packet from channel
let (src, request) = match receiver.recv() {
Ok((src, request)) => (src, request),
Err(e) => {
debug!("Not expected to happen! Error: {}", e);
continue;
}
};
@ -239,7 +232,7 @@ impl DnsServer for DnsUdpServer {
let _ = Builder::new()
.name("DnsUdpServer-incoming".into())
.spawn(move || {
let mut working_ids: LruCache<(SocketAddr, u16), i64> = LruCache::new(256);
let mut working_ids: LruCache<(SocketAddr, u16), i64> = LruCache::new(NonZeroUsize::new(256).unwrap());
loop {
let _ = self.context.statistics.udp_query_count.fetch_add(1, Ordering::Release);
@ -277,15 +270,9 @@ impl DnsServer for DnsUdpServer {
}
working_ids.put(key, cur_time);
// Acquire lock, add request to queue, and notify waiting threads using the condition.
match self.request_queue.lock() {
Ok(mut queue) => {
queue.push_back((src, request));
self.request_cond.notify_one();
}
Err(e) => {
debug!("Failed to send UDP request for processing: {}", e);
}
if let Err(e) = sender.send((src, request)) {
warn!("Error sending work to DNS resolver threads! Error: {}", e);
continue;
}
}
})?;

@ -64,6 +64,7 @@ impl Network {
let mut server = TcpListener::bind(addr).expect("Can't bind to address");
debug!("Started node listener on {}", server.local_addr().unwrap());
let mut buffer = vec![0u8; 65535];
let mut events = Events::with_capacity(64);
let mut poll = Poll::new().expect("Unable to create poll");
poll.registry().register(&mut server, SERVER, Interest::READABLE).expect("Error registering poll");
@ -146,7 +147,7 @@ impl Network {
}
}
token => {
if !self.handle_connection_event(poll.registry(), event, &mut seen_blocks) {
if !self.handle_connection_event(poll.registry(), event, &mut seen_blocks, &mut buffer) {
let _ = self.peers.close_peer(poll.registry(), &token);
let blocks = self.context.lock().unwrap().chain.get_height();
let keys = self.context.lock().unwrap().chain.get_users_count();
@ -202,7 +203,7 @@ impl Network {
log_timer = Instant::now();
seen_blocks.clear();
}
if nodes < MAX_NODES && connect_timer.elapsed().as_secs() >= 5 {
if nodes < MAX_NODES && connect_timer.elapsed().as_secs() >= 2 {
self.peers.connect_new_peers(poll.registry(), &mut self.token, yggdrasil_only);
connect_timer = Instant::now();
}
@ -221,24 +222,28 @@ impl Network {
}
}
fn handle_connection_event(&mut self, registry: &Registry, event: &Event, seen_blocks: &mut HashSet<Bytes>) -> bool {
fn handle_connection_event(&mut self, registry: &Registry, event: &Event, seen_blocks: &mut HashSet<Bytes>, buf: &mut [u8]) -> bool {
if event.is_error() || (event.is_read_closed() && event.is_write_closed()) {
return false;
}
if event.is_readable() {
return self.process_readable(registry, event, seen_blocks);
if !self.process_readable(registry, event, seen_blocks, buf) {
return false;
}
}
if event.is_writable() {
return self.process_writable(registry, event);
if !self.process_writable(registry, event) {
return false;
}
}
true
}
fn process_readable(&mut self, registry: &Registry, event: &Event, seen_blocks: &mut HashSet<Bytes>) -> bool {
let data = {
fn process_readable(&mut self, registry: &Registry, event: &Event, seen_blocks: &mut HashSet<Bytes>, buf: &mut [u8]) -> bool {
let data_size = {
let token = event.token();
match self.peers.get_mut_peer(&token) {
None => {
@ -304,18 +309,18 @@ impl Network {
}
_ => {
let stream = peer.get_stream();
read_message(stream)
read_message(stream, buf)
}
}
}
}
};
if let Ok(data) = data {
if let Ok(data_size) = data_size {
let data = {
match self.peers.get_peer(&event.token()) {
Some(peer) => {
match decode_message(&data, peer.get_cipher()) {
match decode_message(&buf[0..data_size], peer.get_cipher()) {
Ok(data) => data,
Err(_) => {
vec![]
@ -359,7 +364,7 @@ impl Network {
}
State::SendLoop => {
let stream = peer.get_stream();
registry.reregister(stream, event.token(), Interest::WRITABLE).unwrap();
registry.reregister(stream, event.token(), Interest::WRITABLE | Interest::READABLE).unwrap();
peer.set_state(State::SendLoop);
}
State::Twin => {
@ -376,7 +381,7 @@ impl Network {
}
}
} else {
let error = data.err().unwrap();
let error = data_size.err().unwrap();
let addr = match self.peers.get_peer(&event.token()) {
None => String::from("unknown"),
Some(peer) => peer.get_addr().to_string()
@ -413,6 +418,7 @@ impl Network {
encode_message(&message, peer.get_cipher()).unwrap()
};
send_message(peer.get_stream(), &data).unwrap_or_else(|e| warn!("Error sending hello {}", e));
peer.set_state(State::idle());
//debug!("Sent hello to {}", &peer.get_addr());
}
State::Connected => {}
@ -421,6 +427,7 @@ impl Network {
if let Ok(data) = encode_bytes(&data, peer.get_cipher()) {
send_message(peer.get_stream(), &data).unwrap_or_else(|e| warn!("Error sending message {}", e));
}
peer.set_state(State::idle());
}
State::Idle { from } => {
debug!("Odd version of pings :)");
@ -440,10 +447,12 @@ impl Network {
State::SendLoop => {
let data = encode_message(&Message::Loop, peer.get_cipher()).unwrap();
send_message(peer.get_stream(), &data).unwrap_or_else(|e| warn!("Error sending loop {}", e));
peer.set_state(State::idle());
}
State::Twin => {
let data = encode_message(&Message::Twin, peer.get_cipher()).unwrap();
send_message(peer.get_stream(), &data).unwrap_or_else(|e| warn!("Error sending loop {}", e));
peer.set_state(State::idle());
}
}
registry.reregister(peer.get_stream(), event.token(), Interest::READABLE).unwrap();
@ -460,7 +469,8 @@ impl Network {
let my_id = self.peers.get_my_id().to_owned();
let answer = match message {
Message::Hand { app_version, origin, version, public, rand_id } => {
if app_version.starts_with("0.6") {
if !version_compatible(&app_version) {
info!("Banning peer with version {}", &app_version);
return State::Banned;
}
if self.peers.is_our_own_connect(&rand_id) {
@ -494,7 +504,8 @@ impl Network {
if self.peers.is_tween_connect(&rand_id) {
return State::Twin;
}
if app_version.starts_with("0.6") {
if !version_compatible(&app_version) {
info!("Banning peer with version {}", &app_version);
return State::Banned;
}
let nodes = self.peers.get_peers_active_count();
@ -741,18 +752,17 @@ fn decode_message(data: &[u8], cipher: &Option<Chacha>) -> Result<Vec<u8>, chach
}
}
fn read_message(stream: &mut TcpStream) -> Result<Vec<u8>, Error> {
fn read_message(stream: &mut TcpStream, buf: &mut [u8]) -> Result<usize, Error> {
let instant = Instant::now();
let data_size = (stream.read_u16::<BigEndian>()? ^ 0xAAAA) as usize;
if data_size == 0 {
return Err(io::Error::from(ErrorKind::InvalidInput));
}
let mut buf = vec![0u8; data_size];
let mut bytes_read = 0;
let delay = Duration::from_millis(2);
loop {
match stream.read(&mut buf[bytes_read..]) {
match stream.read(&mut buf[bytes_read..data_size]) {
Ok(bytes) => {
bytes_read += bytes;
if bytes_read == data_size {
@ -767,7 +777,7 @@ fn read_message(stream: &mut TcpStream) -> Result<Vec<u8>, Error> {
thread::sleep(delay);
continue;
} else {
break;
return Err(io::Error::from(ErrorKind::WouldBlock));
}
},
Err(ref err) if interrupted(err) => continue,
@ -778,10 +788,10 @@ fn read_message(stream: &mut TcpStream) -> Result<Vec<u8>, Error> {
},
}
}
if buf.len() == data_size {
Ok(buf)
if bytes_read == data_size {
Ok(data_size)
} else {
Err(io::Error::from(ErrorKind::WouldBlock))
Err(io::Error::from(ErrorKind::BrokenPipe))
}
}
@ -914,4 +924,11 @@ fn would_block(err: &io::Error) -> bool {
fn interrupted(err: &io::Error) -> bool {
err.kind() == io::ErrorKind::Interrupted
}
fn version_compatible(version: &str) -> bool {
let my_version = env!("CARGO_PKG_VERSION");
let parts = my_version.split('.').collect::<Vec<&str>>();
let major = format!("{}.{}", parts[0], parts[1]);
version.starts_with(&major)
}

@ -1,11 +1,10 @@
use std::collections::HashMap;
use std::net::SocketAddr;
use std::time::Instant;
use mio::net::TcpStream;
use crate::crypto::Chacha;
use crate::p2p::State;
use crate::Block;
#[derive(Debug)]
pub struct Peer {
@ -17,11 +16,11 @@ pub struct Peer {
inbound: bool,
public: bool,
active: bool,
last_active: Instant,
reconnects: u32,
received_block: u64,
sent_height: u64,
cipher: Option<Chacha>,
fork: HashMap<u64, Block>
cipher: Option<Chacha>
}
impl Peer {
@ -35,11 +34,11 @@ impl Peer {
inbound,
public: false,
active: false,
last_active: Instant::now(),
reconnects: 0,
received_block: 0,
sent_height: 0,
cipher: None,
fork: HashMap::new()
cipher: None
}
}
@ -133,10 +132,13 @@ impl Peer {
pub fn set_active(&mut self, active: bool) {
self.active = active;
if active {
self.last_active = Instant::now();
}
}
pub fn active(&self) -> bool {
self.active
self.active && self.last_active.elapsed().as_secs() < 120
}
pub fn reconnects(&self) -> u32 {
@ -159,14 +161,6 @@ impl Peer {
self.inbound
}
pub fn add_fork_block(&mut self, block: Block) {
self.fork.insert(block.index, block);
}
pub fn get_fork(&self) -> &HashMap<u64, Block> {
&self.fork
}
/// If loopback address then we care about ip and port.
/// If regular address then we only care about the ip and ignore the port.
pub fn equals(&self, addr: &SocketAddr) -> bool {

@ -9,6 +9,7 @@ use chrono::Utc;
use log::{debug, error, info, trace, warn};
use mio::net::TcpStream;
use mio::{Interest, Registry, Token};
use rand::prelude::SliceRandom;
use rand::random;
use rand::seq::IteratorRandom;
@ -194,6 +195,10 @@ impl Peers {
for (_, peer) in self.peers.iter() {
if peer.active() {
count += 1;
} else {
if !matches!(peer.get_state(), State::Connecting) {
trace!("Inactive peer from {:?} in state: {:?}", peer.get_addr(), peer.get_state());
}
}
}
count
@ -249,6 +254,7 @@ impl Peers {
let nodes = self.get_peers_active_count();
let random_time = random::<u64>() % PING_PERIOD;
let mut stale_tokens = Vec::new();
for (token, peer) in self.peers.iter_mut() {
if let State::Idle { from } = peer.get_state() {
if from.elapsed().as_secs() >= PING_PERIOD + random_time {
@ -261,10 +267,23 @@ impl Peers {
peer.set_state(State::message(message));
let stream = peer.get_stream();
registry.reregister(stream, *token, Interest::WRITABLE | Interest::READABLE).unwrap();
}
} else {
if matches!(peer.get_state(), State::Message {..}) {
if !peer.active() {
stale_tokens.push((token.clone(), peer.get_addr()));
continue;
}
let stream = peer.get_stream();
registry.reregister(stream, *token, Interest::WRITABLE).unwrap();
}
}
}
for (token, addr) in &stale_tokens {
info!("Closing stale peer from {}", addr);
self.close_peer(registry, token);
}
// Just purging ignored/banned IPs every 10 minutes
// TODO make it individual for every IP
@ -336,7 +355,7 @@ impl Peers {
None => {}
Some((token, peer)) => {
debug!("Peer {} is higher than we are, requesting block {}", &peer.get_addr().ip(), height + 1);
registry.reregister(peer.get_stream(), *token, Interest::WRITABLE).unwrap();
registry.reregister(peer.get_stream(), *token, Interest::WRITABLE | Interest::READABLE).unwrap();
peer.set_state(State::message(Message::GetBlock { index: height + 1 }));
}
}
@ -344,10 +363,11 @@ impl Peers {
fn ask_blocks_from_peers(&mut self, registry: &Registry, height: u64, max_height: u64, have_blocks: HashSet<u64>) {
let mut rng = rand::thread_rng();
let peers = self.peers
let mut peers = self.peers
.iter_mut()
.filter_map(|(token, peer)| if peer.has_more_blocks(height) { Some((token, peer)) } else { None })
.choose_multiple(&mut rng, (max_height - height) as usize);
peers.shuffle(&mut rng);
let mut index = height + 1;
for (token, peer) in peers {
if have_blocks.contains(&index) {

@ -126,7 +126,10 @@ fn action_check_domain(context: &Arc<Mutex<Context>>, web_view: &mut WebView<()>
let c = context.lock().unwrap();
if let Some(keystore) = c.get_keystore() {
let name = name.to_lowercase();
let available = c.get_chain().is_domain_available(c.get_chain().get_height(), &name, &keystore.get_public());
let available = match c.chain.can_mine_domain(c.chain.get_height(), &name, &keystore.get_public()) {
MineResult::Fine => true,
_ => false
};
web_view.eval(&format!("domainAvailable({})", available)).expect("Error evaluating!");
}
}

@ -191,7 +191,8 @@ function editDomain(domain, event) {
recordsBuffer.push(v);
});
}
document.getElementById("new_domain").value = title.replace("." + domain_data.zone, "");
currentDomain = title.replace("." + domain_data.zone, "");
document.getElementById("new_domain").value = currentDomain;
if (typeof domain_data.info !== 'undefined') {
document.getElementById("info_text").value = domain_data.info;
}

Loading…
Cancel
Save