2021-02-19 21:04:58 +00:00
|
|
|
// With the default subsystem, 'console', windows creates an additional console window for the program.
|
|
|
|
// This is silently ignored on non-windows systems.
|
|
|
|
// See https://msdn.microsoft.com/en-us/library/4cc7ya5b.aspx for more details.
|
2019-12-08 11:32:51 +00:00
|
|
|
#![windows_subsystem = "windows"]
|
2020-04-18 19:31:40 +00:00
|
|
|
extern crate web_view;
|
2021-02-15 22:09:30 +00:00
|
|
|
extern crate tinyfiledialogs as tfd;
|
2019-12-08 11:32:51 +00:00
|
|
|
|
2021-02-19 15:41:43 +00:00
|
|
|
use std::env;
|
2021-01-30 13:18:37 +00:00
|
|
|
use std::sync::{Arc, Mutex};
|
2021-02-19 15:41:43 +00:00
|
|
|
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
2021-01-30 13:18:37 +00:00
|
|
|
use std::thread;
|
2021-02-22 13:55:23 +00:00
|
|
|
use std::time::{Duration, Instant};
|
2021-01-30 13:18:37 +00:00
|
|
|
|
2021-02-19 21:04:58 +00:00
|
|
|
#[cfg(windows)]
|
|
|
|
use winapi::um::wincon::{AttachConsole, FreeConsole, ATTACH_PARENT_PROCESS};
|
|
|
|
|
2021-02-14 17:20:30 +00:00
|
|
|
use rand::RngCore;
|
2021-02-19 15:41:43 +00:00
|
|
|
use serde::Deserialize;
|
2021-01-30 13:18:37 +00:00
|
|
|
use web_view::*;
|
2021-02-19 15:41:43 +00:00
|
|
|
use getopts::Options;
|
2021-02-20 15:28:10 +00:00
|
|
|
use simple_logger::{SimpleLogger};
|
2021-02-21 20:56:56 +00:00
|
|
|
#[allow(unused_imports)]
|
2021-02-20 15:28:10 +00:00
|
|
|
use log::{trace, debug, info, warn, error, LevelFilter};
|
2019-12-01 21:45:25 +00:00
|
|
|
|
2021-03-02 17:11:17 +00:00
|
|
|
use alfis::{Blockchain, Bytes, Context, Keystore, Transaction, check_domain, Block};
|
2021-01-30 13:18:37 +00:00
|
|
|
use alfis::event::Event;
|
|
|
|
use alfis::miner::Miner;
|
2021-02-05 21:24:28 +00:00
|
|
|
use alfis::p2p::Network;
|
2021-02-19 15:41:43 +00:00
|
|
|
use alfis::settings::Settings;
|
|
|
|
use alfis::dns::context::{ServerContext, ResolveStrategy};
|
|
|
|
use alfis::dns::server::{DnsServer, DnsUdpServer, DnsTcpServer};
|
|
|
|
use alfis::dns::protocol::DnsRecord;
|
|
|
|
use alfis::blockchain::filter::BlockchainFilter;
|
2021-01-30 13:18:37 +00:00
|
|
|
|
|
|
|
extern crate serde;
|
2020-04-18 19:31:40 +00:00
|
|
|
extern crate serde_json;
|
2019-12-08 11:32:51 +00:00
|
|
|
|
2021-01-17 23:18:35 +00:00
|
|
|
const KEYSTORE_DIFFICULTY: usize = 24;
|
|
|
|
const SETTINGS_FILENAME: &str = "alfis.cfg";
|
2021-01-14 17:34:43 +00:00
|
|
|
|
2019-12-01 21:45:25 +00:00
|
|
|
fn main() {
|
2021-02-19 21:04:58 +00:00
|
|
|
// When linked with the windows subsystem windows won't automatically attach
|
|
|
|
// to the console of the parent process, so we do it explicitly. This fails silently if the parent has no console.
|
|
|
|
#[cfg(windows)]
|
|
|
|
unsafe {
|
|
|
|
AttachConsole(ATTACH_PARENT_PROCESS);
|
|
|
|
}
|
|
|
|
|
2021-02-22 20:45:32 +00:00
|
|
|
let version = env!("CARGO_PKG_VERSION");
|
|
|
|
println!("Starting ALFIS {}", version);
|
2021-02-19 15:41:43 +00:00
|
|
|
let args: Vec<String> = env::args().collect();
|
|
|
|
let program = args[0].clone();
|
|
|
|
|
|
|
|
let mut opts = Options::new();
|
2021-02-27 17:57:15 +00:00
|
|
|
opts.optflag("h", "help", "Print this help menu");
|
|
|
|
opts.optflag("n", "nogui", "Run without graphic user interface");
|
|
|
|
opts.optflag("v", "verbose", "Show more debug messages");
|
|
|
|
opts.optflag("d", "debug", "Show trace messages, more than debug");
|
|
|
|
opts.optopt("c", "config", "Path to config file", "");
|
2021-02-19 15:41:43 +00:00
|
|
|
|
|
|
|
let opt_matches = match opts.parse(&args[1..]) {
|
|
|
|
Ok(m) => m,
|
|
|
|
Err(f) => panic!(f.to_string()),
|
|
|
|
};
|
|
|
|
|
|
|
|
if opt_matches.opt_present("h") {
|
|
|
|
let brief = format!("Usage: {} [options]", program);
|
|
|
|
print!("{}", opts.usage(&brief));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let no_gui = opt_matches.opt_present("n");
|
|
|
|
|
2021-02-20 15:28:10 +00:00
|
|
|
let mut level = LevelFilter::Info;
|
|
|
|
if opt_matches.opt_present("v") {
|
|
|
|
level = LevelFilter::Debug;
|
|
|
|
}
|
|
|
|
if opt_matches.opt_present("d") {
|
|
|
|
level = LevelFilter::Trace;
|
|
|
|
}
|
2021-02-22 09:11:22 +00:00
|
|
|
let config_name = match opt_matches.opt_str("c") {
|
|
|
|
None => { SETTINGS_FILENAME.to_owned() }
|
|
|
|
Some(path) => { path }
|
|
|
|
};
|
2021-02-20 15:28:10 +00:00
|
|
|
SimpleLogger::new().with_level(level).init().unwrap();
|
|
|
|
|
2021-02-22 09:11:22 +00:00
|
|
|
let settings = Settings::load(&config_name).expect("Error loading settings");
|
2021-01-17 23:18:35 +00:00
|
|
|
let keystore: Keystore = match Keystore::from_file(&settings.key_file, "") {
|
2021-02-15 22:09:30 +00:00
|
|
|
None => {
|
2021-02-20 15:28:10 +00:00
|
|
|
warn!("Generated temporary keystore. Please, generate full-privileged keys.");
|
2021-02-15 22:09:30 +00:00
|
|
|
Keystore::new()
|
|
|
|
}
|
2021-01-17 23:18:35 +00:00
|
|
|
Some(keystore) => { keystore }
|
|
|
|
};
|
2021-02-14 17:20:30 +00:00
|
|
|
let blockchain: Blockchain = Blockchain::new(&settings);
|
2021-02-14 23:29:30 +00:00
|
|
|
match blockchain.get_block(0) {
|
2021-02-20 15:28:10 +00:00
|
|
|
None => { info!("No blocks found in DB"); }
|
|
|
|
Some(block) => { info!("Loaded DB with origin {:?}", &block.hash); }
|
2021-02-14 23:29:30 +00:00
|
|
|
}
|
2021-02-19 15:41:43 +00:00
|
|
|
let settings_copy = settings.clone();
|
2020-04-18 19:31:40 +00:00
|
|
|
let context: Arc<Mutex<Context>> = Arc::new(Mutex::new(Context::new(settings, keystore, blockchain)));
|
2021-02-19 15:41:43 +00:00
|
|
|
start_dns_server(&context, &settings_copy);
|
2020-04-18 19:31:40 +00:00
|
|
|
|
2021-01-14 17:34:43 +00:00
|
|
|
let mut miner_obj = Miner::new(context.clone());
|
|
|
|
miner_obj.start_mining_thread();
|
|
|
|
let miner: Arc<Mutex<Miner>> = Arc::new(Mutex::new(miner_obj));
|
2020-04-18 19:31:40 +00:00
|
|
|
|
2021-02-05 21:24:28 +00:00
|
|
|
let mut network = Network::new(context.clone());
|
2021-02-14 17:20:30 +00:00
|
|
|
network.start().expect("Error starting network component");
|
2021-02-05 21:24:28 +00:00
|
|
|
|
2021-01-14 17:34:43 +00:00
|
|
|
create_genesis_if_needed(&context, &miner);
|
2021-02-19 15:41:43 +00:00
|
|
|
if no_gui {
|
|
|
|
let sleep = Duration::from_millis(1000);
|
|
|
|
loop {
|
|
|
|
thread::sleep(sleep);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
run_interface(context.clone(), miner.clone());
|
|
|
|
}
|
2021-02-19 21:04:58 +00:00
|
|
|
|
|
|
|
// Without explicitly detaching the console cmd won't redraw it's prompt.
|
|
|
|
#[cfg(windows)]
|
|
|
|
unsafe {
|
|
|
|
FreeConsole();
|
|
|
|
}
|
2021-02-19 15:41:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn start_dns_server(context: &Arc<Mutex<Context>>, settings: &Settings) {
|
|
|
|
let server_context = create_server_context(context.clone(), &settings);
|
|
|
|
|
|
|
|
if server_context.enable_udp {
|
|
|
|
let udp_server = DnsUdpServer::new(server_context.clone(), 20);
|
|
|
|
if let Err(e) = udp_server.run_server() {
|
2021-02-20 15:28:10 +00:00
|
|
|
error!("Failed to bind UDP listener: {:?}", e);
|
2021-02-19 15:41:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if server_context.enable_tcp {
|
|
|
|
let tcp_server = DnsTcpServer::new(server_context.clone(), 20);
|
|
|
|
if let Err(e) = tcp_server.run_server() {
|
2021-02-20 15:28:10 +00:00
|
|
|
error!("Failed to bind TCP listener: {:?}", e);
|
2021-02-19 15:41:43 +00:00
|
|
|
}
|
|
|
|
}
|
2019-12-08 11:32:51 +00:00
|
|
|
}
|
|
|
|
|
2021-01-14 17:34:43 +00:00
|
|
|
fn create_genesis_if_needed(context: &Arc<Mutex<Context>>, miner: &Arc<Mutex<Miner>>) {
|
2021-02-13 22:37:44 +00:00
|
|
|
// If there is no origin in settings and no blockchain in DB, generate genesis block
|
|
|
|
let context = context.lock().unwrap();
|
|
|
|
let last_block = context.get_blockchain().last_block();
|
|
|
|
let origin = context.settings.origin.clone();
|
|
|
|
if origin.eq("") && last_block.is_none() {
|
2021-01-20 18:23:41 +00:00
|
|
|
// If blockchain is empty, we are going to mine a Genesis block
|
2021-03-02 17:11:17 +00:00
|
|
|
create_genesis(miner.clone(), &context.get_keystore());
|
2021-01-14 17:34:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-18 19:31:40 +00:00
|
|
|
fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
|
2021-02-05 21:24:28 +00:00
|
|
|
let file_content = include_str!("webview/index.html");
|
2021-02-27 17:57:15 +00:00
|
|
|
let mut styles = inline_style(include_str!("webview/bulma.css"));
|
|
|
|
styles.push_str(&inline_style(include_str!("webview/busy_indicator.css")));
|
2021-02-05 21:24:28 +00:00
|
|
|
let scripts = inline_script(include_str!("webview/scripts.js"));
|
2020-04-18 19:31:40 +00:00
|
|
|
|
2021-01-30 13:18:37 +00:00
|
|
|
let html = Content::Html(file_content.to_owned().replace("{styles}", &styles).replace("{scripts}", &scripts));
|
2021-02-22 20:45:32 +00:00
|
|
|
let title = format!("ALFIS {}", env!("CARGO_PKG_VERSION"));
|
2021-02-22 13:55:23 +00:00
|
|
|
let mut interface = web_view::builder()
|
2021-02-22 20:45:32 +00:00
|
|
|
.title(&title)
|
2021-01-30 13:18:37 +00:00
|
|
|
.content(html)
|
2021-02-22 20:45:32 +00:00
|
|
|
.size(1023, 720)
|
|
|
|
.min_size(895, 350)
|
2020-04-18 19:31:40 +00:00
|
|
|
.resizable(true)
|
2021-02-17 13:54:55 +00:00
|
|
|
.debug(false)
|
2020-04-18 19:31:40 +00:00
|
|
|
.user_data(())
|
2021-01-30 13:18:37 +00:00
|
|
|
.invoke_handler(|web_view, arg| {
|
2020-04-18 19:31:40 +00:00
|
|
|
use Cmd::*;
|
2021-02-20 15:28:10 +00:00
|
|
|
debug!("Command {}", arg);
|
2020-04-18 19:31:40 +00:00
|
|
|
match serde_json::from_str(arg).unwrap() {
|
2021-01-30 13:18:37 +00:00
|
|
|
Loaded => {
|
2021-02-28 21:17:03 +00:00
|
|
|
web_view.eval("showMiningIndicator(false, false);").expect("Error evaluating!");
|
2021-02-14 17:20:30 +00:00
|
|
|
let handle = web_view.handle();
|
2021-02-28 21:17:03 +00:00
|
|
|
let mut status = Status::new();
|
2021-01-30 13:18:37 +00:00
|
|
|
let mut c = context.lock().unwrap();
|
|
|
|
c.bus.register(move |_uuid, e| {
|
2021-02-20 15:28:10 +00:00
|
|
|
debug!("Got event from bus {:?}", &e);
|
2021-02-15 22:09:30 +00:00
|
|
|
let eval = match e {
|
|
|
|
Event::KeyCreated { path, public } => { format!("keystoreChanged('{}', '{}');", &path, &public) }
|
|
|
|
Event::KeyLoaded { path, public } => { format!("keystoreChanged('{}', '{}');", &path, &public) }
|
|
|
|
Event::KeySaved { path, public } => { format!("keystoreChanged('{}', '{}');", &path, &public) }
|
2021-02-28 21:17:03 +00:00
|
|
|
Event::MinerStarted => {
|
|
|
|
status.mining = true;
|
|
|
|
String::from("setLeftStatusBarText('Mining...'); showMiningIndicator(true, false);")
|
2021-02-27 17:57:15 +00:00
|
|
|
}
|
2021-02-28 21:17:03 +00:00
|
|
|
Event::KeyGeneratorStarted => {
|
|
|
|
status.mining = true;
|
|
|
|
String::from("setLeftStatusBarText('Mining...'); showMiningIndicator(true, false);")
|
2021-02-27 17:57:15 +00:00
|
|
|
}
|
2021-02-28 21:17:03 +00:00
|
|
|
Event::MinerStopped | Event::KeyGeneratorStopped => {
|
|
|
|
status.mining = false;
|
|
|
|
if status.syncing {
|
|
|
|
String::from("setLeftStatusBarText('Syncing...'); showMiningIndicator(true, true);")
|
|
|
|
} else {
|
|
|
|
String::from("setLeftStatusBarText('Idle'); showMiningIndicator(false, false);")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Event::SyncFinished => {
|
|
|
|
status.syncing = false;
|
|
|
|
if status.mining {
|
|
|
|
String::from("setLeftStatusBarText('Mining...'); showMiningIndicator(true, false);")
|
|
|
|
} else {
|
|
|
|
format!("setLeftStatusBarText('Idle'); showMiningIndicator(false, false);")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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)
|
|
|
|
}
|
2021-02-27 17:57:15 +00:00
|
|
|
}
|
2021-02-15 22:09:30 +00:00
|
|
|
_ => { String::new() }
|
2021-01-30 13:18:37 +00:00
|
|
|
};
|
2021-02-15 22:09:30 +00:00
|
|
|
|
|
|
|
if !eval.is_empty() {
|
2021-02-20 15:28:10 +00:00
|
|
|
debug!("Evaluating {}", &eval);
|
2021-02-15 22:09:30 +00:00
|
|
|
handle.dispatch(move |web_view| {
|
2021-02-19 15:41:43 +00:00
|
|
|
web_view.eval(&eval.replace("\\", "\\\\"))
|
2021-02-15 22:09:30 +00:00
|
|
|
}).expect("Error dispatching!");
|
|
|
|
}
|
2021-01-30 13:18:37 +00:00
|
|
|
true
|
|
|
|
});
|
2021-02-16 14:14:29 +00:00
|
|
|
let eval = format!("keystoreChanged('{}', '{}');", c.keystore.get_path(), &c.keystore.get_public().to_string());
|
2021-02-20 15:28:10 +00:00
|
|
|
debug!("Evaluating {}", &eval);
|
2021-02-16 14:14:29 +00:00
|
|
|
web_view.eval(&eval.replace("\\", "\\\\")).expect("Error evaluating!");
|
2021-01-30 13:18:37 +00:00
|
|
|
}
|
2021-02-15 22:09:30 +00:00
|
|
|
LoadKey {} => {
|
|
|
|
let result = tfd::open_file_dialog("Open keys file", "", Some((&["*.key"], "*.key")));
|
|
|
|
match result {
|
|
|
|
None => {}
|
|
|
|
Some(file_name) => {
|
|
|
|
match Keystore::from_file(&file_name, "") {
|
|
|
|
None => {
|
2021-02-20 15:28:10 +00:00
|
|
|
error!("Error loading keystore '{}'!", &file_name);
|
2021-02-27 17:57:15 +00:00
|
|
|
}
|
2021-02-15 22:09:30 +00:00
|
|
|
Some(keystore) => {
|
2021-02-20 15:28:10 +00:00
|
|
|
info!("Loaded keystore with key: {:?}", &keystore.get_public());
|
2021-02-15 22:09:30 +00:00
|
|
|
let mut c = context.lock().unwrap();
|
2021-02-27 17:57:15 +00:00
|
|
|
c.bus.post(Event::KeyLoaded { path: keystore.get_path().to_owned(), public: keystore.get_public().to_string() });
|
2021-02-15 22:09:30 +00:00
|
|
|
c.set_keystore(keystore);
|
|
|
|
}
|
|
|
|
}
|
2021-01-30 13:18:37 +00:00
|
|
|
}
|
2020-04-18 19:31:40 +00:00
|
|
|
}
|
2021-02-27 17:57:15 +00:00
|
|
|
}
|
2021-02-15 22:09:30 +00:00
|
|
|
CreateKey {} => {
|
|
|
|
create_key(context.clone());
|
|
|
|
}
|
|
|
|
SaveKey {} => {
|
|
|
|
let result = tfd::save_file_dialog_with_filter("Save keys file", "", &["*.key"], "Key files (*.key)");
|
|
|
|
match result {
|
|
|
|
None => {}
|
|
|
|
Some(new_path) => {
|
|
|
|
let mut c = context.lock().unwrap();
|
|
|
|
let path = new_path.clone();
|
|
|
|
let public = c.keystore.get_public().to_string();
|
|
|
|
c.keystore.save(&new_path, "");
|
2021-02-20 15:28:10 +00:00
|
|
|
info!("Key file saved to {}", &path);
|
2021-02-27 17:57:15 +00:00
|
|
|
c.bus.post(Event::KeySaved { path, public });
|
2021-02-15 22:09:30 +00:00
|
|
|
}
|
|
|
|
}
|
2020-04-18 19:31:40 +00:00
|
|
|
}
|
2021-02-27 17:57:15 +00:00
|
|
|
CheckDomain { name } => {
|
2021-02-22 20:45:32 +00:00
|
|
|
let name = name.to_lowercase();
|
2021-01-30 13:18:37 +00:00
|
|
|
let c = context.lock().unwrap();
|
|
|
|
let available = c.get_blockchain().is_domain_available(&name, &c.get_keystore());
|
2021-02-14 17:20:30 +00:00
|
|
|
web_view.eval(&format!("domainAvailable({})", available)).expect("Error evaluating!");
|
2021-01-30 13:18:37 +00:00
|
|
|
}
|
2021-02-19 15:41:43 +00:00
|
|
|
CreateDomain { name, records, .. } => {
|
2021-02-20 15:28:10 +00:00
|
|
|
debug!("Got records: {}", records);
|
2021-02-22 20:45:32 +00:00
|
|
|
let name = name.to_lowercase();
|
|
|
|
if !check_domain(&name, true) {
|
|
|
|
return Ok(());
|
|
|
|
}
|
2021-02-19 15:41:43 +00:00
|
|
|
if serde_json::from_str::<Vec<DnsRecord>>(&records).is_ok() {
|
2021-02-22 20:45:32 +00:00
|
|
|
let (keystore, transaction) = {
|
|
|
|
let context = context.lock().unwrap();
|
|
|
|
(context.get_keystore(), context.blockchain.get_domain_transaction(&name))
|
2021-02-19 15:41:43 +00:00
|
|
|
};
|
2021-02-20 20:36:58 +00:00
|
|
|
match transaction {
|
|
|
|
None => {
|
|
|
|
create_domain(miner.clone(), name, records, &keystore);
|
|
|
|
}
|
|
|
|
Some(transaction) => {
|
|
|
|
if transaction.pub_key == keystore.get_public() {
|
|
|
|
create_domain(miner.clone(), name, records, &keystore);
|
|
|
|
} else {
|
|
|
|
warn!("Tried to mine not owned domain!");
|
|
|
|
let _ = web_view.eval(&format!("showWarning('{}');", "You cannot change domain that you don't own!"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-02-19 15:41:43 +00:00
|
|
|
} else {
|
2021-02-20 15:28:10 +00:00
|
|
|
warn!("Error in DNS records for domain!");
|
2021-02-20 20:36:58 +00:00
|
|
|
let _ = web_view.eval(&format!("showWarning('{}');", "Something wrong with your records! Please, correct the error and try again."));
|
2021-02-19 15:41:43 +00:00
|
|
|
}
|
2020-04-18 19:31:40 +00:00
|
|
|
}
|
2021-02-19 15:41:43 +00:00
|
|
|
ChangeDomain { .. } => {}
|
|
|
|
RenewDomain { .. } => {}
|
|
|
|
TransferDomain { .. } => {}
|
2021-02-27 17:57:15 +00:00
|
|
|
CheckZone { name } => {
|
2021-02-22 20:45:32 +00:00
|
|
|
let name = name.to_lowercase();
|
|
|
|
if !check_domain(&name, false) {
|
|
|
|
web_view.eval("zoneAvailable(false)").expect("Error evaluating!");
|
|
|
|
} else {
|
|
|
|
let c = context.lock().unwrap();
|
|
|
|
let available = c.get_blockchain().is_domain_available(&name, &c.get_keystore());
|
|
|
|
web_view.eval(&format!("zoneAvailable({})", available)).expect("Error evaluating!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CreateZone { name, data } => {
|
|
|
|
let name = name.to_lowercase();
|
|
|
|
let data = data.to_lowercase();
|
|
|
|
let (keystore, transaction) = {
|
|
|
|
let context = context.lock().unwrap();
|
|
|
|
(context.get_keystore(), context.blockchain.get_domain_transaction(&name))
|
|
|
|
};
|
|
|
|
match transaction {
|
|
|
|
None => {
|
|
|
|
create_domain(miner.clone(), name, data, &keystore);
|
|
|
|
}
|
|
|
|
Some(transaction) => {
|
|
|
|
if transaction.pub_key == keystore.get_public() {
|
|
|
|
create_domain(miner.clone(), name, data, &keystore);
|
|
|
|
} else {
|
|
|
|
warn!("Tried to mine not owned domain!");
|
|
|
|
let _ = web_view.eval(&format!("showWarning('{}');", "You cannot change domain that you don't own!"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-01-30 13:18:37 +00:00
|
|
|
StopMining => {
|
|
|
|
context.lock().unwrap().bus.post(Event::ActionStopMining);
|
|
|
|
}
|
2020-04-18 19:31:40 +00:00
|
|
|
}
|
|
|
|
//dbg!(&signature);
|
|
|
|
Ok(())
|
|
|
|
})
|
2021-02-22 13:55:23 +00:00
|
|
|
.build()
|
|
|
|
.expect("Error building GUI");
|
|
|
|
|
|
|
|
// We use this ugly loop to lower CPU usage a lot.
|
|
|
|
// If we use .run() or only .step() in a loop without sleeps it will try
|
|
|
|
// to support 60FPS and uses more CPU than it should.
|
|
|
|
let pause = Duration::from_millis(25);
|
|
|
|
let mut start = Instant::now();
|
|
|
|
loop {
|
|
|
|
match interface.step() {
|
|
|
|
None => {
|
|
|
|
info!("Interface closed, exiting");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
Some(result) => {
|
|
|
|
match result {
|
|
|
|
Ok(_) => {}
|
|
|
|
Err(_) => {
|
|
|
|
error!("Something wrong with webview, exiting");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if start.elapsed().as_millis() > 1 {
|
|
|
|
thread::sleep(pause);
|
|
|
|
start = Instant::now();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
interface.exit();
|
2019-12-08 11:32:51 +00:00
|
|
|
}
|
|
|
|
|
2021-03-02 17:11:17 +00:00
|
|
|
fn create_genesis(miner: Arc<Mutex<Miner>>, keystore: &Keystore) {
|
|
|
|
let block = Block::new(None, keystore.get_public(), Bytes::default());
|
2021-01-14 17:34:43 +00:00
|
|
|
let mut miner_guard = miner.lock().unwrap();
|
2021-03-02 17:11:17 +00:00
|
|
|
miner_guard.add_block(block);
|
2021-01-14 17:34:43 +00:00
|
|
|
}
|
|
|
|
|
2021-01-17 23:18:35 +00:00
|
|
|
fn create_domain<S: Into<String>>(miner: Arc<Mutex<Miner>>, name: S, data: S, keystore: &Keystore) {
|
|
|
|
let name = name.into();
|
2021-02-20 15:28:10 +00:00
|
|
|
info!("Generating domain or zone {}", name);
|
2021-01-14 17:34:43 +00:00
|
|
|
//let tags_vector: Vec<String> = tags.into().trim().split(",").map(|s| s.trim()).map(String::from).collect();
|
2021-03-02 17:11:17 +00:00
|
|
|
let transaction = Transaction::from_str(name.into(), "domain".into(), data.into(), keystore.get_public().clone());
|
|
|
|
let block = Block::new(Some(transaction), keystore.get_public(), Bytes::default());
|
2021-01-14 17:34:43 +00:00
|
|
|
let mut miner_guard = miner.lock().unwrap();
|
2021-03-02 17:11:17 +00:00
|
|
|
miner_guard.add_block(block);
|
2019-12-08 11:32:51 +00:00
|
|
|
}
|
|
|
|
|
2021-02-15 22:09:30 +00:00
|
|
|
fn create_key(context: Arc<Mutex<Context>>) {
|
2021-02-14 17:20:30 +00:00
|
|
|
let mining = Arc::new(AtomicBool::new(true));
|
2021-02-15 22:09:30 +00:00
|
|
|
let miners_count = Arc::new(AtomicUsize::new(0));
|
2021-02-14 17:20:30 +00:00
|
|
|
{ context.lock().unwrap().bus.post(Event::KeyGeneratorStarted); }
|
2021-01-17 23:18:35 +00:00
|
|
|
for _ in 0..num_cpus::get() {
|
|
|
|
let context = context.clone();
|
|
|
|
let mining = mining.clone();
|
2021-02-15 22:09:30 +00:00
|
|
|
let miners_count = miners_count.clone();
|
2021-01-17 23:18:35 +00:00
|
|
|
thread::spawn(move || {
|
2021-02-15 22:09:30 +00:00
|
|
|
miners_count.fetch_add(1, Ordering::Relaxed);
|
2021-01-17 23:18:35 +00:00
|
|
|
match generate_key(KEYSTORE_DIFFICULTY, mining.clone()) {
|
2021-01-30 13:18:37 +00:00
|
|
|
None => {
|
2021-02-20 15:28:10 +00:00
|
|
|
debug!("Keystore mining finished");
|
2021-01-30 13:18:37 +00:00
|
|
|
}
|
2021-01-17 23:18:35 +00:00
|
|
|
Some(keystore) => {
|
2021-02-20 15:28:10 +00:00
|
|
|
info!("Key mined successfully: {:?}", &keystore.get_public());
|
2021-01-17 23:18:35 +00:00
|
|
|
let mut c = context.lock().unwrap();
|
2021-02-27 17:57:15 +00:00
|
|
|
mining.store(false, Ordering::Relaxed);
|
|
|
|
c.bus.post(Event::KeyCreated { path: keystore.get_path().to_owned(), public: keystore.get_public().to_string() });
|
2021-01-17 23:18:35 +00:00
|
|
|
c.set_keystore(keystore);
|
|
|
|
}
|
|
|
|
}
|
2021-02-15 22:09:30 +00:00
|
|
|
let miners = miners_count.fetch_sub(1, Ordering::Relaxed) - 1;
|
|
|
|
if miners == 0 {
|
|
|
|
context.lock().unwrap().bus.post(Event::KeyGeneratorStopped);
|
|
|
|
}
|
2021-01-17 23:18:35 +00:00
|
|
|
});
|
|
|
|
}
|
2021-01-30 13:18:37 +00:00
|
|
|
context.lock().unwrap().bus.register(move |_uuid, e| {
|
|
|
|
if e == Event::ActionStopMining {
|
|
|
|
mining.store(false, Ordering::Relaxed);
|
|
|
|
}
|
|
|
|
false
|
|
|
|
});
|
2019-12-08 11:32:51 +00:00
|
|
|
}
|
|
|
|
|
2021-01-17 23:18:35 +00:00
|
|
|
fn generate_key(difficulty: usize, mining: Arc<AtomicBool>) -> Option<Keystore> {
|
|
|
|
let mut rng = rand::thread_rng();
|
2021-02-14 17:20:30 +00:00
|
|
|
let mut buf = [0u8; 32];
|
2021-01-17 23:18:35 +00:00
|
|
|
loop {
|
|
|
|
rng.fill_bytes(&mut buf);
|
|
|
|
let keystore = Keystore::from_bytes(&buf);
|
|
|
|
if keystore.hash_is_good(difficulty) {
|
2021-02-20 15:28:10 +00:00
|
|
|
info!("Generated keypair: {:?}", &keystore);
|
2021-01-17 23:18:35 +00:00
|
|
|
return Some(keystore);
|
|
|
|
}
|
|
|
|
if !mining.load(Ordering::Relaxed) {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-01-14 17:34:43 +00:00
|
|
|
|
2021-02-19 15:41:43 +00:00
|
|
|
fn create_server_context(context: Arc<Mutex<Context>>, settings: &Settings) -> Arc<ServerContext> {
|
|
|
|
let mut server_context = ServerContext::new();
|
|
|
|
server_context.allow_recursive = true;
|
2021-02-22 21:03:56 +00:00
|
|
|
server_context.dns_host = settings.dns.host.clone();
|
2021-02-19 15:41:43 +00:00
|
|
|
server_context.dns_port = settings.dns.port;
|
|
|
|
server_context.resolve_strategy = match settings.dns.forwarders.is_empty() {
|
|
|
|
true => { ResolveStrategy::Recursive }
|
2021-02-27 17:57:15 +00:00
|
|
|
false => { ResolveStrategy::Forward { upstreams: settings.dns.forwarders.clone() } }
|
2021-02-19 15:41:43 +00:00
|
|
|
};
|
|
|
|
server_context.filters.push(Box::new(BlockchainFilter::new(context)));
|
|
|
|
match server_context.initialize() {
|
|
|
|
Ok(_) => {}
|
2021-02-20 15:28:10 +00:00
|
|
|
Err(e) => { panic!("DNS server failed to initialize: {:?}", e); }
|
2021-02-19 15:41:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Arc::new(server_context)
|
|
|
|
}
|
|
|
|
|
2020-04-18 19:31:40 +00:00
|
|
|
#[derive(Deserialize)]
|
|
|
|
#[serde(tag = "cmd", rename_all = "camelCase")]
|
|
|
|
pub enum Cmd {
|
2021-01-30 13:18:37 +00:00
|
|
|
Loaded,
|
2021-02-27 17:57:15 +00:00
|
|
|
LoadKey {},
|
|
|
|
CreateKey {},
|
|
|
|
SaveKey {},
|
|
|
|
CheckZone { name: String },
|
|
|
|
CreateZone { name: String, data: String },
|
|
|
|
CheckDomain { name: String },
|
|
|
|
CreateDomain { name: String, records: String, tags: String },
|
|
|
|
ChangeDomain { name: String, records: String, tags: String },
|
|
|
|
RenewDomain { name: String, days: u16 },
|
|
|
|
TransferDomain { name: String, owner: String },
|
2021-01-30 13:18:37 +00:00
|
|
|
StopMining,
|
2019-12-08 11:32:51 +00:00
|
|
|
}
|
|
|
|
|
2021-02-28 21:17:03 +00:00
|
|
|
struct Status {
|
|
|
|
pub mining: bool,
|
|
|
|
pub syncing: bool,
|
|
|
|
pub synced_blocks: u64,
|
|
|
|
pub sync_height: u64,
|
|
|
|
pub nodes_connected: usize,
|
|
|
|
pub chain_height: u64
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Status {
|
|
|
|
fn new() -> Self {
|
|
|
|
Status { mining: false, syncing: false, synced_blocks: 0, sync_height: 0, nodes_connected: 0, chain_height: 0 }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-18 19:31:40 +00:00
|
|
|
fn inline_style(s: &str) -> String {
|
|
|
|
format!(r#"<style type="text/css">{}</style>"#, s)
|
2019-12-08 11:32:51 +00:00
|
|
|
}
|
|
|
|
|
2020-04-18 19:31:40 +00:00
|
|
|
fn inline_script(s: &str) -> String {
|
|
|
|
format!(r#"<script type="text/javascript">{}</script>"#, s)
|
2019-12-01 21:45:25 +00:00
|
|
|
}
|
2021-02-19 15:41:43 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use alfis::dns::protocol::{DnsRecord, TransientTtl};
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn record_to_string() {
|
|
|
|
let record = DnsRecord::A {
|
|
|
|
domain: "google.com".to_string(),
|
|
|
|
addr: "127.0.0.1".parse().unwrap(),
|
|
|
|
ttl: TransientTtl(300)
|
|
|
|
};
|
|
|
|
println!("Record is {:?}", &record);
|
|
|
|
println!("Record in JSON is {}", serde_json::to_string(&record).unwrap());
|
|
|
|
}
|
|
|
|
}
|