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);
let index = block.index;
let timestamp = block.timestamp;
let owner = block.pub_key.clone();
self.last_block = Some(block.clone());
if block.transaction.is_some() {
self.last_full_block = Some(block.clone());
}
let transaction = block.transaction.clone();
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");
}
}
@@ -321,6 +325,9 @@ impl Chain {
}
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 {
let sign_count = self.get_height() - full_block.index;
if sign_count < BLOCK_SIGNERS_MIN {
@@ -356,7 +363,8 @@ impl Chain {
/// Adds transaction to transactions table
fn add_transaction_to_table(&mut self, index: u64, timestamp: i64, t: &Transaction) -> sqlite::Result<State> {
let sql = match t.class.as_ref() {
"domain" => SQL_ADD_DOMAIN,
CLASS_DOMAIN => SQL_ADD_DOMAIN,
CLASS_ORIGIN => return Ok(State::Done),
_ => 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();
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 _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("domain");
let class = String::from(CLASS_DOMAIN);
let data = statement.read::<String>(4).unwrap();
let owner = Bytes::from_bytes(&statement.read::<Vec<u8>>(5).unwrap());
let transaction = Transaction { identity: identity.clone(), confirmation: confirmation.clone(), class, data, owner };
//debug!("Found transaction for domain {}: {:?}", domain, &transaction);
debug!("Found transaction for domain {:?}", &transaction);
if let Some(data) = transaction.get_domain_data() {
let mut domain = keystore.decrypt(data.domain.as_slice(), &confirmation.as_slice()[..12]);
if domain.is_empty() {
// 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();
let decrypted = keystore.decrypt(data.domain.as_slice(), &confirmation.as_slice()[..12]);
let mut domain = String::from_utf8(decrypted.to_vec()).unwrap();
if domain.is_empty() {
domain = String::from("unknown");
}
+14 -2
View File
@@ -25,9 +25,20 @@ pub struct 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 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);
}
@@ -125,6 +136,7 @@ pub enum TransactionType {
pub struct DomainData {
pub domain: Bytes,
pub zone: String,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub info: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
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); }
CheckRecord { data } => { action_check_record(web_view, data); }
CheckDomain { name } => { action_check_domain(&context, web_view, name); }
MineDomain { name, data } => {
action_create_domain(Arc::clone(&context), Arc::clone(&miner), web_view, name, data);
MineDomain { name, data, owner } => {
action_create_domain(Arc::clone(&context), Arc::clone(&miner), web_view, name, data, owner);
}
TransferDomain { .. } => {}
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);
let c = Arc::clone(&context);
let context = context.lock().unwrap();
@@ -358,6 +358,11 @@ fn action_create_domain(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>,
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
let zones = context.chain.get_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) {
MineResult::Fine => {
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();");
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)
}
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 confirmation = hash_identity(&name, Some(&keystore.get_public()));
data.domain = keystore.encrypt(name.as_bytes(), &confirmation.as_slice()[..12]);
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);
miner.lock().unwrap().add_block(block, keystore.clone());
}
@@ -502,7 +507,7 @@ pub enum Cmd {
SaveKey,
CheckRecord { data: String },
CheckDomain { name: String },
MineDomain { name: String, data: String },
MineDomain { name: String, data: String, owner: String },
TransferDomain { name: String, owner: String },
StopMining,
Open { link: String },
+52 -9
View File
@@ -176,13 +176,33 @@
</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="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 disabled id="owners_button" class="button is-link is-light" onclick="showOwnersDialog();" title="You can change domain owners. Leave empty to be yours only.">
<span>Set owners</span><span id="owners_count" class="tag is-info is-hidden ml-2">0</span>
<!--<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 owner</span><span id="owner_count" class="tag is-info is-hidden ml-2">0</span>
</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>
</div>
</div>
@@ -196,23 +216,46 @@
</div>
</div>
<div id="owners_dialog" class="modal">
<div id="owner_dialog" class="modal">
<div class="modal-background"></div>
<div class="modal-content">
<div class="box">
<div class="field">
<label class="label">Public keys</label>
<div class="control">
<textarea class="textarea" id="owners_text"></textarea>
<div class="control is-expanded has-icons-left">
<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>
<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.
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>
<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 is-light" id="owners_negative_button" onclick="ownersCancelButton();">Cancel</button>
<button class="button is-link" id="owner_positive_button" onclick="ownerPositiveButton();">Ok</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>
+56 -46
View File
@@ -1,5 +1,5 @@
var recordsBuffer = [];
var ownersBuffer = [];
var owner = "";
var availableZones = [];
var myDomains = [];
var currentZone;
@@ -114,17 +114,21 @@ function refreshMyDomains() {
var title = value.name;
var domain_data = JSON.parse(value.data);
var tags = "";
domain_data.records.forEach(function(v, i, a) {
if (typeof v.domain !== 'undefined') {
var buf = tag.replace("{domain}", v.domain);
if (typeof v.addr !== 'undefined') {
buf = buf.replace("{ip}", v.addr);
} else if (typeof v.host !== 'undefined') {
buf = buf.replace("{ip}", v.host);
if (typeof domain_data.records !== 'undefined') {
domain_data.records.forEach(function(v, i, a) {
if (typeof v.domain !== 'undefined') {
var buf = tag.replace("{domain}", v.domain);
if (typeof v.addr !== 'undefined') {
buf = buf.replace("{ip}", v.addr);
} 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);
});
document.getElementById("my_domains").innerHTML = cards;
@@ -138,9 +142,11 @@ function editDomain(domain) {
var title = value.name;
var domain_data = JSON.parse(value.data);
recordsBuffer = [];
domain_data.records.forEach(function(v, i, a) {
recordsBuffer.push(v);
});
if (typeof domain_data.records !== 'undefined') {
domain_data.records.forEach(function(v, i, a) {
recordsBuffer.push(v);
});
}
document.getElementById("new_domain").value = title.replace("." + domain_data.zone, "");
changeZone(domain_data.zone);
refreshRecordsList();
@@ -231,11 +237,11 @@ function createDomain() {
var data = {};
data.domain = "";
data.zone = currentZone.name;
data.info = "";
data.records = recordsBuffer;
data.owners = []; // TODO make a dialog to fill them
data.contacts = []; // TODO make a dialog to fill them
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() {
@@ -253,11 +259,11 @@ function domainMiningUnavailable() {
//recordsBuffer = [];
//refreshRecordsList();
document.getElementById("new_domain_dialog").className = "modal";
document.getElementById("tab_domains").disabled = true;
document.getElementById("domain_records").disabled = true;
document.getElementById("add_record_button").disabled = true;
document.getElementById("new_domain_button").disabled = true;
document.getElementById("new_key_button").disabled = true;
document.getElementById("tab_domains").disabled = false;
document.getElementById("domain_records").disabled = false;
document.getElementById("add_record_button").disabled = false;
document.getElementById("new_domain_button").disabled = false;
document.getElementById("new_key_button").disabled = false;
}
function sendAction(param) {
@@ -307,8 +313,13 @@ function showModalDialog(text, callback) {
dialog.className = "modal is-active";
}
function showOwnersDialog() {
var dialog = document.getElementById("owners_dialog");
function showOwnerDialog() {
var dialog = document.getElementById("owner_dialog");
dialog.className = "modal is-active";
}
function showContactsDialog() {
var dialog = document.getElementById("contacts_dialog");
dialog.className = "modal is-active";
}
@@ -320,33 +331,25 @@ function isValidOwner(text) {
return regexp.test(text);
}
function ownersPositiveButton() {
var text = document.getElementById("owners_text").value;
function ownerPositiveButton() {
var text = document.getElementById("owner_text").value;
if (text != "") {
ownersBuffer = [];
var wrong = false;
var lines = text.split("\n");
lines.forEach(function(value, index, array) {
if (wrong) {
return;
}
if (isValidOwner(value)) {
ownersBuffer.push(value);
} else {
alert("Wrong owner '{}'!".replace("{}", value));
wrong = true;
return;
}
});
}
if (!wrong) {
var dialog = document.getElementById("owners_dialog");
dialog.className = "modal";
if (isValidOwner(text)) {
owner = text;
} else {
alert("Wrong owner '{}'!".replace("{}", value));
wrong = true;
return;
}
} else {
owner = "";
}
var dialog = document.getElementById("owner_dialog");
dialog.className = "modal";
}
function ownersCancelButton() {
var dialog = document.getElementById("owners_dialog");
function ownerCancelButton() {
var dialog = document.getElementById("owner_dialog");
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() {
var buf = "";
availableZones.sort(function compare(rhs, lhs) {