Implemented and added usage of eventbus. Added a lot of UI interaction. Added a lot of DB work.
This commit is contained in:
+1
-1
@@ -22,7 +22,7 @@ base64 = "0.11.0"
|
|||||||
chrono = "0.4.9"
|
chrono = "0.4.9"
|
||||||
rand = "0.7.2"
|
rand = "0.7.2"
|
||||||
sqlite = "0.25.3"
|
sqlite = "0.25.3"
|
||||||
eventbus = "0.5.1"
|
uuid = { version = "0.8.2", features = ["serde", "v4"] }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
winres = "0.1"
|
winres = "0.1"
|
||||||
|
|||||||
+93
-35
@@ -1,4 +1,4 @@
|
|||||||
use crate::{Block, Transaction, Bytes};
|
use crate::{Block, Transaction, Bytes, Keystore};
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use sqlite::{Connection, State, Readable, Statement, Error};
|
use sqlite::{Connection, State, Readable, Statement, Error};
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ impl Blockchain {
|
|||||||
match self.db.prepare("SELECT * FROM blocks ORDER BY id DESC LIMIT 1;") {
|
match self.db.prepare("SELECT * FROM blocks ORDER BY id DESC LIMIT 1;") {
|
||||||
Ok(mut statement) => {
|
Ok(mut statement) => {
|
||||||
while statement.next().unwrap() == State::Row {
|
while statement.next().unwrap() == State::Row {
|
||||||
match Self::get_block(&mut statement) {
|
match Self::get_block_from_statement(&mut statement) {
|
||||||
None => { println!("Something wrong with block in DB!"); }
|
None => { println!("Something wrong with block in DB!"); }
|
||||||
Some(block) => {
|
Some(block) => {
|
||||||
println!("Loaded last block: {:?}", &block);
|
println!("Loaded last block: {:?}", &block);
|
||||||
@@ -52,56 +52,100 @@ impl Blockchain {
|
|||||||
'prev_block_hash' BINARY,
|
'prev_block_hash' BINARY,
|
||||||
'hash' BINARY
|
'hash' BINARY
|
||||||
);
|
);
|
||||||
CREATE INDEX block_index ON blocks (id);"
|
CREATE INDEX block_index ON blocks (id);
|
||||||
|
CREATE TABLE transactions (id INTEGER PRIMARY KEY AUTOINCREMENT, identity BINARY, method TEXT, data TEXT, pub_key BINARY, signature BINARY);
|
||||||
|
CREATE INDEX ids ON transactions (identity);"
|
||||||
).expect("Error creating blocks table");
|
).expect("Error creating blocks table");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_block(statement: &mut Statement) -> Option<Block> {
|
|
||||||
let index = statement.read::<i64>(0).unwrap() as u64;
|
|
||||||
let timestamp = statement.read::<i64>(1).unwrap();
|
|
||||||
let chain_name = statement.read::<String>(2).unwrap();
|
|
||||||
let version_flags = statement.read::<i64>(3).unwrap() as u32;
|
|
||||||
let difficulty = statement.read::<i64>(4).unwrap() as usize;
|
|
||||||
let random = statement.read::<i64>(5).unwrap() as u32;
|
|
||||||
let nonce = statement.read::<i64>(6).unwrap() as u64;
|
|
||||||
let transaction = Transaction::from_json(&statement.read::<String>(7).unwrap());
|
|
||||||
let prev_block_hash = Bytes::from_bytes(statement.read::<Vec<u8>>(8).unwrap().as_slice());
|
|
||||||
let hash = Bytes::from_bytes(statement.read::<Vec<u8>>(9).unwrap().as_slice());
|
|
||||||
Some(Block::from_all_params(index, timestamp, &chain_name, version_flags, difficulty, random, nonce, prev_block_hash, hash, transaction))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_block(&mut self, block: Block) {
|
pub fn add_block(&mut self, block: Block) {
|
||||||
if self.check_block(&block, &self.last_block) {
|
if self.check_block(&block, &self.last_block) {
|
||||||
println!("Adding block:\n{:?}", &block);
|
println!("Adding block:\n{:?}", &block);
|
||||||
self.blocks.push(block.clone());
|
self.blocks.push(block.clone());
|
||||||
self.last_block = Some(block.clone());
|
self.last_block = Some(block.clone());
|
||||||
|
let transaction = block.transaction.clone();
|
||||||
|
|
||||||
// Adding block to DB
|
{
|
||||||
let mut statement = self.db.prepare("INSERT INTO blocks (\
|
// Adding block to DB
|
||||||
id, timestamp, chain_name, version_flags, difficulty,\
|
let mut statement = self.db.prepare("INSERT INTO blocks (\
|
||||||
random, nonce, 'transaction', prev_block_hash, hash)\
|
id, timestamp, chain_name, version_flags, difficulty,\
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)").unwrap();
|
random, nonce, 'transaction', prev_block_hash, hash)\
|
||||||
statement.bind(1, block.index as i64);
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);").unwrap();
|
||||||
statement.bind(2, block.timestamp as i64);
|
statement.bind(1, block.index as i64);
|
||||||
statement.bind(3, &*block.chain_name);
|
statement.bind(2, block.timestamp as i64);
|
||||||
statement.bind(4, block.version_flags as i64);
|
statement.bind(3, block.chain_name.as_ref() as &str);
|
||||||
statement.bind(5, block.difficulty as i64);
|
statement.bind(4, block.version_flags as i64);
|
||||||
statement.bind(6, block.random as i64);
|
statement.bind(5, block.difficulty as i64);
|
||||||
statement.bind(7, block.nonce as i64);
|
statement.bind(6, block.random as i64);
|
||||||
match block.transaction {
|
statement.bind(7, block.nonce as i64);
|
||||||
None => { statement.bind(8, ""); }
|
match &transaction {
|
||||||
Some(transaction) => { statement.bind(8, &*transaction.to_string()); }
|
None => { statement.bind(8, ""); }
|
||||||
|
Some(transaction) => {
|
||||||
|
statement.bind(8, transaction.to_string().as_ref() as &str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
statement.bind(9, block.prev_block_hash.as_bytes());
|
||||||
|
statement.bind(10, block.hash.as_bytes());
|
||||||
|
statement.next().expect("Error adding block to DB");
|
||||||
|
}
|
||||||
|
|
||||||
|
match &transaction {
|
||||||
|
None => {}
|
||||||
|
Some(transaction) => {
|
||||||
|
self.add_transaction(transaction);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
statement.bind(9, block.prev_block_hash.as_bytes());
|
|
||||||
statement.bind(10, block.hash.as_bytes());
|
|
||||||
statement.next().expect("Error adding block to DB");
|
|
||||||
} else {
|
} else {
|
||||||
println!("Bad block found, ignoring:\n{:?}", &block);
|
println!("Bad block found, ignoring:\n{:?}", &block);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn add_transaction(&mut self, t: &Transaction) {
|
||||||
|
let mut statement = self.db.prepare("INSERT INTO transactions (identity, method, data, pub_key, signature) VALUES (?, ?, ?, ?, ?)").unwrap();
|
||||||
|
statement.bind(1, t.identity.as_bytes());
|
||||||
|
statement.bind(2, t.method.as_ref() as &str);
|
||||||
|
statement.bind(3, t.data.as_ref() as &str);
|
||||||
|
statement.bind(4, t.pub_key.as_bytes());
|
||||||
|
statement.bind(5, t.signature.as_bytes());
|
||||||
|
statement.next().expect("Error adding transaction to DB");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_domain_available(&self, domain: &str, keystore: &Keystore) -> bool {
|
||||||
|
if domain.is_empty() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let identity_hash = Transaction::hash_identity(domain);
|
||||||
|
let mut statement = self.db.prepare("SELECT pub_key FROM transactions WHERE identity = ? ORDER BY id DESC LIMIT 1;").unwrap();
|
||||||
|
statement.bind(1, identity_hash.as_bytes());
|
||||||
|
while let State::Row = statement.next().unwrap() {
|
||||||
|
let pub_key = Bytes::from_bytes(statement.read::<Vec<u8>>(0).unwrap().as_slice());
|
||||||
|
if !pub_key.eq(&keystore.get_public()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let parts: Vec<&str> = domain.rsplitn(2, ".").collect();
|
||||||
|
if parts.len() > 1 {
|
||||||
|
// We do not support third level domains
|
||||||
|
if parts.last().unwrap().contains(".") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Checking for available zone, for this domain
|
||||||
|
let identity_hash = Transaction::hash_identity(parts.first().unwrap());
|
||||||
|
let mut statement = self.db.prepare("SELECT identity FROM transactions WHERE identity = ? ORDER BY id DESC LIMIT 1;").unwrap();
|
||||||
|
statement.bind(1, identity_hash.as_bytes());
|
||||||
|
while let State::Row = statement.next().unwrap() {
|
||||||
|
// If there is such a zone
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_last_block(&self) -> Option<Block> {
|
pub fn get_last_block(&self) -> Option<Block> {
|
||||||
self.last_block.clone()
|
self.last_block.clone()
|
||||||
}
|
}
|
||||||
@@ -129,6 +173,20 @@ impl Blockchain {
|
|||||||
return block.prev_block_hash == prev_block.as_ref().unwrap().hash;
|
return block.prev_block_hash == prev_block.as_ref().unwrap().hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_block_from_statement(statement: &mut Statement) -> Option<Block> {
|
||||||
|
let index = statement.read::<i64>(0).unwrap() as u64;
|
||||||
|
let timestamp = statement.read::<i64>(1).unwrap();
|
||||||
|
let chain_name = statement.read::<String>(2).unwrap();
|
||||||
|
let version_flags = statement.read::<i64>(3).unwrap() as u32;
|
||||||
|
let difficulty = statement.read::<i64>(4).unwrap() as usize;
|
||||||
|
let random = statement.read::<i64>(5).unwrap() as u32;
|
||||||
|
let nonce = statement.read::<i64>(6).unwrap() as u64;
|
||||||
|
let transaction = Transaction::from_json(&statement.read::<String>(7).unwrap());
|
||||||
|
let prev_block_hash = Bytes::from_bytes(statement.read::<Vec<u8>>(8).unwrap().as_slice());
|
||||||
|
let hash = Bytes::from_bytes(statement.read::<Vec<u8>>(9).unwrap().as_slice());
|
||||||
|
Some(Block::from_all_params(index, timestamp, &chain_name, version_flags, difficulty, random, nonce, prev_block_hash, hash, transaction))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn check_block_hash(block: &Block) -> bool {
|
pub fn check_block_hash(block: &Block) -> bool {
|
||||||
// We need to clear Hash value to rehash it without it for check :(
|
// We need to clear Hash value to rehash it without it for check :(
|
||||||
let mut copy: Block = block.clone();
|
let mut copy: Block = block.clone();
|
||||||
|
|||||||
Vendored
+5
@@ -10830,3 +10830,8 @@ label.panel-block:hover {
|
|||||||
padding: 3rem 1.5rem 6rem;
|
padding: 3rem 1.5rem 6rem;
|
||||||
}
|
}
|
||||||
/*# sourceMappingURL=bulma.css.map */
|
/*# sourceMappingURL=bulma.css.map */
|
||||||
|
|
||||||
|
// TODO move to another file
|
||||||
|
.container {
|
||||||
|
margin: 10pt;
|
||||||
|
}
|
||||||
+4
-2
@@ -1,4 +1,5 @@
|
|||||||
use crate::{Keystore, Blockchain};
|
use crate::{Keystore, Blockchain, Bus};
|
||||||
|
use crate::event::Event;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use serde::{Serialize, Deserialize, Serializer, Deserializer};
|
use serde::{Serialize, Deserialize, Serializer, Deserializer};
|
||||||
use serde::de::Error;
|
use serde::de::Error;
|
||||||
@@ -9,12 +10,13 @@ pub struct Context {
|
|||||||
pub(crate) settings: Settings,
|
pub(crate) settings: Settings,
|
||||||
pub(crate) keystore: Keystore,
|
pub(crate) keystore: Keystore,
|
||||||
pub(crate) blockchain: Blockchain,
|
pub(crate) blockchain: Blockchain,
|
||||||
|
pub bus: Bus<Event>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Context {
|
impl Context {
|
||||||
/// Creating an essential context to work with
|
/// Creating an essential context to work with
|
||||||
pub fn new(settings: Settings, keystore: Keystore, blockchain: Blockchain) -> Context {
|
pub fn new(settings: Settings, keystore: Keystore, blockchain: Blockchain) -> Context {
|
||||||
Context { settings, keystore, blockchain }
|
Context { settings, keystore, blockchain, bus: Bus::new() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Load keystore and return Context
|
/// Load keystore and return Context
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
|
pub enum Event {
|
||||||
|
MinerStarted,
|
||||||
|
MinerStopped,
|
||||||
|
KeyGeneratorStarted,
|
||||||
|
KeyGeneratorStopped,
|
||||||
|
NewBlockReceived,
|
||||||
|
BlockchainChanged,
|
||||||
|
ActionStopMining,
|
||||||
|
}
|
||||||
+22
-4
@@ -8,9 +8,27 @@
|
|||||||
<script defer src="https://use.fontawesome.com/releases/v5.3.1/js/all.js"></script>
|
<script defer src="https://use.fontawesome.com/releases/v5.3.1/js/all.js"></script>
|
||||||
{scripts}
|
{scripts}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body onload="onLoad();">
|
||||||
|
<div class="mining_indicator" id="mining_indicator" onclick="miningIndicatorClick(this)">
|
||||||
|
<span></span>
|
||||||
|
<span></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="modal_dialog" class="modal">
|
||||||
|
<div class="modal-background"></div>
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="box">
|
||||||
|
<p id="modal_text" class="is-centered">Do you really want to cancel mining?</p>
|
||||||
|
<br/>
|
||||||
|
<div class="buttons is-grouped is-centered">
|
||||||
|
<button class="button is-link" id="modal_positive_button" onclick="positiveButton();">Ok</button>
|
||||||
|
<button class="button is-link is-light" id="modal_negative_button" onclick="cancelButton();">Cancel</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<br/>
|
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column is-one-fifth">
|
<div class="column is-one-fifth">
|
||||||
<div class="menu">
|
<div class="menu">
|
||||||
@@ -134,7 +152,7 @@
|
|||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label">New domain name</label>
|
<label class="label">New domain name</label>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input class="input" type="text" placeholder="example.ygg" id="new_domain">
|
<input class="input" type="text" placeholder="example.ygg" id="new_domain" oninput="onDomainChange(this)">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -154,7 +172,7 @@
|
|||||||
|
|
||||||
<div class="field is-grouped">
|
<div class="field is-grouped">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<button class="button is-link" onclick="createDomain();">Create domain</button>
|
<button class="button is-link" id="new_domain_button" onclick="createDomain();" disabled>Create domain</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -6,10 +6,14 @@ pub mod transaction;
|
|||||||
pub use crate::transaction::Transaction;
|
pub use crate::transaction::Transaction;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
pub use crate::utils::*;
|
pub use crate::utils::*;
|
||||||
|
pub mod simplebus;
|
||||||
|
pub use crate::simplebus::*;
|
||||||
pub mod keys;
|
pub mod keys;
|
||||||
pub use crate::keys::Keystore;
|
pub use crate::keys::Keystore;
|
||||||
pub use crate::keys::Bytes;
|
pub use crate::keys::Bytes;
|
||||||
pub mod miner;
|
pub mod miner;
|
||||||
pub mod context;
|
pub mod context;
|
||||||
|
pub mod event;
|
||||||
|
|
||||||
pub use crate::context::Context;
|
pub use crate::context::Context;
|
||||||
pub use crate::context::Settings;
|
pub use crate::context::Settings;
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
.mining_indicator {
|
||||||
|
position:absolute;
|
||||||
|
right:10px;
|
||||||
|
width:30px;
|
||||||
|
height:30px;
|
||||||
|
display:inline-block;
|
||||||
|
padding:0px;
|
||||||
|
text-align:right;
|
||||||
|
float:right;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
.mining_indicator span {
|
||||||
|
position:absolute;
|
||||||
|
display:inline-block;
|
||||||
|
width:30px;
|
||||||
|
height:30px;
|
||||||
|
border-radius:100%;
|
||||||
|
background:#3273dc;
|
||||||
|
-webkit-animation:mining_indicator 1.6s linear infinite;
|
||||||
|
animation:mining_indicator 1.6s linear infinite;
|
||||||
|
}
|
||||||
|
.mining_indicator span:last-child {
|
||||||
|
animation-delay:-0.8s;
|
||||||
|
-webkit-animation-delay:-0.8s;
|
||||||
|
}
|
||||||
|
@keyframes mining_indicator {
|
||||||
|
0% {transform: scale(0, 0);opacity:0.5;}
|
||||||
|
100% {transform: scale(1, 1);opacity:0;}
|
||||||
|
}
|
||||||
|
@-webkit-keyframes mining_indicator {
|
||||||
|
0% {-webkit-transform: scale(0, 0);opacity:0.5;}
|
||||||
|
100% {-webkit-transform: scale(1, 1);opacity:0;}
|
||||||
|
}
|
||||||
+62
-15
@@ -1,17 +1,20 @@
|
|||||||
#![windows_subsystem = "windows"]
|
#![windows_subsystem = "windows"]
|
||||||
extern crate web_view;
|
extern crate web_view;
|
||||||
use alfis::{Blockchain, Block, Transaction, Keystore, Bytes, Settings, Context};
|
|
||||||
use alfis::miner::Miner;
|
|
||||||
use web_view::*;
|
|
||||||
use std::thread;
|
|
||||||
use rand::{Rng, RngCore};
|
|
||||||
|
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
extern crate serde;
|
|
||||||
use serde::{Serialize, Deserialize};
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
|
use rand::{Rng, RngCore};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use web_view::*;
|
||||||
|
|
||||||
|
use alfis::{Block, Blockchain, Bytes, Context, Keystore, Settings, Transaction};
|
||||||
|
use alfis::event::Event;
|
||||||
|
use alfis::miner::Miner;
|
||||||
|
|
||||||
|
extern crate serde;
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
|
|
||||||
const ONE_YEAR: u16 = 365;
|
const ONE_YEAR: u16 = 365;
|
||||||
@@ -49,34 +52,61 @@ fn create_genesis_if_needed(context: &Arc<Mutex<Context>>, miner: &Arc<Mutex<Min
|
|||||||
|
|
||||||
fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
|
fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
|
||||||
let file_content = include_str!("index.html");
|
let file_content = include_str!("index.html");
|
||||||
let styles = inline_style(include_str!("bulma.css"));
|
let mut styles= inline_style(include_str!("bulma.css"));
|
||||||
|
styles.push_str(&inline_style(include_str!("loader.css")));
|
||||||
let scripts = inline_script(include_str!("scripts.js"));
|
let scripts = inline_script(include_str!("scripts.js"));
|
||||||
|
|
||||||
|
let html = Content::Html(file_content.to_owned().replace("{styles}", &styles).replace("{scripts}", &scripts));
|
||||||
web_view::builder()
|
web_view::builder()
|
||||||
.title("ALFIS 0.1.0")
|
.title("ALFIS 0.1.0")
|
||||||
.content(Content::Html(file_content.to_owned().replace("{styles}", &styles).replace("{scripts}", &scripts)))
|
.content(html)
|
||||||
.size(1024, 720)
|
.size(1024, 720)
|
||||||
.resizable(true)
|
.resizable(true)
|
||||||
.debug(true)
|
.debug(true)
|
||||||
.user_data(())
|
.user_data(())
|
||||||
.invoke_handler(|_web_view, arg| {
|
.invoke_handler(|web_view, arg| {
|
||||||
use Cmd::*;
|
use Cmd::*;
|
||||||
println!("Command {}", arg);
|
println!("Command {}", arg);
|
||||||
match serde_json::from_str(arg).unwrap() {
|
match serde_json::from_str(arg).unwrap() {
|
||||||
|
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
|
||||||
|
});
|
||||||
|
}
|
||||||
LoadKey { name, pass } => {
|
LoadKey { name, pass } => {
|
||||||
match Keystore::from_file(&name, &pass) {
|
match Keystore::from_file(&name, &pass) {
|
||||||
None => {
|
None => {
|
||||||
println!("Error loading keystore '{}'!", &name);
|
println!("Error loading keystore '{}'!", &name);
|
||||||
},
|
},
|
||||||
Some(k) => {
|
Some(keystore) => {
|
||||||
let mut c = context.lock().unwrap();
|
let mut c = context.lock().unwrap();
|
||||||
c.set_keystore(k);
|
c.set_keystore(keystore);
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
CreateKey { name, pass } => {
|
CreateKey { name, pass } => {
|
||||||
create_key(context.clone(), &name, &pass);
|
create_key(context.clone(), &name, &pass);
|
||||||
}
|
}
|
||||||
|
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));
|
||||||
|
}
|
||||||
CreateDomain { name, records, tags } => {
|
CreateDomain { name, records, tags } => {
|
||||||
let keystore = {
|
let keystore = {
|
||||||
let mut guard = context.lock().unwrap();
|
let mut guard = context.lock().unwrap();
|
||||||
@@ -90,6 +120,9 @@ fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
|
|||||||
}
|
}
|
||||||
RenewDomain { name, days } => {}
|
RenewDomain { name, days } => {}
|
||||||
TransferDomain { name, owner } => {}
|
TransferDomain { name, owner } => {}
|
||||||
|
StopMining => {
|
||||||
|
context.lock().unwrap().bus.post(Event::ActionStopMining);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//dbg!(&signature);
|
//dbg!(&signature);
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -131,21 +164,32 @@ fn create_key(context: Arc<Mutex<Context>>, filename: &str, password: &str) {
|
|||||||
let mut mining = Arc::new(AtomicBool::new(true));
|
let mut mining = Arc::new(AtomicBool::new(true));
|
||||||
for _ in 0..num_cpus::get() {
|
for _ in 0..num_cpus::get() {
|
||||||
let context = context.clone();
|
let context = context.clone();
|
||||||
|
{ context.lock().unwrap().bus.post(Event::KeyGeneratorStarted); }
|
||||||
let filename= filename.to_owned();
|
let filename= filename.to_owned();
|
||||||
let password= password.to_owned();
|
let password= password.to_owned();
|
||||||
let mining = mining.clone();
|
let mining = mining.clone();
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
match generate_key(KEYSTORE_DIFFICULTY, mining.clone()) {
|
match generate_key(KEYSTORE_DIFFICULTY, mining.clone()) {
|
||||||
None => { println!("Keystore mining finished"); }
|
None => {
|
||||||
|
println!("Keystore mining finished");
|
||||||
|
context.lock().unwrap().bus.post(Event::KeyGeneratorStopped);
|
||||||
|
}
|
||||||
Some(keystore) => {
|
Some(keystore) => {
|
||||||
let mut c = context.lock().unwrap();
|
let mut c = context.lock().unwrap();
|
||||||
mining.store(false,Ordering::Relaxed);
|
mining.store(false,Ordering::Relaxed);
|
||||||
keystore.save(&filename, &password);
|
keystore.save(&filename, &password);
|
||||||
c.set_keystore(keystore);
|
c.set_keystore(keystore);
|
||||||
|
c.bus.post(Event::KeyGeneratorStopped);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
context.lock().unwrap().bus.register(move |_uuid, e| {
|
||||||
|
if e == Event::ActionStopMining {
|
||||||
|
mining.store(false, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
false
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_key(difficulty: usize, mining: Arc<AtomicBool>) -> Option<Keystore> {
|
fn generate_key(difficulty: usize, mining: Arc<AtomicBool>) -> Option<Keystore> {
|
||||||
@@ -168,12 +212,15 @@ fn generate_key(difficulty: usize, mining: Arc<AtomicBool>) -> Option<Keystore>
|
|||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(tag = "cmd", rename_all = "camelCase")]
|
#[serde(tag = "cmd", rename_all = "camelCase")]
|
||||||
pub enum Cmd {
|
pub enum Cmd {
|
||||||
|
Loaded,
|
||||||
LoadKey{name: String, pass: String},
|
LoadKey{name: String, pass: String},
|
||||||
CreateKey{name: String, pass: String},
|
CreateKey{name: String, pass: String},
|
||||||
|
CheckDomain{name: String},
|
||||||
CreateDomain{name: String, records: String, tags: String},
|
CreateDomain{name: String, records: String, tags: String},
|
||||||
ChangeDomain{name: String, records: String, tags: String},
|
ChangeDomain{name: String, records: String, tags: String},
|
||||||
RenewDomain{name: String, days: u16},
|
RenewDomain{name: String, days: u16},
|
||||||
TransferDomain{name: String, owner: String},
|
TransferDomain{name: String, owner: String},
|
||||||
|
StopMining,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn inline_style(s: &str) -> String {
|
fn inline_style(s: &str) -> String {
|
||||||
|
|||||||
+21
-8
@@ -1,13 +1,16 @@
|
|||||||
use crate::{Transaction, Block, Keystore, Bytes, Context, hash_is_good};
|
use std::sync::{Arc, Condvar, Mutex};
|
||||||
use std::sync::{Mutex, Arc, Condvar};
|
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
|
||||||
use crypto::digest::Digest;
|
|
||||||
use std::sync::atomic::{AtomicBool, Ordering, AtomicU32};
|
|
||||||
use chrono::Utc;
|
|
||||||
use crypto::sha2::Sha256;
|
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use chrono::Utc;
|
||||||
|
use crypto::digest::Digest;
|
||||||
|
use crypto::sha2::Sha256;
|
||||||
use num_cpus;
|
use num_cpus;
|
||||||
|
|
||||||
|
use crate::{Block, Bytes, Context, hash_is_good, Keystore, Transaction};
|
||||||
|
use crate::event::Event;
|
||||||
|
|
||||||
pub struct Miner {
|
pub struct Miner {
|
||||||
context: Arc<Mutex<Context>>,
|
context: Arc<Mutex<Context>>,
|
||||||
keystore: Keystore,
|
keystore: Keystore,
|
||||||
@@ -75,6 +78,13 @@ impl Miner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
let mining = self.mining.clone();
|
||||||
|
self.context.lock().unwrap().bus.register(move |uuid, e| {
|
||||||
|
if e == Event::ActionStopMining {
|
||||||
|
mining.store(false, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
false
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_mining(&self) -> bool {
|
pub fn is_mining(&self) -> bool {
|
||||||
@@ -86,7 +96,8 @@ impl Miner {
|
|||||||
let mut chain_name= String::new();
|
let mut chain_name= String::new();
|
||||||
let mut version_flags= 0u32;
|
let mut version_flags= 0u32;
|
||||||
{
|
{
|
||||||
let c = context.lock().unwrap();
|
let mut c = context.lock().unwrap();
|
||||||
|
c.bus.post(Event::MinerStarted);
|
||||||
chain_name = c.settings.chain_name.clone();
|
chain_name = c.settings.chain_name.clone();
|
||||||
version_flags = c.settings.version_flags;
|
version_flags = c.settings.version_flags;
|
||||||
}
|
}
|
||||||
@@ -142,7 +153,9 @@ impl Miner {
|
|||||||
},
|
},
|
||||||
Some(block) => {
|
Some(block) => {
|
||||||
count = live_threads.fetch_sub(1, Ordering::Relaxed);
|
count = live_threads.fetch_sub(1, Ordering::Relaxed);
|
||||||
context.lock().unwrap().blockchain.add_block(block);
|
let mut context = context.lock().unwrap();
|
||||||
|
context.blockchain.add_block(block);
|
||||||
|
context.bus.post(Event::MinerStopped);
|
||||||
mining.store(false, Ordering::Relaxed);
|
mining.store(false, Ordering::Relaxed);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
function onLoad() {
|
||||||
|
external.invoke(JSON.stringify({cmd: 'loaded'}));
|
||||||
|
}
|
||||||
|
|
||||||
function openTab(element, tabName) {
|
function openTab(element, tabName) {
|
||||||
// Declare all variables
|
// Declare all variables
|
||||||
var i, tabContent, tabLinks;
|
var i, tabContent, tabLinks;
|
||||||
@@ -60,3 +64,55 @@ function transferDomain() {
|
|||||||
function sendAction(param) {
|
function sendAction(param) {
|
||||||
external.invoke(JSON.stringify(param));
|
external.invoke(JSON.stringify(param));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onDomainChange(element) {
|
||||||
|
external.invoke(JSON.stringify({cmd: 'checkDomain', name: element.value}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function domainAvailable(available) {
|
||||||
|
input = document.getElementById("new_domain");
|
||||||
|
button = document.getElementById("new_domain_button");
|
||||||
|
if (available) {
|
||||||
|
input.className = "input";
|
||||||
|
button.disabled = false
|
||||||
|
} else {
|
||||||
|
input.className = "input is-danger";
|
||||||
|
button.disabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showModalDialog(text, callback) {
|
||||||
|
message = document.getElementById("modal_text");
|
||||||
|
message.textContent = text;
|
||||||
|
|
||||||
|
button_positive = document.getElementById("modal_positive_button");
|
||||||
|
button_positive.onclick = function() {
|
||||||
|
callback();
|
||||||
|
dialog = document.getElementById("modal_dialog");
|
||||||
|
dialog.className = "modal";
|
||||||
|
};
|
||||||
|
|
||||||
|
button_negative = document.getElementById("modal_negative_button");
|
||||||
|
button_negative.onclick = function() {
|
||||||
|
dialog = document.getElementById("modal_dialog");
|
||||||
|
dialog.className = "modal";
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog = document.getElementById("modal_dialog");
|
||||||
|
dialog.className = "modal is-active";
|
||||||
|
}
|
||||||
|
|
||||||
|
function showMiningIndicator(visible) {
|
||||||
|
indicator = document.getElementById("mining_indicator");
|
||||||
|
if (visible) {
|
||||||
|
indicator.style.visibility = 'visible';
|
||||||
|
} else {
|
||||||
|
indicator.style.visibility = 'hidden';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function miningIndicatorClick(element) {
|
||||||
|
showModalDialog("Do you really want to stop mining?", function() {
|
||||||
|
external.invoke(JSON.stringify({cmd: 'stopMining'}));
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
use crate::event::Event;
|
||||||
|
use uuid::Uuid;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
pub struct Bus<T> {
|
||||||
|
listeners: HashMap<Uuid, Box<dyn FnMut(&Uuid, T) -> bool + Send + Sync>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Clone> Bus<T> {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Bus { listeners: HashMap::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register<F>(&mut self, mut closure: F) -> Uuid where F: FnMut(&Uuid, T) -> bool + Send + Sync + 'static {
|
||||||
|
let uuid = Uuid::new_v4();
|
||||||
|
self.listeners.insert(uuid.clone(), Box::new(closure));
|
||||||
|
uuid
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unregister(&mut self, uuid: &Uuid) {
|
||||||
|
self.listeners.remove(&uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn post(&mut self, event: T) {
|
||||||
|
self.listeners.retain(|uuid, closure| {
|
||||||
|
closure(uuid, event.clone())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod tests {
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
use std::sync::atomic::{AtomicI32, Ordering};
|
||||||
|
use std::thread;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use crate::Bus;
|
||||||
|
use crate::event::Event;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test1() {
|
||||||
|
let mut string = Arc::new(Mutex::new(String::from("start")));
|
||||||
|
let mut bus = Arc::new(Mutex::new(Bus::new()));
|
||||||
|
let string_copy = string.clone();
|
||||||
|
{
|
||||||
|
bus.lock().unwrap().register(move |_uuid, e| {
|
||||||
|
println!("Event {:?} received!", e);
|
||||||
|
let mut copy = string_copy.lock().unwrap();
|
||||||
|
copy.clear();
|
||||||
|
copy.push_str("from thread");
|
||||||
|
false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let bus2 = bus.clone();
|
||||||
|
thread::spawn(move || {
|
||||||
|
bus2.lock().unwrap().post(Event::BlockchainChanged);
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut guard = string.lock().unwrap();
|
||||||
|
thread::sleep(Duration::from_millis(100));
|
||||||
|
println!("string = {}", &guard);
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
-5
@@ -20,11 +20,8 @@ pub struct Transaction {
|
|||||||
|
|
||||||
impl Transaction {
|
impl Transaction {
|
||||||
pub fn from_str(identity: String, method: String, data: String, pub_key: Bytes) -> Self {
|
pub fn from_str(identity: String, method: String, data: String, pub_key: Bytes) -> Self {
|
||||||
let mut buf: [u8; 32] = [0; 32];
|
let bytes = Self::hash_identity(&identity);
|
||||||
let mut digest = Sha256::new();
|
return Self::new(bytes, method, data, pub_key);
|
||||||
digest.input_str(&identity);
|
|
||||||
digest.result(&mut buf);
|
|
||||||
return Self::new(Bytes::from_bytes(&buf), method, data, pub_key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(identity: Bytes, method: String, data: String, pub_key: Bytes) -> Self {
|
pub fn new(identity: Bytes, method: String, data: String, pub_key: Bytes) -> Self {
|
||||||
@@ -51,6 +48,14 @@ impl Transaction {
|
|||||||
// Let it panic if something is not okay
|
// Let it panic if something is not okay
|
||||||
serde_json::to_string(&self).unwrap()
|
serde_json::to_string(&self).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn hash_identity(identity: &str) -> Bytes {
|
||||||
|
let mut buf: [u8; 32] = [0; 32];
|
||||||
|
let mut digest = Sha256::new();
|
||||||
|
digest.input_str(identity);
|
||||||
|
digest.result(&mut buf);
|
||||||
|
Bytes::from_bytes(&buf)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Transaction {
|
impl fmt::Debug for Transaction {
|
||||||
|
|||||||
Reference in New Issue
Block a user