Decoupled domain records change from domain renewal.

This commit is contained in:
Revertron
2022-04-13 13:02:58 +02:00
parent 90c5ba7e83
commit 5b5943a4aa
8 changed files with 112 additions and 55 deletions
Generated
+7 -7
View File
@@ -84,7 +84,7 @@ dependencies = [
[[package]] [[package]]
name = "alfis" name = "alfis"
version = "0.6.12" version = "0.7.0"
dependencies = [ dependencies = [
"base64", "base64",
"bincode", "bincode",
@@ -719,9 +719,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.104" version = "0.2.123"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b2f96d100e1cf1929e7719b7edb3b90ab5298072638fccd77be9ce942ecdfce" checksum = "cb691a747a7ab48abc15c5b42066eaafde10dc427e3b6ee2a1cf43db04c763bd"
[[package]] [[package]]
name = "log" name = "log"
@@ -734,9 +734,9 @@ dependencies = [
[[package]] [[package]]
name = "lru" name = "lru"
version = "0.7.3" version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcb87f3080f6d1d69e8c564c0fcfde1d7aa8cc451ce40cae89479111f03bc0eb" checksum = "32613e41de4c47ab04970c348ca7ae7382cf116625755af070b008a15516a889"
dependencies = [ dependencies = [
"hashbrown", "hashbrown",
] ]
@@ -1291,9 +1291,9 @@ dependencies = [
[[package]] [[package]]
name = "thread-priority" name = "thread-priority"
version = "0.8.0" version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b3fe6ec8f0ac600de217a06f30b95f9aee46f93158fe7df97797eeee70f1f65" checksum = "696668a68983ad737e08e11e9afb701e962cab9f07f2a4ff339316b2d5b0870d"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
+3 -3
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "alfis" name = "alfis"
version = "0.6.12" version = "0.7.0"
authors = ["Revertron <alfis@revertron.com>"] authors = ["Revertron <alfis@revertron.com>"]
edition = "2021" edition = "2021"
build = "build.rs" build = "build.rs"
@@ -39,7 +39,7 @@ sqlite = "0.26.0"
uuid = { version = "0.8.2", features = ["serde", "v4"] } uuid = { version = "0.8.2", features = ["serde", "v4"] }
mio = { version = "0.8.2", features = ["os-poll", "net"] } mio = { version = "0.8.2", features = ["os-poll", "net"] }
ureq = { version = "2.4", optional = true } ureq = { version = "2.4", optional = true }
lru = "0.7.3" lru = "0.7.5"
derive_more = "0.99.17" derive_more = "0.99.17"
lazy_static = "1.4.0" lazy_static = "1.4.0"
@@ -50,7 +50,7 @@ open = { version = "2.1.1", optional = true }
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
winapi = { version = "0.3.9", features = ["impl-default", "wincon", "shellscalingapi"] } winapi = { version = "0.3.9", features = ["impl-default", "wincon", "shellscalingapi"] }
thread-priority = "0.8.0" thread-priority = "0.8.2"
[target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))'.dependencies] [target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))'.dependencies]
thread-priority = "0.8.2" thread-priority = "0.8.2"
+68 -24
View File
@@ -1,5 +1,5 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::cmp::max; use std::cmp::{max, min};
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::fs; use std::fs;
use std::ops::Deref; use std::ops::Deref;
@@ -37,10 +37,11 @@ const SQL_GET_LAST_FULL_BLOCK: &str = "SELECT * FROM blocks WHERE id < ? AND `tr
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_OWNER_BY_ID: &str = "SELECT signing FROM domains WHERE id < ? AND identity = ? ORDER BY id DESC LIMIT 1;"; const SQL_GET_DOMAIN_OWNER_BY_ID: &str = "SELECT signing FROM domains WHERE id < ? AND 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_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 signing = ? ORDER BY id;"; const SQL_GET_DOMAINS_BY_KEY: &str = "SELECT timestamp, identity, data, signing FROM domains WHERE signing = ? ORDER BY id;";
const SQL_GET_DOMAINS_COUNT: &str = "SELECT count(DISTINCT identity) FROM domains;"; const SQL_GET_DOMAINS_COUNT: &str = "SELECT count(DISTINCT identity) FROM domains;";
const SQL_GET_USERS_COUNT: &str = "SELECT count(DISTINCT pub_key) FROM blocks;"; const SQL_GET_USERS_COUNT: &str = "SELECT count(DISTINCT pub_key) FROM blocks;";
const SQL_GET_USER_BLOCK_COUNT: &str = "SELECT count(pub_key) FROM blocks WHERE pub_key = ? AND id < ?"; const SQL_GET_USER_BLOCK_COUNT: &str = "SELECT count(pub_key) FROM blocks WHERE pub_key = ? AND id < ?";
const SQL_GET_DOMAIN_UPDATE_TIME: &str = "SELECT domains.timestamp FROM blocks JOIN domains ON blocks.id = domains.id WHERE difficulty >= 23 AND identity = ? ORDER BY domains.id DESC LIMIT 1;";
const SQL_GET_OPTIONS: &str = "SELECT * FROM options;"; const SQL_GET_OPTIONS: &str = "SELECT * FROM options;";
@@ -558,7 +559,7 @@ impl Chain {
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");
if let State::Row = statement.next().unwrap() { if let State::Row = statement.next().unwrap() {
// If there is such a zone // If there is such an ID
return true; return true;
} }
false false
@@ -591,7 +592,40 @@ impl Chain {
Fine Fine
} }
pub fn get_id_transaction(&self, identity_hash: &Bytes) -> Option<Transaction> { pub fn get_domain_renewal_time(&self, 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 {
// This domain is too old
return None;
}
return Some(timestamp);
}
None
}
pub fn get_domain_update_time(&self, identity_hash: &Bytes) -> Option<i64> {
let mut statement = self.db.prepare(SQL_GET_DOMAIN_BY_ID).unwrap();
statement.bind(1, identity_hash.as_slice()).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;
}
return Some(timestamp);
}
None
}
pub fn get_domain_transaction_by_id(&self, identity_hash: &Bytes) -> Option<Transaction> {
if self.get_domain_renewal_time(identity_hash).is_none() {
// Domain has expired
return None;
}
let mut statement = self.db.prepare(SQL_GET_DOMAIN_BY_ID).unwrap(); 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(1, identity_hash.as_slice()).expect("Error in bind");
if let State::Row = statement.next().unwrap() { if let State::Row = statement.next().unwrap() {
@@ -618,7 +652,7 @@ impl Chain {
return None; return None;
} }
let identity_hash = hash_identity(domain, None); let identity_hash = hash_identity(domain, None);
if let Some(transaction) = self.get_id_transaction(&identity_hash) { if let Some(transaction) = self.get_domain_transaction_by_id(&identity_hash) {
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);
@@ -671,33 +705,31 @@ impl Chain {
let mut statement = self.db.prepare(SQL_GET_DOMAINS_BY_KEY).unwrap(); let mut statement = self.db.prepare(SQL_GET_DOMAINS_BY_KEY).unwrap();
statement.bind(1, &**pub_key).expect("Error in bind"); statement.bind(1, &**pub_key).expect("Error in bind");
while let State::Row = statement.next().unwrap() { while let State::Row = statement.next().unwrap() {
let _index = statement.read::<i64>(0).unwrap() as u64; let timestamp = statement.read::<i64>(0).unwrap();
let timestamp = statement.read::<i64>(1).unwrap(); let identity = Bytes::from_bytes(&statement.read::<Vec<u8>>(1).unwrap());
let identity = Bytes::from_bytes(&statement.read::<Vec<u8>>(2).unwrap()); let data = statement.read::<String>(2).unwrap();
let confirmation = Bytes::from_bytes(&statement.read::<Vec<u8>>(3).unwrap()); let signing = Bytes::from_bytes(&statement.read::<Vec<u8>>(3).unwrap());
let class = String::from(CLASS_DOMAIN);
let data = statement.read::<String>(4).unwrap();
let signing = Bytes::from_bytes(&statement.read::<Vec<u8>>(5).unwrap());
let encryption = Bytes::from_bytes(&statement.read::<Vec<u8>>(6).unwrap());
// Get the last transaction for this id and check if it is still ours // Get the last transaction for this id and check if it is still ours
if let Some(transaction) = self.get_id_transaction(&identity) { if let Some(transaction) = self.get_domain_transaction_by_id(&identity) {
if transaction.signing != signing { if transaction.signing != signing {
trace!("Identity {:?} is not ours anymore, skipping", &identity); trace!("Identity {:?} is not ours anymore, skipping", &identity);
continue; continue;
} }
} }
let transaction = Transaction { identity: identity.clone(), confirmation: confirmation.clone(), class, data, signing, encryption };
//trace!("Found transaction for domain {:?}", &transaction); //trace!("Found transaction for domain {:?}", &transaction);
if let Some(data) = transaction.get_domain_data() { if let Ok(data) = serde_json::from_str::<DomainData>(&data) {
let decrypted = keystore.decrypt(data.encrypted.as_slice()); let decrypted = keystore.decrypt(data.encrypted.as_slice());
let mut domain = String::from_utf8(decrypted.to_vec()).unwrap(); let mut domain = String::from_utf8(decrypted.to_vec()).unwrap();
if domain.is_empty() { if domain.is_empty() {
domain = String::from("unknown"); domain = String::from("unknown");
} }
trace!("Found my domain {}", domain); // TODO optimize
result.insert(identity, (domain, timestamp, data)); match self.get_domain_renewal_time(&identity) {
None => result.insert(identity, (domain, timestamp, data)),
Some(t) => result.insert(identity, (domain, t, data))
};
} }
} }
result result
@@ -786,7 +818,7 @@ impl Chain {
SIGNER_DIFFICULTY SIGNER_DIFFICULTY
} }
} }
Some(t) => self.get_difficulty_for_transaction(t, block.index) Some(t) => self.get_difficulty_for_transaction(t)
}; };
if block.difficulty < difficulty { if block.difficulty < difficulty {
warn!("Block difficulty is lower than needed"); warn!("Block difficulty is lower than needed");
@@ -955,14 +987,12 @@ impl Chain {
true true
} }
fn get_difficulty_for_transaction(&self, transaction: &Transaction, index: u64) -> u32 { fn get_difficulty_for_transaction(&self, transaction: &Transaction) -> u32 {
match transaction.class.as_ref() { match transaction.class.as_ref() {
CLASS_DOMAIN => { CLASS_DOMAIN => {
// If this domain is already in blockchain we approve slightly smaller difficulty // If this domain is already in blockchain we approve slightly smaller difficulty
let discount = match self.is_domain_in_blockchain(index, &transaction.identity) { let discount = self.get_identity_discount(&transaction.identity, false);
true => { 1 } // TODO move this check somewhere appropriate
false => { 0 }
};
return match serde_json::from_str::<DomainData>(&transaction.data) { return match serde_json::from_str::<DomainData>(&transaction.data) {
Ok(_) => DOMAIN_DIFFICULTY - discount, Ok(_) => DOMAIN_DIFFICULTY - discount,
Err(_) => { Err(_) => {
@@ -976,6 +1006,20 @@ impl Chain {
} }
} }
pub fn get_identity_discount(&self, identity: &Bytes, renewal: bool) -> u32 {
match self.get_domain_update_time(identity) {
None => 0u32,
Some(timestamp) => {
if renewal || self.get_height() < BLOCKS_WITHOUT_DISCOUNT {
return 1;
}
// 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
}
}
}
/// Gets public keys of a node that needs to mine "signature" block above this block /// Gets public keys of a node that needs to mine "signature" block above this block
/// block - last full block /// block - last full block
pub fn get_block_signers(&self, block: &Block) -> Vec<Bytes> { pub fn get_block_signers(&self, block: &Block) -> Vec<Bytes> {
+2
View File
@@ -7,6 +7,7 @@ pub const ORIGIN_DIFFICULTY: u32 = 28;
pub const DOMAIN_DIFFICULTY: u32 = 24; 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;
pub const BLOCKS_WITHOUT_DISCOUNT: u64 = 4999;
/// Blocks start to be signed starting from this index /// Blocks start to be signed starting from this index
pub const BLOCK_SIGNERS_START: u64 = 35; pub const BLOCK_SIGNERS_START: u64 = 35;
@@ -25,6 +26,7 @@ pub const LIMITED_CONFIDENCE_DEPTH: u64 = 4;
pub const BLOCK_SIGNERS_START_RANDOM: i64 = 90; pub const BLOCK_SIGNERS_START_RANDOM: i64 = 90;
pub const NEW_DOMAINS_INTERVAL: i64 = 86400; // One day in seconds 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 pub const DOMAIN_LIFETIME: i64 = 86400 * 365; // One year
pub const MAX_RECORDS: usize = 30; pub const MAX_RECORDS: usize = 30;
pub const MAX_DATA_LEN: usize = 255; pub const MAX_DATA_LEN: usize = 255;
+15 -6
View File
@@ -451,20 +451,29 @@ impl Network {
if self.peers.is_our_own_connect(&rand_id) { if self.peers.is_our_own_connect(&rand_id) {
warn!("Detected loop connect"); warn!("Detected loop connect");
State::SendLoop State::SendLoop
} else if origin.eq(my_origin) && version == my_version { } else if origin.eq(my_origin) {
let peer = self.peers.get_mut_peer(token).unwrap(); let peer = self.peers.get_mut_peer(token).unwrap();
peer.set_public(public);
peer.set_active(true);
debug!("Incoming v{} on {}", &app_version, peer.get_addr().ip()); debug!("Incoming v{} on {}", &app_version, peer.get_addr().ip());
let app_version = self.context.lock().unwrap().app_version.clone(); let app_version = self.context.lock().unwrap().app_version.clone();
State::message(Message::shake(&app_version, &origin, version, me_public, &my_id, my_height)) if version == my_version {
peer.set_public(public);
peer.set_active(true);
} else {
warn!("Handshake from unsupported version: {} (local version: {})", version, my_version);
}
State::message(Message::shake(&app_version, &origin, my_version, me_public, &my_id, my_height))
} else { } else {
warn!("Handshake from unsupported chain or version: {}, {}", &origin, version); warn!("Handshake from unsupported chain: {}", &origin);
State::Banned State::Banned
} }
} }
Message::Shake { app_version, origin, version, public, rand_id, height } => { Message::Shake { app_version, origin, version, public, rand_id, height } => {
if origin.ne(my_origin) || version != my_version { if origin.ne(my_origin) {
return State::Banned;
} else if version > my_version {
warn!("Can't work with newer blockchain version {} and ALFIS version {}, please upgrade!", version, &app_version);
return State::Banned;
} else if version != my_version {
return State::Banned; return State::Banned;
} }
if self.peers.is_tween_connect(&rand_id) { if self.peers.is_tween_connect(&rand_id) {
+7 -13
View File
@@ -53,8 +53,8 @@ pub fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
SelectKey { index } => { action_select_key(&context, web_view, index); } SelectKey { index } => { action_select_key(&context, web_view, index); }
CheckRecord { data } => { action_check_record(web_view, data); } CheckRecord { data } => { action_check_record(web_view, data); }
CheckDomain { name } => { action_check_domain(&context, web_view, name); } CheckDomain { name } => { action_check_domain(&context, web_view, name); }
MineDomain { name, data, signing, encryption } => { MineDomain { name, data, signing, encryption, renewal } => {
action_create_domain(Arc::clone(&context), Arc::clone(&miner), web_view, name, data, signing, encryption); action_create_domain(Arc::clone(&context), Arc::clone(&miner), web_view, name, data, signing, encryption, renewal);
} }
TransferDomain { .. } => {} TransferDomain { .. } => {}
StopMining => { post(Event::ActionStopMining); } StopMining => { post(Event::ActionStopMining); }
@@ -390,7 +390,7 @@ fn send_keys_to_ui(context: &MutexGuard<Context>, handle: &Handle<()>) {
} }
} }
fn action_create_domain(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>, web_view: &mut WebView<()>, name: String, data: String, signing: String, encryption: String) { fn action_create_domain(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>, web_view: &mut WebView<()>, name: String, data: String, signing: String, encryption: String, renewal: bool) {
debug!("Creating domain with data: {}", &data); debug!("Creating domain with data: {}", &data);
let c = Arc::clone(&context); let c = Arc::clone(&context);
let context = context.lock().unwrap(); let context = context.lock().unwrap();
@@ -443,7 +443,7 @@ 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 => {
std::mem::drop(context); std::mem::drop(context);
create_domain(c, miner, CLASS_DOMAIN, &name, data, DOMAIN_DIFFICULTY, &keystore, signing, encryption); create_domain(c, miner, CLASS_DOMAIN, &name, data, DOMAIN_DIFFICULTY, &keystore, signing, encryption, renewal);
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));
} }
@@ -561,7 +561,7 @@ fn format_event_now(kind: &str, message: &str) -> String {
} }
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
fn create_domain(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>, class: &str, name: &str, mut data: DomainData, difficulty: u32, keystore: &Keystore, signing: Bytes, encryption: Bytes) { fn create_domain(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>, class: &str, name: &str, mut data: DomainData, difficulty: u32, keystore: &Keystore, signing: Bytes, encryption: Bytes, renewal: bool) {
let name = name.to_owned(); let name = name.to_owned();
let encrypted = CryptoBox::encrypt(encryption.as_slice(), name.as_bytes()).expect("Error encrypting domain name!"); let encrypted = CryptoBox::encrypt(encryption.as_slice(), name.as_bytes()).expect("Error encrypting domain name!");
data.encrypted = Bytes::from_bytes(&encrypted); data.encrypted = Bytes::from_bytes(&encrypted);
@@ -574,13 +574,7 @@ fn create_domain(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>, class:
}; };
let transaction = Transaction::from_str(name, class.to_owned(), data, signing, encryption); let transaction = Transaction::from_str(name, class.to_owned(), data, signing, encryption);
// If this domain is already in blockchain we approve slightly smaller difficulty // If this domain is already in blockchain we approve slightly smaller difficulty
let discount = { let discount = context.lock().unwrap().chain.get_identity_discount(&transaction.identity, renewal);
let context = context.lock().unwrap();
match context.chain.is_domain_in_blockchain(context.chain.get_height(), &transaction.identity) {
true => { 1 }
false => { 0 }
}
};
let block = Block::new(Some(transaction), keystore.get_public(), Bytes::default(), difficulty - discount); let block = Block::new(Some(transaction), keystore.get_public(), Bytes::default(), difficulty - discount);
miner.lock().unwrap().add_block(block, keystore.clone()); miner.lock().unwrap().add_block(block, keystore.clone());
} }
@@ -595,7 +589,7 @@ pub enum Cmd {
SelectKey { index: usize }, SelectKey { index: usize },
CheckRecord { data: String }, CheckRecord { data: String },
CheckDomain { name: String }, CheckDomain { name: String },
MineDomain { name: String, data: String, signing: String, encryption: String }, MineDomain { name: String, data: String, signing: String, encryption: String, renewal: bool },
TransferDomain { name: String, owner: String }, TransferDomain { name: String, owner: String },
StopMining, StopMining,
Open { link: String } Open { link: String }
+8 -1
View File
@@ -204,7 +204,7 @@
<div class="modal-content"> <div class="modal-content">
<div class="box" id="new_domain_dialog_box"> <div class="box" id="new_domain_dialog_box">
<button class="delete" aria-label="close" onclick="closeDialog('new_domain_dialog')"></button> <button class="delete" aria-label="close" onclick="closeDialog('new_domain_dialog')"></button>
<label class="label">New domain:</label> <label class="label">Domain name:</label>
<div class="field is-grouped is-fullwidth"> <div class="field is-grouped is-fullwidth">
<div class="control field has-addons is-expanded"> <div class="control field has-addons is-expanded">
<div class="control is-expanded has-icons-left"> <div class="control is-expanded has-icons-left">
@@ -265,6 +265,13 @@
<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 -->
</div> </div>
<div class="field mt-3">
<label class="control">
<input type="checkbox" id="renewal"> Renew domain by using higher difficulty while mining (works after block number 4999, until then it does nothing).
</label>
<p class="help">Starting from block 5000 the change of DNS records is decoupled from domain renewal.</p>
</div>
</div> </div>
</div> </div>
</div> </div>
+2 -1
View File
@@ -302,8 +302,9 @@ function createDomain() {
data.info = document.getElementById("info_text").value; data.info = document.getElementById("info_text").value;
data.records = recordsBuffer; data.records = recordsBuffer;
data.contacts = getContacts(); data.contacts = getContacts();
var renewal = document.getElementById("renewal").checked;
data = JSON.stringify(data); data = JSON.stringify(data);
external.invoke(JSON.stringify({cmd: 'mineDomain', name: domain, data: data, signing: ownerSigning, encryption: ownerEncryption})); external.invoke(JSON.stringify({cmd: 'mineDomain', name: domain, data: data, signing: ownerSigning, encryption: ownerEncryption, renewal: renewal}));
} }
function getContacts() { function getContacts() {