Added a possibility to set owner for mined domain.
Fixed small errors.
This commit is contained in:
+15
-18
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user