mirror of
https://github.com/Revertron/Alfis
synced 2024-11-15 06:12:52 +00:00
Decoupled domain records change from domain renewal.
This commit is contained in:
parent
90c5ba7e83
commit
5b5943a4aa
14
Cargo.lock
generated
14
Cargo.lock
generated
@ -84,7 +84,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "alfis"
|
||||
version = "0.6.12"
|
||||
version = "0.7.0"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bincode",
|
||||
@ -719,9 +719,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.104"
|
||||
version = "0.2.123"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b2f96d100e1cf1929e7719b7edb3b90ab5298072638fccd77be9ce942ecdfce"
|
||||
checksum = "cb691a747a7ab48abc15c5b42066eaafde10dc427e3b6ee2a1cf43db04c763bd"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
@ -734,9 +734,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lru"
|
||||
version = "0.7.3"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcb87f3080f6d1d69e8c564c0fcfde1d7aa8cc451ce40cae89479111f03bc0eb"
|
||||
checksum = "32613e41de4c47ab04970c348ca7ae7382cf116625755af070b008a15516a889"
|
||||
dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
@ -1291,9 +1291,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thread-priority"
|
||||
version = "0.8.0"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b3fe6ec8f0ac600de217a06f30b95f9aee46f93158fe7df97797eeee70f1f65"
|
||||
checksum = "696668a68983ad737e08e11e9afb701e962cab9f07f2a4ff339316b2d5b0870d"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "alfis"
|
||||
version = "0.6.12"
|
||||
version = "0.7.0"
|
||||
authors = ["Revertron <alfis@revertron.com>"]
|
||||
edition = "2021"
|
||||
build = "build.rs"
|
||||
@ -39,7 +39,7 @@ sqlite = "0.26.0"
|
||||
uuid = { version = "0.8.2", features = ["serde", "v4"] }
|
||||
mio = { version = "0.8.2", features = ["os-poll", "net"] }
|
||||
ureq = { version = "2.4", optional = true }
|
||||
lru = "0.7.3"
|
||||
lru = "0.7.5"
|
||||
derive_more = "0.99.17"
|
||||
lazy_static = "1.4.0"
|
||||
|
||||
@ -50,7 +50,7 @@ open = { version = "2.1.1", optional = true }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
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]
|
||||
thread-priority = "0.8.2"
|
||||
|
@ -1,5 +1,5 @@
|
||||
use std::cell::RefCell;
|
||||
use std::cmp::max;
|
||||
use std::cmp::{max, min};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fs;
|
||||
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_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_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_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_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;";
|
||||
|
||||
@ -558,7 +559,7 @@ impl Chain {
|
||||
statement.bind(1, height as i64).expect("Error in bind");
|
||||
statement.bind(2, &***id).expect("Error in bind");
|
||||
if let State::Row = statement.next().unwrap() {
|
||||
// If there is such a zone
|
||||
// If there is such an ID
|
||||
return true;
|
||||
}
|
||||
false
|
||||
@ -591,7 +592,40 @@ impl Chain {
|
||||
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();
|
||||
statement.bind(1, identity_hash.as_slice()).expect("Error in bind");
|
||||
if let State::Row = statement.next().unwrap() {
|
||||
@ -618,7 +652,7 @@ impl Chain {
|
||||
return 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);
|
||||
if transaction.check_identity(domain) {
|
||||
return Some(transaction);
|
||||
@ -671,33 +705,31 @@ impl Chain {
|
||||
let mut statement = self.db.prepare(SQL_GET_DOMAINS_BY_KEY).unwrap();
|
||||
statement.bind(1, &**pub_key).expect("Error in bind");
|
||||
while let State::Row = statement.next().unwrap() {
|
||||
let _index = statement.read::<i64>(0).unwrap() as u64;
|
||||
let timestamp = statement.read::<i64>(1).unwrap();
|
||||
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);
|
||||
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());
|
||||
let timestamp = statement.read::<i64>(0).unwrap();
|
||||
let identity = Bytes::from_bytes(&statement.read::<Vec<u8>>(1).unwrap());
|
||||
let data = statement.read::<String>(2).unwrap();
|
||||
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_id_transaction(&identity) {
|
||||
if let Some(transaction) = self.get_domain_transaction_by_id(&identity) {
|
||||
if transaction.signing != signing {
|
||||
trace!("Identity {:?} is not ours anymore, skipping", &identity);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let transaction = Transaction { identity: identity.clone(), confirmation: confirmation.clone(), class, data, signing, encryption };
|
||||
//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 mut domain = String::from_utf8(decrypted.to_vec()).unwrap();
|
||||
if domain.is_empty() {
|
||||
domain = String::from("unknown");
|
||||
}
|
||||
trace!("Found my domain {}", domain);
|
||||
result.insert(identity, (domain, timestamp, data));
|
||||
// TODO optimize
|
||||
match self.get_domain_renewal_time(&identity) {
|
||||
None => result.insert(identity, (domain, timestamp, data)),
|
||||
Some(t) => result.insert(identity, (domain, t, data))
|
||||
};
|
||||
}
|
||||
}
|
||||
result
|
||||
@ -786,7 +818,7 @@ impl Chain {
|
||||
SIGNER_DIFFICULTY
|
||||
}
|
||||
}
|
||||
Some(t) => self.get_difficulty_for_transaction(t, block.index)
|
||||
Some(t) => self.get_difficulty_for_transaction(t)
|
||||
};
|
||||
if block.difficulty < difficulty {
|
||||
warn!("Block difficulty is lower than needed");
|
||||
@ -955,14 +987,12 @@ impl Chain {
|
||||
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() {
|
||||
CLASS_DOMAIN => {
|
||||
// If this domain is already in blockchain we approve slightly smaller difficulty
|
||||
let discount = match self.is_domain_in_blockchain(index, &transaction.identity) {
|
||||
true => { 1 }
|
||||
false => { 0 }
|
||||
};
|
||||
let discount = self.get_identity_discount(&transaction.identity, false);
|
||||
// TODO move this check somewhere appropriate
|
||||
return match serde_json::from_str::<DomainData>(&transaction.data) {
|
||||
Ok(_) => DOMAIN_DIFFICULTY - discount,
|
||||
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
|
||||
/// block - last full block
|
||||
pub fn get_block_signers(&self, block: &Block) -> Vec<Bytes> {
|
||||
|
@ -7,6 +7,7 @@ pub const ORIGIN_DIFFICULTY: u32 = 28;
|
||||
pub const DOMAIN_DIFFICULTY: u32 = 24;
|
||||
pub const SIGNER_DIFFICULTY: u32 = 16;
|
||||
pub const KEYSTORE_DIFFICULTY: u32 = 23;
|
||||
pub const BLOCKS_WITHOUT_DISCOUNT: u64 = 4999;
|
||||
|
||||
/// Blocks start to be signed starting from this index
|
||||
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 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 MAX_RECORDS: usize = 30;
|
||||
pub const MAX_DATA_LEN: usize = 255;
|
||||
|
@ -451,20 +451,29 @@ impl Network {
|
||||
if self.peers.is_our_own_connect(&rand_id) {
|
||||
warn!("Detected loop connect");
|
||||
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();
|
||||
peer.set_public(public);
|
||||
peer.set_active(true);
|
||||
debug!("Incoming v{} on {}", &app_version, peer.get_addr().ip());
|
||||
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 {
|
||||
warn!("Handshake from unsupported chain or version: {}, {}", &origin, version);
|
||||
warn!("Handshake from unsupported chain: {}", &origin);
|
||||
State::Banned
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
if self.peers.is_tween_connect(&rand_id) {
|
||||
|
@ -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); }
|
||||
CheckRecord { data } => { action_check_record(web_view, data); }
|
||||
CheckDomain { name } => { action_check_domain(&context, web_view, name); }
|
||||
MineDomain { name, data, signing, encryption } => {
|
||||
action_create_domain(Arc::clone(&context), Arc::clone(&miner), web_view, 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, renewal);
|
||||
}
|
||||
TransferDomain { .. } => {}
|
||||
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);
|
||||
let c = Arc::clone(&context);
|
||||
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) {
|
||||
MineResult::Fine => {
|
||||
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();");
|
||||
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)]
|
||||
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 encrypted = CryptoBox::encrypt(encryption.as_slice(), name.as_bytes()).expect("Error encrypting domain name!");
|
||||
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);
|
||||
// If this domain is already in blockchain we approve slightly smaller difficulty
|
||||
let discount = {
|
||||
let context = context.lock().unwrap();
|
||||
match context.chain.is_domain_in_blockchain(context.chain.get_height(), &transaction.identity) {
|
||||
true => { 1 }
|
||||
false => { 0 }
|
||||
}
|
||||
};
|
||||
let discount = context.lock().unwrap().chain.get_identity_discount(&transaction.identity, renewal);
|
||||
let block = Block::new(Some(transaction), keystore.get_public(), Bytes::default(), difficulty - discount);
|
||||
miner.lock().unwrap().add_block(block, keystore.clone());
|
||||
}
|
||||
@ -595,7 +589,7 @@ pub enum Cmd {
|
||||
SelectKey { index: usize },
|
||||
CheckRecord { data: 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 },
|
||||
StopMining,
|
||||
Open { link: String }
|
||||
|
@ -204,7 +204,7 @@
|
||||
<div class="modal-content">
|
||||
<div class="box" id="new_domain_dialog_box">
|
||||
<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="control field has-addons is-expanded">
|
||||
<div class="control is-expanded has-icons-left">
|
||||
@ -265,6 +265,13 @@
|
||||
<div class="list mt-2" id="domain_records">
|
||||
<!-- Here will be our domain records, added by dialog -->
|
||||
</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>
|
||||
|
@ -302,8 +302,9 @@ function createDomain() {
|
||||
data.info = document.getElementById("info_text").value;
|
||||
data.records = recordsBuffer;
|
||||
data.contacts = getContacts();
|
||||
var renewal = document.getElementById("renewal").checked;
|
||||
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() {
|
||||
|
Loading…
Reference in New Issue
Block a user