mirror of https://github.com/Revertron/Alfis
Made a full refactoring of synchronization primitives between settings, keystore, blockchain and miner.
parent
3eaf63ba79
commit
01f37cc238
Binary file not shown.
@ -0,0 +1,50 @@
|
||||
use crate::{Keystore, Blockchain};
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub struct Context {
|
||||
pub(crate) settings: Settings,
|
||||
pub(crate) keystore: Keystore,
|
||||
pub(crate) blockchain: Blockchain,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
/// Creating an essential context to work with
|
||||
pub fn new(settings: Settings, keystore: Keystore, blockchain: Blockchain) -> Context {
|
||||
Context { settings, keystore, blockchain }
|
||||
}
|
||||
|
||||
/// Load keystore and return Context
|
||||
pub fn load_keystore<S: Into<String>>(mut self, name: S, password: S) -> Context {
|
||||
let filename = &name.into();
|
||||
match Keystore::from_file(filename, &password.into()) {
|
||||
None => {
|
||||
println!("Error loading keystore '{}'!", filename);
|
||||
},
|
||||
Some(keystore) => {
|
||||
self.keystore = keystore;
|
||||
},
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn get_keystore(&self) -> Keystore {
|
||||
self.keystore.clone()
|
||||
}
|
||||
|
||||
pub fn set_keystore(&mut self, keystore: Keystore) {
|
||||
self.keystore = keystore;
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Settings {
|
||||
pub chain_id: u32,
|
||||
pub version: u32,
|
||||
salts: HashMap<String, String>
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
/// TODO parse settings
|
||||
pub fn new<S: Into<String>>(chain_id: u32, version: u32, settings: S) -> Settings {
|
||||
Settings { chain_id, version, salts: HashMap::new() }
|
||||
}
|
||||
}
|
@ -0,0 +1,271 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Wyrd</title>
|
||||
{styles}
|
||||
<script defer src="https://use.fontawesome.com/releases/v5.3.1/js/all.js"></script>
|
||||
{scripts}
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<br/>
|
||||
<div class="columns">
|
||||
<div class="column is-one-fifth">
|
||||
<div class="menu">
|
||||
<ul class="menu-list">
|
||||
<li><a class="is-active" onclick="openTab(this, 'main')">Main</a></li>
|
||||
</ul>
|
||||
<p class="menu-label">Key management</p>
|
||||
<ul class="menu-list">
|
||||
<li><a onclick="openTab(this, 'key_load')">Load key</a></li>
|
||||
<li><a onclick="openTab(this, 'key_create')">Create key</a></li>
|
||||
<li><a onclick="openTab(this, 'key_passwd')">Change password</a></li>
|
||||
</ul>
|
||||
<p class="menu-label">Domain management</p>
|
||||
<ul class="menu-list">
|
||||
<li><a onclick="openTab(this, 'dom_new')">Create domain</a></li>
|
||||
<li><a onclick="openTab(this, 'dom_edit')">Manage domain</a></li>
|
||||
<li><a onclick="openTab(this, 'dom_renew')">Renew domain</a></li>
|
||||
<li><a onclick="openTab(this, 'dom_transfer')">Transfer domain</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div><!--column-->
|
||||
|
||||
<div class="column auto">
|
||||
<div class="content center" id="main">This screen in now yet implemented</div>
|
||||
|
||||
<div class="content is-hidden" id="key_load">
|
||||
<form action="#">
|
||||
<div class="field">
|
||||
<label class="label">Key file name</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text" placeholder="default.key" id="load_key_name">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">Key password</label>
|
||||
<div class="control">
|
||||
<input class="input" type="password" placeholder="123456" id="load_key_password">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field is-grouped">
|
||||
<div class="control">
|
||||
<button class="button is-link" onclick="loadKey();">Load</button>
|
||||
</div>
|
||||
<div class="control">
|
||||
<button class="button is-link is-light">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="content is-hidden" id="key_create">
|
||||
<form action="#">
|
||||
<div class="field">
|
||||
<label class="label">Key file name</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text" placeholder="default.key" id="create_key_name">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">Key password</label>
|
||||
<div class="control">
|
||||
<input class="input" type="password" placeholder="123456" id="create_key_password">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field is-grouped">
|
||||
<div class="control">
|
||||
<button class="button is-link" onclick="createKey();">Create</button>
|
||||
</div>
|
||||
<div class="control">
|
||||
<button class="button is-link is-light">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="content is-hidden" id="key_passwd">
|
||||
<form action="#">
|
||||
<div class="field">
|
||||
<label class="label">Key file name</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text" placeholder="default.key" id="change_key_name">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">Old key password</label>
|
||||
<div class="control">
|
||||
<input class="input" type="password" placeholder="123456" id="change_key_password">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">New key password</label>
|
||||
<div class="control">
|
||||
<input class="input" type="password" placeholder="123456" id="change_key_password_new">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">Repeat key password</label>
|
||||
<div class="control">
|
||||
<input class="input" type="password" placeholder="123456" id="change_key_password_repeat">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field is-grouped">
|
||||
<div class="control">
|
||||
<button class="button is-link">Change password</button>
|
||||
</div>
|
||||
<div class="control">
|
||||
<button class="button is-link is-light">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="content is-hidden" id="dom_new">
|
||||
<form action="#">
|
||||
<div class="field">
|
||||
<label class="label">New domain name</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text" placeholder="example.ygg" id="new_domain">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">Domain records</label>
|
||||
<div class="control">
|
||||
<textarea class="textarea" placeholder="@ IN AAAA 200:1111:2222:3333:4444:5555:6666:7777" id="new_domain_records"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">Domain tags (will be used for search)</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text" placeholder="blog, community, friendship" id="new_domain_tags">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field is-grouped">
|
||||
<div class="control">
|
||||
<button class="button is-link" onclick="createDomain();">Create domain</button>
|
||||
</div>
|
||||
<div class="control">
|
||||
<button class="button is-link is-light">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="content is-hidden" id="dom_edit">
|
||||
<form action="#">
|
||||
<div class="field">
|
||||
<label class="label">Your existing domain name</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text" placeholder="example.ygg" id="change_domain">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">All new domain records</label>
|
||||
<div class="control">
|
||||
<textarea class="textarea" placeholder="@ IN AAAA 200:1111:2222:3333:4444:5555:6666:7777" id="change_domain_records"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">Domain tags (will be used for search)</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text" placeholder="blog, community, friendship" id="change_domain_tags">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field is-grouped">
|
||||
<div class="control">
|
||||
<button class="button is-link" onclick="changeDomain();">Replace records</button>
|
||||
</div>
|
||||
<div class="control">
|
||||
<button class="button is-link is-light">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="content is-hidden" id="dom_renew">
|
||||
<form action="#">
|
||||
<div class="field">
|
||||
<label class="label">Your existing domain name</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text" placeholder="example.ygg" id="renew_domain">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">Days to add</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text" placeholder="365" value="365" id="renew_domain_extend_days" disabled>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field is-grouped">
|
||||
<div class="control">
|
||||
<button class="button is-link" onclick="renewDomain();">Renew domain</button>
|
||||
</div>
|
||||
<div class="control">
|
||||
<button class="button is-link is-light">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="content is-hidden" id="dom_transfer">
|
||||
<form action="#">
|
||||
<div class="field">
|
||||
<label class="label">Your existing domain name</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text" placeholder="example.ygg" id="transfer_domain">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">Public key of new owner</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text" placeholder="3764ef954577a1815db3cc65aa3e2b18a52f12a3f6fcbd6a10d9ce8d06741ddd" id="transfer_domain_transfer_owner">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field is-grouped">
|
||||
<div class="control">
|
||||
<button class="button is-link" onclick="transferDomain();">Transfer domain</button>
|
||||
</div>
|
||||
<div class="control">
|
||||
<button class="button is-link is-light">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div> <!-- columns -->
|
||||
</div>
|
||||
|
||||
<div class="footer is-hidden">Some footer text is here</div>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,137 @@
|
||||
use crate::{Transaction, Block, Keystore, Key, Context};
|
||||
use std::sync::{Mutex, Arc};
|
||||
use crypto::digest::Digest;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use chrono::Utc;
|
||||
use num_bigint::BigUint;
|
||||
use num_traits::One;
|
||||
use crypto::sha2::Sha256;
|
||||
use std::thread;
|
||||
|
||||
pub struct Miner {
|
||||
context: Arc<Mutex<Context>>,
|
||||
keystore: Keystore,
|
||||
chain_id: u32,
|
||||
version: u32,
|
||||
transactions: Arc<Mutex<Vec<Transaction>>>,
|
||||
last_block: Option<Block>,
|
||||
running: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl Miner {
|
||||
pub fn new(context: Arc<Mutex<Context>>) -> Self {
|
||||
let c = context.lock().unwrap();
|
||||
Miner {
|
||||
context: context.clone(),
|
||||
keystore: c.keystore.clone(),
|
||||
chain_id: c.settings.chain_id,
|
||||
version: c.settings.version,
|
||||
transactions: Arc::new(Mutex::new(Vec::new())),
|
||||
last_block: c.blockchain.blocks.last().cloned(),
|
||||
running: Arc::new(AtomicBool::new(false)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_transaction(&mut self, transaction: Transaction) {
|
||||
self.transactions.lock().unwrap().push(transaction);
|
||||
}
|
||||
|
||||
pub fn stop(&mut self) {
|
||||
self.running.store(false, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
pub fn mine(&mut self) {
|
||||
let transaction = { self.transactions.lock().unwrap().first().cloned() };
|
||||
match transaction {
|
||||
Some(transaction) => {
|
||||
self.mine_internal(transaction);
|
||||
},
|
||||
None => {
|
||||
println!("Nothing to mine");
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_mining(&self) -> bool {
|
||||
self.running.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
fn mine_internal(&mut self, mut transaction: Transaction) {
|
||||
let mut last_block_time = 0i64;
|
||||
let block = {
|
||||
// Signing it with private key from Keystore
|
||||
let sign_hash = self.keystore.sign(&transaction.get_bytes());
|
||||
transaction.set_signature(Key::from_bytes(&sign_hash));
|
||||
|
||||
match &self.last_block {
|
||||
None => {
|
||||
// Creating a block with that signed transaction
|
||||
Block::new(0,Utc::now().timestamp(), self.chain_id, self.version, Key::zero32(), Some(transaction))
|
||||
},
|
||||
Some(block) => {
|
||||
last_block_time = block.timestamp;
|
||||
// Creating a block with that signed transaction
|
||||
Block::new(block.index + 1,Utc::now().timestamp(), self.chain_id, self.version, block.hash.clone(), Some(transaction))
|
||||
},
|
||||
}
|
||||
};
|
||||
//let blockchain = self.blockchain.clone();
|
||||
let transactions = self.transactions.clone();
|
||||
let running = self.running.clone();
|
||||
running.store(true, Ordering::Relaxed);
|
||||
let context = self.context.clone();
|
||||
thread::spawn(move || {
|
||||
match find_hash(&mut Sha256::new(), block, last_block_time, running.clone()) {
|
||||
None => {
|
||||
println!("Mining stopped");
|
||||
},
|
||||
Some(block) => {
|
||||
//blockchain.lock().unwrap().add_block(block);
|
||||
transactions.lock().unwrap().remove(0);
|
||||
running.store(false, Ordering::Relaxed);
|
||||
context.lock().unwrap().blockchain.add_block(block);
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn find_hash(digest: &mut dyn Digest, mut block: Block, prev_block_time: i64, running: Arc<AtomicBool>) -> Option<Block> {
|
||||
let mut buf: [u8; 32] = [0; 32];
|
||||
block.random = rand::random();
|
||||
let start_difficulty = block.difficulty;
|
||||
for nonce in 0..std::u64::MAX {
|
||||
if !running.load(Ordering::Relaxed) {
|
||||
return None;
|
||||
}
|
||||
block.timestamp = Utc::now().timestamp();
|
||||
block.nonce = nonce;
|
||||
// TODO uncomment for real run
|
||||
//block.difficulty = start_difficulty + get_time_difficulty(prev_block_time, block.timestamp);
|
||||
|
||||
digest.reset();
|
||||
digest.input(serde_json::to_string(&block).unwrap().as_bytes());
|
||||
digest.result(&mut buf);
|
||||
if hash_is_good(&buf, block.difficulty) {
|
||||
block.hash = Key::from_bytes(&buf);
|
||||
return Some(block);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn get_time_difficulty(prev_time: i64, now: i64) -> usize {
|
||||
let diff = now - prev_time;
|
||||
if diff < 900_000 {
|
||||
(900_000 as usize - diff as usize) / 60_000
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
fn hash_is_good(hash: &[u8], difficulty: usize) -> bool {
|
||||
let target = BigUint::one() << ((hash.len() << 3) - difficulty);
|
||||
let hash_int = BigUint::from_bytes_be(&hash);
|
||||
|
||||
return hash_int < target;
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
function openTab(element, tabName) {
|
||||
// Declare all variables
|
||||
var i, tabContent, tabLinks;
|
||||
|
||||
// Get all elements with class="content" and hide them
|
||||
tabContent = document.getElementsByClassName("content");
|
||||
for (i = 0; i < tabContent.length; i++) {
|
||||
tabContent[i].className = "context is-hidden";
|
||||
}
|
||||
|
||||
// Get all elements with class="tablinks" and remove the class "active"
|
||||
tabLinks = document.getElementsByClassName("is-active");
|
||||
for (i = 0; i < tabLinks.length; i++) {
|
||||
tabLinks[i].className = "";
|
||||
}
|
||||
|
||||
// Show the current tab, and add an "active" class to the button that opened the tab
|
||||
document.getElementById(tabName).className = "content";
|
||||
element.className = "is-active";
|
||||
}
|
||||
|
||||
function loadKey() {
|
||||
key_name = document.getElementById("load_key_name").value;
|
||||
key_pass = document.getElementById("load_key_password").value;
|
||||
external.invoke(JSON.stringify({cmd: 'loadKey', name: key_name, pass: key_pass}));
|
||||
}
|
||||
|
||||
function createKey() {
|
||||
key_name = document.getElementById("create_key_name").value;
|
||||
key_pass = document.getElementById("create_key_password").value;
|
||||
external.invoke(JSON.stringify({cmd: 'createKey', name: key_name, pass: key_pass}));
|
||||
}
|
||||
|
||||
function createDomain() {
|
||||
new_domain = document.getElementById("new_domain").value;
|
||||
new_dom_records = document.getElementById("new_domain_records").value;
|
||||
new_dom_tags = document.getElementById("new_domain_tags").value;
|
||||
external.invoke(JSON.stringify({cmd: 'createDomain', name: new_domain, records: new_dom_records, tags: new_dom_tags}));
|
||||
}
|
||||
|
||||
function changeDomain() {
|
||||
domain = document.getElementById("change_domain").value;
|
||||
dom_records = document.getElementById("change_domain_records").value;
|
||||
dom_tags = document.getElementById("change_domain_records").value;
|
||||
external.invoke(JSON.stringify({cmd: 'changeDomain', name: domain, records: dom_records, tags: dom_tags}));
|
||||
}
|
||||
|
||||
function renewDomain() {
|
||||
domain = document.getElementById("renew_domain").value;
|
||||
days = document.getElementById("renew_domain_extend_days").value;
|
||||
external.invoke(JSON.stringify({cmd: 'renewDomain', name: domain, days: days}));
|
||||
}
|
||||
|
||||
function transferDomain() {
|
||||
domain = document.getElementById("transfer_domain").value;
|
||||
new_owner = document.getElementById("transfer_domain_transfer_owner").value;
|
||||
external.invoke(JSON.stringify({cmd: 'transferDomain', name: domain, owner: new_owner}));
|
||||
}
|
||||
|
||||
function sendAction(param) {
|
||||
external.invoke(JSON.stringify(param));
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
|
||||
/// Signal to a serializable object how much of its data should be serialized
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum SerializationMode {
|
||||
/// Serialize everything sufficiently to fully reconstruct the object
|
||||
Full,
|
||||
/// Serialize the data that defines the object
|
||||
Hash,
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
html {
|
||||
background: #202020;
|
||||
background-attachment: fixed;
|
||||
}
|
||||
html, body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: linear-gradient(0deg, #404040, #090909);
|
||||
background-attachment: fixed;
|
||||
overflow: hidden;
|
||||
}
|
||||
h1, h2, h3 { color: #dddddd; }
|
||||
p { color: #dddddd; }
|
||||
a { color: #dddddd; }
|
||||
/* Style the tab */
|
||||
.tab {
|
||||
overflow: hidden;
|
||||
border: 0px;
|
||||
background: linear-gradient(180deg, #404040, #202020);
|
||||
}
|
||||
|
||||
/* Style the buttons that are used to open the tab content */
|
||||
.tab button {
|
||||
background: inherit;
|
||||
color: #dddddd;
|
||||
float: left;
|
||||
border: none;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
padding: 8px 16px;
|
||||
transition: 0.3s;
|
||||
}
|
||||
|
||||
/* Change background color of buttons on hover */
|
||||
.tab button:hover {
|
||||
background: linear-gradient(180deg, #808080, #202020);
|
||||
}
|
||||
|
||||
/* Create an active/current tablink class */
|
||||
.tab button.active {
|
||||
background: linear-gradient(180deg, #606060, #202020);
|
||||
}
|
||||
|
||||
/* Style the tab content */
|
||||
.tabcontent {
|
||||
display: none;
|
||||
padding: 12px 12px;
|
||||
border: 0px;
|
||||
border-top: none;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
button {
|
||||
border: none;
|
||||
outline: none;
|
||||
color: #dddddd;
|
||||
padding: 8px 8px;
|
||||
background: linear-gradient(180deg, #404040, #202020);
|
||||
}
|
||||
|
||||
.tabcontent button {
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.footer {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
background-color: #404040;
|
||||
color: #dddddd;
|
||||
padding: 6px 12px;
|
||||
}
|
Loading…
Reference in New Issue