2019-12-08 11:32:51 +00:00
|
|
|
#![windows_subsystem = "windows"]
|
2020-04-18 19:31:40 +00:00
|
|
|
extern crate web_view;
|
2019-12-08 11:32:51 +00:00
|
|
|
|
2020-04-18 19:31:40 +00:00
|
|
|
use std::collections::HashMap;
|
2021-01-30 13:18:37 +00:00
|
|
|
use std::sync::{Arc, Mutex};
|
2021-01-17 23:18:35 +00:00
|
|
|
use std::sync::atomic::{AtomicBool, Ordering};
|
2021-01-30 13:18:37 +00:00
|
|
|
use std::thread;
|
|
|
|
|
|
|
|
use rand::{Rng, RngCore};
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use web_view::*;
|
2019-12-01 21:45:25 +00:00
|
|
|
|
2021-01-30 13:18:37 +00:00
|
|
|
use alfis::{Block, Blockchain, Bytes, Context, Keystore, Settings, Transaction};
|
|
|
|
use alfis::event::Event;
|
|
|
|
use alfis::miner::Miner;
|
2021-02-05 21:24:28 +00:00
|
|
|
use alfis::p2p::Network;
|
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-14 17:34:43 +00:00
|
|
|
const ONE_YEAR: u16 = 365;
|
|
|
|
const GENESIS_ZONE: &str = "ygg";
|
|
|
|
const GENESIS_ZONE_DIFFICULTY: u16 = 20;
|
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() {
|
2020-04-18 19:31:40 +00:00
|
|
|
println!("ALFIS 0.1.0");
|
2021-01-17 23:18:35 +00:00
|
|
|
let settings = Settings::load(SETTINGS_FILENAME).expect("Error loading settings");
|
|
|
|
let keystore: Keystore = match Keystore::from_file(&settings.key_file, "") {
|
|
|
|
None => { generate_key(KEYSTORE_DIFFICULTY, Arc::new(AtomicBool::new(true))).expect("Could not load or generate keypair") }
|
|
|
|
Some(keystore) => { keystore }
|
|
|
|
};
|
2021-02-13 22:37:44 +00:00
|
|
|
let blockchain: Blockchain = Blockchain::new(settings.origin.clone(), settings.version);
|
2020-04-18 19:31:40 +00:00
|
|
|
let context: Arc<Mutex<Context>> = Arc::new(Mutex::new(Context::new(settings, keystore, blockchain)));
|
|
|
|
|
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());
|
|
|
|
network.start();
|
|
|
|
|
2021-01-14 17:34:43 +00:00
|
|
|
create_genesis_if_needed(&context, &miner);
|
2020-04-18 19:31:40 +00:00
|
|
|
run_interface(context.clone(), miner.clone());
|
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();
|
|
|
|
// TODO compare first block's hash to origin
|
|
|
|
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-02-13 22:37:44 +00:00
|
|
|
create_genesis(miner.clone(), GENESIS_ZONE, &context.get_keystore(), GENESIS_ZONE_DIFFICULTY);
|
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");
|
|
|
|
let mut styles= inline_style(include_str!("webview/bulma.css"));
|
|
|
|
styles.push_str(&inline_style(include_str!("webview/loader.css")));
|
|
|
|
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));
|
2020-04-18 19:31:40 +00:00
|
|
|
web_view::builder()
|
|
|
|
.title("ALFIS 0.1.0")
|
2021-01-30 13:18:37 +00:00
|
|
|
.content(html)
|
2020-04-18 19:31:40 +00:00
|
|
|
.size(1024, 720)
|
|
|
|
.resizable(true)
|
|
|
|
.debug(true)
|
|
|
|
.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::*;
|
|
|
|
println!("Command {}", arg);
|
|
|
|
match serde_json::from_str(arg).unwrap() {
|
2021-01-30 13:18:37 +00:00
|
|
|
Loaded => {
|
|
|
|
web_view.eval("showMiningIndicator(false);");
|
|
|
|
let mut handle = web_view.handle();
|
|
|
|
let mut c = context.lock().unwrap();
|
|
|
|
c.bus.register(move |_uuid, e| {
|
|
|
|
println!("Got event from bus {:?}", &e);
|
|
|
|
let visible = match e {
|
|
|
|
Event::MinerStarted => { true }
|
|
|
|
Event::KeyGeneratorStarted => { true }
|
|
|
|
Event::MinerStopped => { false }
|
|
|
|
Event::KeyGeneratorStopped => { false }
|
|
|
|
_ => { false }
|
|
|
|
};
|
|
|
|
handle.dispatch(move |web_view| {
|
|
|
|
web_view.eval(&format!("showMiningIndicator({});", visible));
|
|
|
|
return WVResult::Ok(());
|
|
|
|
});
|
|
|
|
true
|
|
|
|
});
|
|
|
|
}
|
2020-04-18 19:31:40 +00:00
|
|
|
LoadKey { name, pass } => {
|
|
|
|
match Keystore::from_file(&name, &pass) {
|
|
|
|
None => {
|
|
|
|
println!("Error loading keystore '{}'!", &name);
|
|
|
|
},
|
2021-01-30 13:18:37 +00:00
|
|
|
Some(keystore) => {
|
2020-04-18 19:31:40 +00:00
|
|
|
let mut c = context.lock().unwrap();
|
2021-01-30 13:18:37 +00:00
|
|
|
c.set_keystore(keystore);
|
|
|
|
}
|
2020-04-18 19:31:40 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
CreateKey { name, pass } => {
|
2021-01-17 23:18:35 +00:00
|
|
|
create_key(context.clone(), &name, &pass);
|
2020-04-18 19:31:40 +00:00
|
|
|
}
|
2021-01-30 13:18:37 +00:00
|
|
|
CheckDomain { name} => {
|
|
|
|
let c = context.lock().unwrap();
|
|
|
|
let available = c.get_blockchain().is_domain_available(&name, &c.get_keystore());
|
|
|
|
web_view.eval(&format!("domainAvailable({})", available));
|
|
|
|
}
|
2020-04-18 19:31:40 +00:00
|
|
|
CreateDomain { name, records, tags } => {
|
2021-01-14 17:34:43 +00:00
|
|
|
let keystore = {
|
|
|
|
let mut guard = context.lock().unwrap();
|
|
|
|
guard.get_keystore()
|
|
|
|
};
|
2021-01-17 23:18:35 +00:00
|
|
|
create_domain(miner.clone(), name, records, &keystore);
|
2020-04-18 19:31:40 +00:00
|
|
|
}
|
|
|
|
ChangeDomain { name, records, tags } => {
|
2021-01-14 17:34:43 +00:00
|
|
|
let keystore = { context.lock().unwrap().get_keystore() };
|
|
|
|
// TODO
|
2020-04-18 19:31:40 +00:00
|
|
|
}
|
|
|
|
RenewDomain { name, days } => {}
|
|
|
|
TransferDomain { name, owner } => {}
|
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(())
|
|
|
|
})
|
|
|
|
.run()
|
|
|
|
.unwrap();
|
2019-12-08 11:32:51 +00:00
|
|
|
}
|
|
|
|
|
2021-01-14 17:34:43 +00:00
|
|
|
fn create_genesis<S: Into<String>>(miner: Arc<Mutex<Miner>>, name: S, keystore: &Keystore, difficulty: u16) {
|
2021-01-17 23:18:35 +00:00
|
|
|
let mut transaction = Transaction::from_str(name.into(), "zone".to_owned(), difficulty.to_string(), keystore.get_public().clone());
|
2021-01-14 17:34:43 +00:00
|
|
|
// Signing it with private key from Signature
|
|
|
|
let sign_hash = keystore.sign(&transaction.get_bytes());
|
2021-01-17 23:18:35 +00:00
|
|
|
transaction.set_signature(Bytes::from_bytes(&sign_hash));
|
2021-01-14 17:34:43 +00:00
|
|
|
let mut miner_guard = miner.lock().unwrap();
|
|
|
|
miner_guard.add_transaction(transaction);
|
|
|
|
}
|
|
|
|
|
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();
|
|
|
|
println!("Generating domain {}", name);
|
2021-01-14 17:34:43 +00:00
|
|
|
//let rec_vector: Vec<String> = records.into().trim().split("\n").map(|s| s.trim()).map(String::from).collect();
|
|
|
|
//let tags_vector: Vec<String> = tags.into().trim().split(",").map(|s| s.trim()).map(String::from).collect();
|
2021-01-17 23:18:35 +00:00
|
|
|
let mut transaction = { create_transaction(keystore, name, "domain".into(), data.into()) };
|
2021-01-14 17:34:43 +00:00
|
|
|
let mut miner_guard = miner.lock().unwrap();
|
|
|
|
miner_guard.add_transaction(transaction);
|
|
|
|
}
|
|
|
|
|
2021-01-17 23:18:35 +00:00
|
|
|
fn create_transaction<S: Into<String>>(keystore: &Keystore, name: S, method: S, data: S) -> Transaction {
|
2020-04-18 19:31:40 +00:00
|
|
|
// Creating transaction
|
2021-01-14 17:34:43 +00:00
|
|
|
// TODO Do not use owner for now, make a field in UI and use it if filled
|
2021-01-17 23:18:35 +00:00
|
|
|
let mut transaction = Transaction::from_str(name.into(), method.into(), data.into(), keystore.get_public().clone());
|
2020-04-18 19:31:40 +00:00
|
|
|
// Signing it with private key from Signature
|
|
|
|
let sign_hash = keystore.sign(&transaction.get_bytes());
|
2021-01-17 23:18:35 +00:00
|
|
|
transaction.set_signature(Bytes::from_bytes(&sign_hash));
|
2020-04-18 19:31:40 +00:00
|
|
|
transaction
|
2019-12-08 11:32:51 +00:00
|
|
|
}
|
|
|
|
|
2021-01-17 23:18:35 +00:00
|
|
|
fn create_key(context: Arc<Mutex<Context>>, filename: &str, password: &str) {
|
|
|
|
let mut mining = Arc::new(AtomicBool::new(true));
|
|
|
|
for _ in 0..num_cpus::get() {
|
|
|
|
let context = context.clone();
|
2021-01-30 13:18:37 +00:00
|
|
|
{ context.lock().unwrap().bus.post(Event::KeyGeneratorStarted); }
|
2021-01-17 23:18:35 +00:00
|
|
|
let filename= filename.to_owned();
|
|
|
|
let password= password.to_owned();
|
|
|
|
let mining = mining.clone();
|
|
|
|
thread::spawn(move || {
|
|
|
|
match generate_key(KEYSTORE_DIFFICULTY, mining.clone()) {
|
2021-01-30 13:18:37 +00:00
|
|
|
None => {
|
|
|
|
println!("Keystore mining finished");
|
|
|
|
context.lock().unwrap().bus.post(Event::KeyGeneratorStopped);
|
|
|
|
}
|
2021-01-17 23:18:35 +00:00
|
|
|
Some(keystore) => {
|
|
|
|
let mut c = context.lock().unwrap();
|
|
|
|
mining.store(false,Ordering::Relaxed);
|
|
|
|
keystore.save(&filename, &password);
|
|
|
|
c.set_keystore(keystore);
|
2021-01-30 13:18:37 +00:00
|
|
|
c.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();
|
|
|
|
let mut buf = [0u8; 64];
|
|
|
|
loop {
|
|
|
|
rng.fill_bytes(&mut buf);
|
|
|
|
let keystore = Keystore::from_bytes(&buf);
|
|
|
|
if keystore.hash_is_good(difficulty) {
|
|
|
|
println!("Generated keypair: {:?}", &keystore);
|
|
|
|
return Some(keystore);
|
|
|
|
}
|
|
|
|
if !mining.load(Ordering::Relaxed) {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
2021-01-14 17:34:43 +00:00
|
|
|
|
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,
|
2020-04-18 19:31:40 +00:00
|
|
|
LoadKey{name: String, pass: String},
|
|
|
|
CreateKey{name: String, pass: String},
|
2021-01-30 13:18:37 +00:00
|
|
|
CheckDomain{name: String},
|
2020-04-18 19:31:40 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|