Remastered domain mining interface!

This commit is contained in:
Revertron
2021-03-26 18:22:43 +01:00
parent 594878614f
commit fd6edce6b5
5 changed files with 203 additions and 90 deletions
+20
View File
@@ -45,6 +45,7 @@ const SQL_GET_LAST_FULL_BLOCK_FOR_KEY: &str = "SELECT * FROM blocks WHERE `trans
const SQL_GET_PUBLIC_KEY_BY_ID: &str = "SELECT pub_key FROM transactions WHERE identity = ? ORDER BY id DESC LIMIT 1;"; const SQL_GET_PUBLIC_KEY_BY_ID: &str = "SELECT pub_key FROM transactions WHERE identity = ? ORDER BY id DESC LIMIT 1;";
const SQL_GET_ID_BY_ID: &str = "SELECT identity FROM transactions WHERE identity = ? ORDER BY id DESC LIMIT 1;"; const SQL_GET_ID_BY_ID: &str = "SELECT identity FROM transactions WHERE identity = ? ORDER BY id DESC LIMIT 1;";
const SQL_GET_TRANSACTION_BY_ID: &str = "SELECT * FROM transactions WHERE identity = ? ORDER BY id DESC LIMIT 1;"; const SQL_GET_TRANSACTION_BY_ID: &str = "SELECT * FROM transactions WHERE identity = ? ORDER BY id DESC LIMIT 1;";
const SQL_GET_TRANSACTIONS_WITH_ZONE: &str = "SELECT data FROM transactions WHERE data LIKE '%difficulty%';";
pub struct Chain { pub struct Chain {
origin: Bytes, origin: Bytes,
@@ -271,6 +272,25 @@ impl Chain {
true true
} }
pub fn get_zones(&self) -> Vec<ZoneData> {
let mut result = Vec::new();
match self.db.prepare(SQL_GET_TRANSACTIONS_WITH_ZONE) {
Ok(mut statement) => {
while statement.next().unwrap() == State::Row {
let data = statement.read::<String>(0).unwrap();
info!("Got zone data {}", &data);
if let Ok(zone_data) = serde_json::from_str(&data) {
result.push(zone_data);
}
}
}
Err(e) => {
warn!("Can't get zones from DB {}", e);
}
}
result
}
/// Checks if some zone exists in our blockchain /// Checks if some zone exists in our blockchain
pub fn is_zone_in_blockchain(&self, zone: &str) -> bool { pub fn is_zone_in_blockchain(&self, zone: &str) -> bool {
if self.zones.borrow().contains(zone) { if self.zones.borrow().contains(zone) {
+8 -1
View File
@@ -6,6 +6,7 @@ use serde::ser::SerializeStruct;
use crate::blockchain::hash_utils::*; use crate::blockchain::hash_utils::*;
use crate::bytes::Bytes; use crate::bytes::Bytes;
use crate::dns::protocol::DnsRecord; use crate::dns::protocol::DnsRecord;
use std::fmt::{Display, Formatter};
extern crate serde; extern crate serde;
extern crate serde_json; extern crate serde_json;
@@ -90,8 +91,14 @@ impl DomainData {
} }
} }
#[derive(Clone, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct ZoneData { pub struct ZoneData {
pub name: String, pub name: String,
pub difficulty: u32 pub difficulty: u32
}
impl Display for ZoneData {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.write_str(&format!("{} ({})", self.name, self.difficulty))
}
} }
+91 -73
View File
@@ -20,7 +20,7 @@ use alfis::dns::protocol::DnsRecord;
use alfis::commons::{ZONE_MAX_LENGTH, ZONE_DIFFICULTY}; use alfis::commons::{ZONE_MAX_LENGTH, ZONE_DIFFICULTY};
use Cmd::*; use Cmd::*;
use alfis::blockchain::transaction::{DomainData, ZoneData}; use alfis::blockchain::transaction::{DomainData, ZoneData};
use self::web_view::WebView; use self::web_view::{WebView, Handle};
use alfis::blockchain::enums::MineResult; use alfis::blockchain::enums::MineResult;
pub fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) { pub fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
@@ -175,92 +175,110 @@ fn action_load_key(context: &Arc<Mutex<Context>>, web_view: &mut WebView<()>) {
fn action_loaded(context: &Arc<Mutex<Context>>, web_view: &mut WebView<()>) { fn action_loaded(context: &Arc<Mutex<Context>>, web_view: &mut WebView<()>) {
web_view.eval("showMiningIndicator(false, false);").expect("Error evaluating!"); web_view.eval("showMiningIndicator(false, false);").expect("Error evaluating!");
let handle = web_view.handle(); let handle: Handle<()> = web_view.handle();
let mut status = Status::new(); let status = Arc::new(Mutex::new(Status::new()));
let context_copy = Arc::clone(&context);
let mut c = context.lock().unwrap(); let mut c = context.lock().unwrap();
c.bus.register(move |_uuid, e| { c.bus.register(move |_uuid, e| {
//debug!("Got event from bus {:?}", &e); //debug!("Got event from bus {:?}", &e);
let eval = match e { let status = Arc::clone(&status);
Event::KeyCreated { path, public, hash } | let handle = handle.clone();
Event::KeyLoaded { path, public, hash } | let context_copy = Arc::clone(&context_copy);
Event::KeySaved { path, public, hash } => { thread::spawn(move || {
format!("keystoreChanged('{}', '{}', '{}');", &path, &public, &hash) let mut status = status.lock().unwrap();
} let context = context_copy.lock().unwrap();
Event::MinerStarted | Event::KeyGeneratorStarted => { let eval = match e {
status.mining = true; Event::KeyCreated { path, public, hash } |
String::from("setLeftStatusBarText('Mining...'); showMiningIndicator(true, false);") Event::KeyLoaded { path, public, hash } |
} Event::KeySaved { path, public, hash } => {
Event::MinerStopped {success, full} => { format!("keystoreChanged('{}', '{}', '{}');", &path, &public, &hash)
status.mining = false; }
let mut s = if status.syncing { Event::MinerStarted | Event::KeyGeneratorStarted => {
String::from("setLeftStatusBarText('Syncing...'); showMiningIndicator(true, true);") status.mining = true;
} else { String::from("setLeftStatusBarText('Mining...'); showMiningIndicator(true, false);")
String::from("setLeftStatusBarText('Idle'); showMiningIndicator(false, false);") }
}; Event::MinerStopped {success, full} => {
if full { status.mining = false;
let mut s = if status.syncing {
String::from("setLeftStatusBarText('Syncing...'); showMiningIndicator(true, true);")
} else {
String::from("setLeftStatusBarText('Idle'); showMiningIndicator(false, false);")
};
if full {
match success {
true => { s.push_str(" showSuccess('Block successfully mined!')"); }
false => { s.push_str(" showSuccess('Mining unsuccessful, sorry.')"); }
}
}
s
}
Event::KeyGeneratorStopped {success} => {
status.mining = false;
let mut s = if status.syncing {
String::from("setLeftStatusBarText('Syncing...'); showMiningIndicator(true, true);")
} else {
String::from("setLeftStatusBarText('Idle'); showMiningIndicator(false, false);")
};
match success { match success {
true => { s.push_str(" showSuccess('Block successfully mined!')"); } true => { s.push_str(" showSuccess('Key pair successfully mined!<br>Don`t forget to save!')"); }
false => { s.push_str(" showSuccess('Mining unsuccessful, sorry.')"); } false => { s.push_str(" showSuccess('Key mining got nothing, sorry.')"); }
}
s
}
Event::Syncing { have, height } => {
status.syncing = true;
status.synced_blocks = have;
status.sync_height = height;
if status.mining {
String::from("setLeftStatusBarText('Mining...'); showMiningIndicator(true, false);")
} else {
format!("setLeftStatusBarText('Synchronizing {}/{}'); showMiningIndicator(true, true);", have, height)
} }
} }
s Event::SyncFinished => {
} status.syncing = false;
Event::KeyGeneratorStopped {success} => { if status.mining {
status.mining = false; String::from("setLeftStatusBarText('Mining...'); showMiningIndicator(true, false);")
let mut s = if status.syncing { } else {
String::from("setLeftStatusBarText('Syncing...'); showMiningIndicator(true, true);") format!("setLeftStatusBarText('Idle'); showMiningIndicator(false, false);")
} else { }
String::from("setLeftStatusBarText('Idle'); showMiningIndicator(false, false);")
};
match success {
true => { s.push_str(" showSuccess('Key pair successfully mined!<br>Don`t forget to save!')"); }
false => { s.push_str(" showSuccess('Key mining got nothing, sorry.')"); }
} }
s Event::NetworkStatus { nodes, blocks } => {
} if status.mining || status.syncing || nodes < 3 {
Event::Syncing { have, height } => { format!("setRightStatusBarText('Nodes: {}, Blocks: {}')", nodes, blocks)
status.syncing = true; } else {
status.synced_blocks = have; format!("setLeftStatusBarText('Idle'); setRightStatusBarText('Nodes: {}, Blocks: {}')", nodes, blocks)
status.sync_height = height; }
if status.mining {
String::from("setLeftStatusBarText('Mining...'); showMiningIndicator(true, false);")
} else {
format!("setLeftStatusBarText('Synchronizing {}/{}'); showMiningIndicator(true, true);", have, height)
} }
} Event::BlockchainChanged {index} => {
Event::SyncFinished => { info!("Current blockchain height is {}", index);
status.syncing = false; if let Ok(zones) = serde_json::to_string(&context.chain.get_zones()) {
if status.mining { let _ = handle.dispatch(move |web_view|{
String::from("setLeftStatusBarText('Mining...'); showMiningIndicator(true, false);") web_view.eval(&format!("zonesChanged('{}');", &zones))
} else { });
format!("setLeftStatusBarText('Idle'); showMiningIndicator(false, false);") }
String::new() // Nothing
} }
} _ => { String::new() }
Event::NetworkStatus { nodes, blocks } => { };
if status.mining || status.syncing || nodes < 3 {
format!("setRightStatusBarText('Nodes: {}, Blocks: {}')", nodes, blocks)
} else {
format!("setLeftStatusBarText('Idle'); setRightStatusBarText('Nodes: {}, Blocks: {}')", nodes, blocks)
}
}
_ => { String::new() }
};
if !eval.is_empty() { if !eval.is_empty() {
handle.dispatch(move |web_view| { handle.dispatch(move |web_view| {
web_view.eval(&eval.replace("\\", "\\\\")) web_view.eval(&eval.replace("\\", "\\\\"))
}).expect("Error dispatching!"); }).expect("Error dispatching!");
} }
});
true true
}); });
if let Some(keystore) = c.get_keystore() { if let Some(keystore) = c.get_keystore() {
let eval = format!("keystoreChanged('{}', '{}', '{}');", let path = keystore.get_path().to_owned();
keystore.get_path(), let public = keystore.get_public().to_string();
&keystore.get_public().to_string(), let hash = keystore.get_hash().to_string();
&keystore.get_hash().to_string()); c.bus.post(Event::KeyLoaded { path, public, hash });
//debug!("Evaluating {}", &eval);
web_view.eval(&eval.replace("\\", "\\\\")).expect("Error evaluating!");
} }
let index = c.chain.height();
c.bus.post(Event::BlockchainChanged { index });
} }
fn action_create_domain(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>, web_view: &mut WebView<()>, name: String, records: &String) { fn action_create_domain(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>, web_view: &mut WebView<()>, name: String, records: &String) {
+32 -9
View File
@@ -76,16 +76,39 @@
<!-- Domain mining --> <!-- Domain mining -->
<div class="tab content is-hidden" id="tab_domains"> <div class="tab content is-hidden" id="tab_domains">
<div class="field is-grouped"> <div class="field is-grouped is-fullwidth">
<div class="control is-expanded has-icons-left"> <div class="control field has-addons is-expanded">
<input class="input is-expanded" type="text" placeholder="example.tld" id="new_domain" oninput="onDomainChange(this)"> <div class="control is-expanded has-icons-left">
<span class="icon is-small is-left"> <input class="input is-expanded" type="text" placeholder="domain" id="new_domain" oninput="onDomainChange(this)">
<svg viewBox="0 0 24 24" style="width: 20px; height: 20px;"><path d="M17.9,17.39C17.64,16.59 16.89,16 16,16H15V13A1,1 0 0,0 14,12H8V10H10A1,1 0 0,0 11,9V7H13A2,2 0 0,0 15,5V4.59C17.93,5.77 20,8.64 20,12C20,14.08 19.2,15.97 17.9,17.39M11,19.93C7.05,19.44 4,16.08 4,12C4,11.38 4.08,10.78 4.21,10.21L9,15V16A2,2 0 0,0 11,18M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z"></path></svg> <span class="icon is-small is-left">
</span> <svg viewBox="0 0 24 24" style="width: 20px; height: 20px;"><path d="M17.9,17.39C17.64,16.59 16.89,16 16,16H15V13A1,1 0 0,0 14,12H8V10H10A1,1 0 0,0 11,9V7H13A2,2 0 0,0 15,5V4.59C17.93,5.77 20,8.64 20,12C20,14.08 19.2,15.97 17.9,17.39M11,19.93C7.05,19.44 4,16.08 4,12C4,11.38 4.08,10.78 4.21,10.21L9,15V16A2,2 0 0,0 11,18M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z"></path></svg>
</span>
</div>
<!-- Zones -->
<div class="control">
<div class="dropdown" onclick="toggle(this, event);" onblur="alert('blured!')">
<div class="dropdown-trigger">
<button class="button" aria-haspopup="true" aria-controls="zones-menu">
<span id="zones-current-name">Select zone</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="zones-menu" role="menu">
<div class="dropdown-content" id="zones-links">
</div>
</div>
</div>
</div>
</div> </div>
<div class="buttons has-addons">
<button id="add_record_button" class="button is-info is-light" onclick="showNewRecordDialog();">Add record</button> <div class="control">
<button id="new_domain_button" class="button is-info" onclick="createDomain();">Mine domain</button> <div class="buttons has-addons">
<button id="add_record_button" class="button is-info is-light" onclick="showNewRecordDialog();">Add record</button>
<button id="new_domain_button" class="button is-info" onclick="createDomain();">Mine domain</button>
</div>
</div> </div>
</div> </div>
<p class="help">Enter domain name, add some DNS-records, then hit the "Mine domain" button!</p> <p class="help">Enter domain name, add some DNS-records, then hit the "Mine domain" button!</p>
+52 -7
View File
@@ -1,16 +1,18 @@
var recordsBuffer = []; var recordsBuffer = [];
var availableZones = [];
var currentZone = "";
function addRecord(record) { function addRecord(record) {
recordsBuffer.push(record); recordsBuffer.push(record);
refresh_records_list(); refreshRecordsList();
} }
function delRecord(index) { function delRecord(index) {
recordsBuffer.splice(index, 1); recordsBuffer.splice(index, 1);
refresh_records_list(); refreshRecordsList();
} }
function refresh_records_list() { function refreshRecordsList() {
var buf = ""; var buf = "";
if (recordsBuffer.length > 0) { if (recordsBuffer.length > 0) {
buf = "<label class=\"label\">Records:</label>\n"; buf = "<label class=\"label\">Records:</label>\n";
@@ -66,7 +68,7 @@ function showNewRecordDialog() {
button_negative.onclick = function() { button_negative.onclick = function() {
var dialog = document.getElementById("new_record_dialog"); var dialog = document.getElementById("new_record_dialog");
dialog.className = "modal"; dialog.className = "modal";
refresh_records_list(); refreshRecordsList();
} }
var dialog = document.getElementById("new_record_dialog"); var dialog = document.getElementById("new_record_dialog");
@@ -117,7 +119,12 @@ function openTab(element, tabName) {
// Show the current tab, and add an "is-active" class to the button that opened the tab // Show the current tab, and add an "is-active" class to the button that opened the tab
document.getElementById(tabName).className = "tab content"; document.getElementById(tabName).className = "tab content";
element.parentElement.className = "tab is-active"; element.parentElement.className = "tab is-active";
refresh_records_list(); refreshRecordsList();
}
function toggle(element, event) {
event.stopPropagation();
element.classList.toggle('is-active');
} }
function loadKey() { function loadKey() {
@@ -149,11 +156,13 @@ function recordOkay(okay) {
function createDomain() { function createDomain() {
var new_domain = document.getElementById("new_domain").value.toLowerCase(); var new_domain = document.getElementById("new_domain").value.toLowerCase();
var new_dom_records = JSON.stringify(recordsBuffer); var new_dom_records = JSON.stringify(recordsBuffer);
external.invoke(JSON.stringify({cmd: 'mineDomain', name: new_domain, records: new_dom_records})); var domain = new_domain + "." + currentZone.name;
external.invoke(JSON.stringify({cmd: 'mineDomain', name: domain, records: new_dom_records}));
} }
function domainMiningStarted() { function domainMiningStarted() {
recordsBuffer = []; recordsBuffer = [];
refreshRecordsList();
} }
function createZone() { function createZone() {
@@ -171,7 +180,8 @@ function sendAction(param) {
} }
function onDomainChange(element) { function onDomainChange(element) {
external.invoke(JSON.stringify({cmd: 'checkDomain', name: element.value})); var domain = element.value + "." + currentZone.name;
external.invoke(JSON.stringify({cmd: 'checkDomain', name: domain}));
} }
function domainAvailable(available) { function domainAvailable(available) {
@@ -320,4 +330,39 @@ function keystoreChanged(path, pub_key, hash) {
new_zone.disabled = false; new_zone.disabled = false;
var new_zone_difficulty = document.getElementById("new_zone_difficulty"); var new_zone_difficulty = document.getElementById("new_zone_difficulty");
new_zone_difficulty.disabled = false; new_zone_difficulty.disabled = false;
}
function refreshZonesList() {
var buf = "";
availableZones.forEach(function(value, index, array) {
var zone = value.name + " (" + value.difficulty + "🔥)";
var add_class = "";
if (currentZone.name == value.name) {
add_class = "is-active";
}
buf += "<a id=\"zone-{1}\" class=\"dropdown-item {2}\" onclick=\"changeZone('{3}');\">.{4}</a>"
.replace("{1}", value.name)
.replace("{2}", add_class)
.replace("{3}", value.name)
.replace("{4}", zone);
});
var links = document.getElementById("zones-links");
links.innerHTML = buf;
var cur_name = document.getElementById("zones-current-name");
cur_name.innerHTML = "." + currentZone.name + " (" + currentZone.difficulty + "🔥)";
}
function zonesChanged(text) {
availableZones = JSON.parse(text);
currentZone = availableZones[0];
refreshZonesList();
}
function changeZone(zone) {
availableZones.forEach(function(value, index, array) {
if (value.name == zone) {
currentZone = value;
}
});
refreshZonesList();
} }