Added a possibility to set owner for mined domain.

Fixed small errors.
This commit is contained in:
Revertron
2021-05-03 12:33:22 +02:00
parent fab62bdfc8
commit 52695e0988
5 changed files with 149 additions and 82 deletions
+15 -18
View File
@@ -229,13 +229,17 @@ impl Chain {
debug!("Adding block:\n{:?}", &block); debug!("Adding block:\n{:?}", &block);
let index = block.index; let index = block.index;
let timestamp = block.timestamp; let timestamp = block.timestamp;
let owner = block.pub_key.clone();
self.last_block = Some(block.clone()); self.last_block = Some(block.clone());
if block.transaction.is_some() { if block.transaction.is_some() {
self.last_full_block = Some(block.clone()); self.last_full_block = Some(block.clone());
} }
let transaction = block.transaction.clone(); let transaction = block.transaction.clone();
if self.add_block_to_table(block).is_ok() { if self.add_block_to_table(block).is_ok() {
if let Some(transaction) = transaction { if let Some(mut transaction) = transaction {
if transaction.owner.is_empty() {
transaction.owner = owner;
}
self.add_transaction_to_table(index, timestamp, &transaction).expect("Error adding transaction"); self.add_transaction_to_table(index, timestamp, &transaction).expect("Error adding transaction");
} }
} }
@@ -321,6 +325,9 @@ impl Chain {
} }
pub fn is_waiting_signers(&self) -> bool { pub fn is_waiting_signers(&self) -> bool {
if self.get_height() < BLOCK_SIGNERS_START {
return false;
}
if let Some(full_block) = &self.last_full_block { if let Some(full_block) = &self.last_full_block {
let sign_count = self.get_height() - full_block.index; let sign_count = self.get_height() - full_block.index;
if sign_count < BLOCK_SIGNERS_MIN { if sign_count < BLOCK_SIGNERS_MIN {
@@ -356,7 +363,8 @@ impl Chain {
/// Adds transaction to transactions table /// Adds transaction to transactions table
fn add_transaction_to_table(&mut self, index: u64, timestamp: i64, t: &Transaction) -> sqlite::Result<State> { fn add_transaction_to_table(&mut self, index: u64, timestamp: i64, t: &Transaction) -> sqlite::Result<State> {
let sql = match t.class.as_ref() { let sql = match t.class.as_ref() {
"domain" => SQL_ADD_DOMAIN, CLASS_DOMAIN => SQL_ADD_DOMAIN,
CLASS_ORIGIN => return Ok(State::Done),
_ => return Err(sqlite::Error { code: None, message: None }) _ => return Err(sqlite::Error { code: None, message: None })
}; };
@@ -590,29 +598,18 @@ 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 _index = statement.read::<i64>(0).unwrap() as u64;
let timestamp = statement.read::<i64>(1).unwrap(); let timestamp = statement.read::<i64>(1).unwrap();
let identity = Bytes::from_bytes(&statement.read::<Vec<u8>>(2).unwrap()); let identity = Bytes::from_bytes(&statement.read::<Vec<u8>>(2).unwrap());
let confirmation = Bytes::from_bytes(&statement.read::<Vec<u8>>(3).unwrap()); let confirmation = Bytes::from_bytes(&statement.read::<Vec<u8>>(3).unwrap());
let class = String::from("domain"); let class = String::from(CLASS_DOMAIN);
let data = statement.read::<String>(4).unwrap(); let data = statement.read::<String>(4).unwrap();
let owner = Bytes::from_bytes(&statement.read::<Vec<u8>>(5).unwrap()); let owner = Bytes::from_bytes(&statement.read::<Vec<u8>>(5).unwrap());
let transaction = Transaction { identity: identity.clone(), confirmation: confirmation.clone(), class, data, owner }; let transaction = Transaction { identity: identity.clone(), confirmation: confirmation.clone(), class, data, owner };
//debug!("Found transaction for domain {}: {:?}", domain, &transaction); debug!("Found transaction for domain {:?}", &transaction);
if let Some(data) = transaction.get_domain_data() { if let Some(data) = transaction.get_domain_data() {
let mut domain = keystore.decrypt(data.domain.as_slice(), &confirmation.as_slice()[..12]); let decrypted = keystore.decrypt(data.domain.as_slice(), &confirmation.as_slice()[..12]);
if domain.is_empty() { let mut domain = String::from_utf8(decrypted.to_vec()).unwrap();
// Legacy encryption scheme
for i in 1..=10 {
let b = self.get_block(index - i).unwrap();
domain = keystore.decrypt(data.domain.as_slice(), &b.hash.as_slice()[..12]);
if !domain.is_empty() {
break;
}
}
}
let mut domain = String::from_utf8(domain.to_vec()).unwrap();
if domain.is_empty() { if domain.is_empty() {
domain = String::from("unknown"); domain = String::from("unknown");
} }
+14 -2
View File
@@ -25,9 +25,20 @@ pub struct Transaction {
} }
impl Transaction { impl Transaction {
pub fn from_str(identity: String, method: String, data: String, owner: Bytes) -> Self { pub fn from_str(identity: String, method: String, data: String, miner: Bytes, owner: Bytes) -> Self {
let hash = hash_identity(&identity, None); let hash = hash_identity(&identity, None);
let confirmation = hash_identity(&identity, Some(&owner)); let key = if owner.is_empty() {
&miner
} else {
&owner
};
let confirmation = hash_identity(&identity, Some(key));
// If the miner doesn't change owner, we don't include owner at all
let owner = if owner.is_empty() || owner == miner {
miner
} else {
owner
};
return Self::new(hash, confirmation, method, data, owner); return Self::new(hash, confirmation, method, data, owner);
} }
@@ -125,6 +136,7 @@ pub enum TransactionType {
pub struct DomainData { pub struct DomainData {
pub domain: Bytes, pub domain: Bytes,
pub zone: String, pub zone: String,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub info: String, pub info: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")] #[serde(default, skip_serializing_if = "Vec::is_empty")]
pub records: Vec<DnsRecord>, pub records: Vec<DnsRecord>,
+12 -7
View File
@@ -53,8 +53,8 @@ pub fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
SaveKey => { action_save_key(&context); } SaveKey => { action_save_key(&context); }
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 } => { MineDomain { name, data, owner } => {
action_create_domain(Arc::clone(&context), Arc::clone(&miner), web_view, name, data); action_create_domain(Arc::clone(&context), Arc::clone(&miner), web_view, name, data, owner);
} }
TransferDomain { .. } => {} TransferDomain { .. } => {}
StopMining => { context.lock().unwrap().bus.post(Event::ActionStopMining); } StopMining => { context.lock().unwrap().bus.post(Event::ActionStopMining); }
@@ -332,7 +332,7 @@ fn load_domains(context: &mut MutexGuard<Context>, handle: &Handle<()>) {
}); });
} }
fn action_create_domain(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>, web_view: &mut WebView<()>, name: String, data: String) { fn action_create_domain(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>, web_view: &mut WebView<()>, name: String, data: String, owner: String) {
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();
@@ -358,6 +358,11 @@ fn action_create_domain(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>,
return; return;
} }
}; };
let owner = if !owner.is_empty() {
Bytes::new(from_hex(&owner).unwrap())
} else {
Bytes::default()
};
// Check if yggdrasil only quality of zone is not violated // Check if yggdrasil only quality of zone is not violated
let zones = context.chain.get_zones(); let zones = context.chain.get_zones();
for z in zones { for z in zones {
@@ -376,7 +381,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); create_domain(c, miner, CLASS_DOMAIN, &name, data, DOMAIN_DIFFICULTY, &keystore, owner);
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));
} }
@@ -482,13 +487,13 @@ fn format_event_now(kind: &str, message: &str) -> String {
format!("addEvent('{}', '{}', '{}');", kind, time.format("%d.%m.%y %X"), message) format!("addEvent('{}', '{}', '{}');", kind, time.format("%d.%m.%y %X"), message)
} }
fn create_domain(_context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>, class: &str, name: &str, mut data: DomainData, difficulty: u32, keystore: &Keystore) { fn create_domain(_context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>, class: &str, name: &str, mut data: DomainData, difficulty: u32, keystore: &Keystore, owner: Bytes) {
let name = name.to_owned(); let name = name.to_owned();
let confirmation = hash_identity(&name, Some(&keystore.get_public())); let confirmation = hash_identity(&name, Some(&keystore.get_public()));
data.domain = keystore.encrypt(name.as_bytes(), &confirmation.as_slice()[..12]); data.domain = keystore.encrypt(name.as_bytes(), &confirmation.as_slice()[..12]);
let data = serde_json::to_string(&data).unwrap(); let data = serde_json::to_string(&data).unwrap();
let transaction = Transaction::from_str(name, class.to_owned(), data, keystore.get_public().clone()); let transaction = Transaction::from_str(name, class.to_owned(), data, keystore.get_public().clone(), owner);
let block = Block::new(Some(transaction), keystore.get_public(), Bytes::default(), difficulty); let block = Block::new(Some(transaction), keystore.get_public(), Bytes::default(), difficulty);
miner.lock().unwrap().add_block(block, keystore.clone()); miner.lock().unwrap().add_block(block, keystore.clone());
} }
@@ -502,7 +507,7 @@ pub enum Cmd {
SaveKey, SaveKey,
CheckRecord { data: String }, CheckRecord { data: String },
CheckDomain { name: String }, CheckDomain { name: String },
MineDomain { name: String, data: String }, MineDomain { name: String, data: String, owner: String },
TransferDomain { name: String, owner: String }, TransferDomain { name: String, owner: String },
StopMining, StopMining,
Open { link: String }, Open { link: String },
+52 -9
View File
@@ -176,13 +176,33 @@
</div> </div>
</div> </div>
<div class="control">
<div class="dropdown" id="advanced-dropdown" onclick="toggle(this, event);">
<div class="dropdown-trigger">
<button class="button" aria-haspopup="true" aria-controls="advanced-menu" onblur="closeAdvancedDropdown()">
<span>Advanced</span>
<span class="icon is-small">
<svg viewBox="0 0 24 24" style="width: 20px; height: 20px;"><path d="M7.41,8.58L12,13.17L16.59,8.58L18,10L12,16L6,10L7.41,8.58Z"></path></svg>
</span>
</button>
</div>
<div class="dropdown-menu" id="advanced-menu" role="menu">
<div class="dropdown-content">
<a class="dropdown-item" onclick="showOwnerDialog();" title="You can change domain owner. Leave empty to be yours only.">Change owner</a>
<a class="dropdown-item" onclick="showContactsDialog();" title="You can add contact information to your domain, if you wish">Set contacts</a>
<a class="dropdown-item">Set info</a>
</div>
</div>
</div>
</div>
<div class="control"> <div class="control">
<div class="buttons has-addons"> <div class="buttons has-addons">
<button id="add_record_button" class="button is-link is-light" onclick="showNewRecordDialog();" title="Domain is nothing without good DNS records">Add record</button> <button id="add_record_button" class="button is-link is-light" onclick="showNewRecordDialog();" title="Domain is nothing without good DNS records">Add record</button>
<button disabled id="owners_button" class="button is-link is-light" onclick="showOwnersDialog();" title="You can change domain owners. Leave empty to be yours only."> <!--<button disabled id="owner_button" class="button is-link is-light" onclick="showOwnerDialog();" title="You can change domain owner. Leave empty to be yours only.">
<span>Set owners</span><span id="owners_count" class="tag is-info is-hidden ml-2">0</span> <span>Set owner</span><span id="owner_count" class="tag is-info is-hidden ml-2">0</span>
</button> </button>
<button disabled id="add_contacts_button" class="button is-link is-light" onclick="showContactsDialog();" title="You can add contact information to your domain, if you wish">Set contacts</button> <button disabled id="add_contacts_button" class="button is-link is-light" onclick="showContactsDialog();" title="You can add contact information to your domain, if you wish">Set contacts</button>-->
<button id="new_domain_button" class="button is-link" onclick="createDomain();" title="Start mining">Mine domain</button> <button id="new_domain_button" class="button is-link" onclick="createDomain();" title="Start mining">Mine domain</button>
</div> </div>
</div> </div>
@@ -196,23 +216,46 @@
</div> </div>
</div> </div>
<div id="owners_dialog" class="modal"> <div id="owner_dialog" class="modal">
<div class="modal-background"></div> <div class="modal-background"></div>
<div class="modal-content"> <div class="modal-content">
<div class="box"> <div class="box">
<div class="field"> <div class="field">
<label class="label">Public keys</label> <label class="label">Public keys</label>
<div class="control"> <div class="control is-expanded has-icons-left">
<textarea class="textarea" id="owners_text"></textarea> <input class="input is-expanded" type="text" placeholder="Public key" id="owner_text">
<span class="icon is-small is-left">
<svg viewBox="0 0 24 24" style="width: 20px; height: 20px;"><path d="M12,17A2,2 0 0,0 14,15C14,13.89 13.1,13 12,13A2,2 0 0,0 10,15A2,2 0 0,0 12,17M18,8A2,2 0 0,1 20,10V20A2,2 0 0,1 18,22H6A2,2 0 0,1 4,20V10C4,8.89 4.9,8 6,8H7V6A5,5 0 0,1 12,1A5,5 0 0,1 17,6V8H18M12,3A3,3 0 0,0 9,6V8H15V6A3,3 0 0,0 12,3Z"></path></svg>
</span>
</div> </div>
</div> </div>
<p class="help mb-3">Domains can be owned by several people. <p class="help mb-3">Domains can be owned by several people.
You need to add their public keys in this text field, separated by new line. You need to add their public keys in this text field, separated by new line.
If you don't include your own key, then domain will be completely transferred to entered owners. If you don't include your own key, then domain will be completely transferred to entered owner.
If you wish to own domain by yourself, just leave this space empty.</p> If you wish to own domain by yourself, just leave this space empty.</p>
<div class="buttons is-grouped is-centered"> <div class="buttons is-grouped is-centered">
<button class="button is-link" id="owners_positive_button" onclick="ownersPositiveButton();">Ok</button> <button class="button is-link" id="owner_positive_button" onclick="ownerPositiveButton();">Ok</button>
<button class="button is-link is-light" id="owners_negative_button" onclick="ownersCancelButton();">Cancel</button> <button class="button is-link is-light" id="owner_negative_button" onclick="ownerCancelButton();">Cancel</button>
</div>
</div>
</div>
</div>
<div id="contacts_dialog" class="modal">
<div class="modal-background"></div>
<div class="modal-content">
<div class="box">
<div class="field">
<label class="label">Domain owner contacts</label>
<div class="control">
<textarea class="textarea" id="contacts_text"></textarea>
</div>
</div>
<p class="help mb-3">You can add some contacts to your domain if you wish to be contacted in regards of your services.
Just list your contacts, separated by new line.</p>
<div class="buttons is-grouped is-centered">
<button class="button is-link" id="contacts_positive_button" onclick="contactsPositiveButton();">Ok</button>
<button class="button is-link is-light" id="contacts_negative_button" onclick="contactsCancelButton();">Cancel</button>
</div> </div>
</div> </div>
</div> </div>
+56 -46
View File
@@ -1,5 +1,5 @@
var recordsBuffer = []; var recordsBuffer = [];
var ownersBuffer = []; var owner = "";
var availableZones = []; var availableZones = [];
var myDomains = []; var myDomains = [];
var currentZone; var currentZone;
@@ -114,17 +114,21 @@ function refreshMyDomains() {
var title = value.name; var title = value.name;
var domain_data = JSON.parse(value.data); var domain_data = JSON.parse(value.data);
var tags = ""; var tags = "";
domain_data.records.forEach(function(v, i, a) { if (typeof domain_data.records !== 'undefined') {
if (typeof v.domain !== 'undefined') { domain_data.records.forEach(function(v, i, a) {
var buf = tag.replace("{domain}", v.domain); if (typeof v.domain !== 'undefined') {
if (typeof v.addr !== 'undefined') { var buf = tag.replace("{domain}", v.domain);
buf = buf.replace("{ip}", v.addr); if (typeof v.addr !== 'undefined') {
} else if (typeof v.host !== 'undefined') { buf = buf.replace("{ip}", v.addr);
buf = buf.replace("{ip}", v.host); } else if (typeof v.host !== 'undefined') {
buf = buf.replace("{ip}", v.host);
}
tags = tags + buf;
} }
tags = tags + buf; });
} } else {
}); tags = tag.replace("{domain}", "No records").replace("{ip}", "");
}
cards = cards + card.replace("{title}", title).replace("{domain}", title).replace("{tags}", tags); cards = cards + card.replace("{title}", title).replace("{domain}", title).replace("{tags}", tags);
}); });
document.getElementById("my_domains").innerHTML = cards; document.getElementById("my_domains").innerHTML = cards;
@@ -138,9 +142,11 @@ function editDomain(domain) {
var title = value.name; var title = value.name;
var domain_data = JSON.parse(value.data); var domain_data = JSON.parse(value.data);
recordsBuffer = []; recordsBuffer = [];
domain_data.records.forEach(function(v, i, a) { if (typeof domain_data.records !== 'undefined') {
recordsBuffer.push(v); domain_data.records.forEach(function(v, i, a) {
}); recordsBuffer.push(v);
});
}
document.getElementById("new_domain").value = title.replace("." + domain_data.zone, ""); document.getElementById("new_domain").value = title.replace("." + domain_data.zone, "");
changeZone(domain_data.zone); changeZone(domain_data.zone);
refreshRecordsList(); refreshRecordsList();
@@ -231,11 +237,11 @@ function createDomain() {
var data = {}; var data = {};
data.domain = ""; data.domain = "";
data.zone = currentZone.name; data.zone = currentZone.name;
data.info = "";
data.records = recordsBuffer; data.records = recordsBuffer;
data.owners = []; // TODO make a dialog to fill them
data.contacts = []; // TODO make a dialog to fill them data.contacts = []; // TODO make a dialog to fill them
data = JSON.stringify(data); data = JSON.stringify(data);
external.invoke(JSON.stringify({cmd: 'mineDomain', name: domain, data: data})); external.invoke(JSON.stringify({cmd: 'mineDomain', name: domain, data: data, owner: owner}));
} }
function domainMiningStarted() { function domainMiningStarted() {
@@ -253,11 +259,11 @@ function domainMiningUnavailable() {
//recordsBuffer = []; //recordsBuffer = [];
//refreshRecordsList(); //refreshRecordsList();
document.getElementById("new_domain_dialog").className = "modal"; document.getElementById("new_domain_dialog").className = "modal";
document.getElementById("tab_domains").disabled = true; document.getElementById("tab_domains").disabled = false;
document.getElementById("domain_records").disabled = true; document.getElementById("domain_records").disabled = false;
document.getElementById("add_record_button").disabled = true; document.getElementById("add_record_button").disabled = false;
document.getElementById("new_domain_button").disabled = true; document.getElementById("new_domain_button").disabled = false;
document.getElementById("new_key_button").disabled = true; document.getElementById("new_key_button").disabled = false;
} }
function sendAction(param) { function sendAction(param) {
@@ -307,8 +313,13 @@ function showModalDialog(text, callback) {
dialog.className = "modal is-active"; dialog.className = "modal is-active";
} }
function showOwnersDialog() { function showOwnerDialog() {
var dialog = document.getElementById("owners_dialog"); var dialog = document.getElementById("owner_dialog");
dialog.className = "modal is-active";
}
function showContactsDialog() {
var dialog = document.getElementById("contacts_dialog");
dialog.className = "modal is-active"; dialog.className = "modal is-active";
} }
@@ -320,33 +331,25 @@ function isValidOwner(text) {
return regexp.test(text); return regexp.test(text);
} }
function ownersPositiveButton() { function ownerPositiveButton() {
var text = document.getElementById("owners_text").value; var text = document.getElementById("owner_text").value;
if (text != "") { if (text != "") {
ownersBuffer = []; if (isValidOwner(text)) {
var wrong = false; owner = text;
var lines = text.split("\n"); } else {
lines.forEach(function(value, index, array) { alert("Wrong owner '{}'!".replace("{}", value));
if (wrong) { wrong = true;
return; return;
} }
if (isValidOwner(value)) { } else {
ownersBuffer.push(value); owner = "";
} else {
alert("Wrong owner '{}'!".replace("{}", value));
wrong = true;
return;
}
});
}
if (!wrong) {
var dialog = document.getElementById("owners_dialog");
dialog.className = "modal";
} }
var dialog = document.getElementById("owner_dialog");
dialog.className = "modal";
} }
function ownersCancelButton() { function ownerCancelButton() {
var dialog = document.getElementById("owners_dialog"); var dialog = document.getElementById("owner_dialog");
dialog.className = "modal"; dialog.className = "modal";
} }
@@ -449,6 +452,13 @@ function closeZonesDropdown() {
} }
} }
function closeAdvancedDropdown() {
var active = document.activeElement;
if (active == null || (active.id != 'advanced-menu' && active.tagName != 'BODY')) {
document.getElementById("advanced-dropdown").className = "dropdown";
}
}
function refreshZonesList() { function refreshZonesList() {
var buf = ""; var buf = "";
availableZones.sort(function compare(rhs, lhs) { availableZones.sort(function compare(rhs, lhs) {