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"
|
||||
rand = "0.7.2"
|
||||
sqlite = "0.25.3"
|
||||
eventbus = "0.5.1"
|
||||
uuid = { version = "0.8.2", features = ["serde", "v4"] }
|
||||
|
||||
[build-dependencies]
|
||||
winres = "0.1"
|
||||
|
||||
+93
-35
@@ -1,4 +1,4 @@
|
||||
use crate::{Block, Transaction, Bytes};
|
||||
use crate::{Block, Transaction, Bytes, Keystore};
|
||||
use chrono::Utc;
|
||||
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;") {
|
||||
Ok(mut statement) => {
|
||||
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!"); }
|
||||
Some(block) => {
|
||||
println!("Loaded last block: {:?}", &block);
|
||||
@@ -52,56 +52,100 @@ impl Blockchain {
|
||||
'prev_block_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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
if self.check_block(&block, &self.last_block) {
|
||||
println!("Adding block:\n{:?}", &block);
|
||||
self.blocks.push(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 (\
|
||||
id, timestamp, chain_name, version_flags, difficulty,\
|
||||
random, nonce, 'transaction', prev_block_hash, hash)\
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)").unwrap();
|
||||
statement.bind(1, block.index as i64);
|
||||
statement.bind(2, block.timestamp as i64);
|
||||
statement.bind(3, &*block.chain_name);
|
||||
statement.bind(4, block.version_flags as i64);
|
||||
statement.bind(5, block.difficulty as i64);
|
||||
statement.bind(6, block.random as i64);
|
||||
statement.bind(7, block.nonce as i64);
|
||||
match block.transaction {
|
||||
None => { statement.bind(8, ""); }
|
||||
Some(transaction) => { statement.bind(8, &*transaction.to_string()); }
|
||||
{
|
||||
// Adding block to DB
|
||||
let mut statement = self.db.prepare("INSERT INTO blocks (\
|
||||
id, timestamp, chain_name, version_flags, difficulty,\
|
||||
random, nonce, 'transaction', prev_block_hash, hash)\
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);").unwrap();
|
||||
statement.bind(1, block.index as i64);
|
||||
statement.bind(2, block.timestamp as i64);
|
||||
statement.bind(3, block.chain_name.as_ref() as &str);
|
||||
statement.bind(4, block.version_flags as i64);
|
||||
statement.bind(5, block.difficulty as i64);
|
||||
statement.bind(6, block.random as i64);
|
||||
statement.bind(7, block.nonce as i64);
|
||||
match &transaction {
|
||||
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 {
|
||||
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> {
|
||||
self.last_block.clone()
|
||||
}
|
||||
@@ -129,6 +173,20 @@ impl Blockchain {
|
||||
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 {
|
||||
// We need to clear Hash value to rehash it without it for check :(
|
||||
let mut copy: Block = block.clone();
|
||||
|
||||
Vendored
+6
-1
@@ -10829,4 +10829,9 @@ label.panel-block:hover {
|
||||
background-color: #fafafa;
|
||||
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 serde::{Serialize, Deserialize, Serializer, Deserializer};
|
||||
use serde::de::Error;
|
||||
@@ -9,12 +10,13 @@ pub struct Context {
|
||||
pub(crate) settings: Settings,
|
||||
pub(crate) keystore: Keystore,
|
||||
pub(crate) blockchain: Blockchain,
|
||||
pub bus: Bus<Event>,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
/// Creating an essential context to work with
|
||||
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
|
||||
|
||||
@@ -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>
|
||||
{scripts}
|
||||
</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">
|
||||
<br/>
|
||||
<div class="columns">
|
||||
<div class="column is-one-fifth">
|
||||
<div class="menu">
|
||||
@@ -134,7 +152,7 @@
|
||||
<div class="field">
|
||||
<label class="label">New domain name</label>
|
||||
<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>
|
||||
|
||||
@@ -154,7 +172,7 @@
|
||||
|
||||
<div class="field is-grouped">
|
||||
<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>
|
||||
</form>
|
||||
|
||||
@@ -6,10 +6,14 @@ pub mod transaction;
|
||||
pub use crate::transaction::Transaction;
|
||||
pub mod utils;
|
||||
pub use crate::utils::*;
|
||||
pub mod simplebus;
|
||||
pub use crate::simplebus::*;
|
||||
pub mod keys;
|
||||
pub use crate::keys::Keystore;
|
||||
pub use crate::keys::Bytes;
|
||||
pub mod miner;
|
||||
pub mod context;
|
||||
pub mod event;
|
||||
|
||||
pub use crate::context::Context;
|
||||
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"]
|
||||
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::sync::{Arc, Mutex};
|
||||
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;
|
||||
|
||||
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>>) {
|
||||
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 html = Content::Html(file_content.to_owned().replace("{styles}", &styles).replace("{scripts}", &scripts));
|
||||
web_view::builder()
|
||||
.title("ALFIS 0.1.0")
|
||||
.content(Content::Html(file_content.to_owned().replace("{styles}", &styles).replace("{scripts}", &scripts)))
|
||||
.content(html)
|
||||
.size(1024, 720)
|
||||
.resizable(true)
|
||||
.debug(true)
|
||||
.user_data(())
|
||||
.invoke_handler(|_web_view, arg| {
|
||||
.invoke_handler(|web_view, arg| {
|
||||
use Cmd::*;
|
||||
println!("Command {}", arg);
|
||||
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 } => {
|
||||
match Keystore::from_file(&name, &pass) {
|
||||
None => {
|
||||
println!("Error loading keystore '{}'!", &name);
|
||||
},
|
||||
Some(k) => {
|
||||
Some(keystore) => {
|
||||
let mut c = context.lock().unwrap();
|
||||
c.set_keystore(k);
|
||||
},
|
||||
c.set_keystore(keystore);
|
||||
}
|
||||
}
|
||||
},
|
||||
CreateKey { 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 } => {
|
||||
let keystore = {
|
||||
let mut guard = context.lock().unwrap();
|
||||
@@ -90,6 +120,9 @@ fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
|
||||
}
|
||||
RenewDomain { name, days } => {}
|
||||
TransferDomain { name, owner } => {}
|
||||
StopMining => {
|
||||
context.lock().unwrap().bus.post(Event::ActionStopMining);
|
||||
}
|
||||
}
|
||||
//dbg!(&signature);
|
||||
Ok(())
|
||||
@@ -131,21 +164,32 @@ 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();
|
||||
{ context.lock().unwrap().bus.post(Event::KeyGeneratorStarted); }
|
||||
let filename= filename.to_owned();
|
||||
let password= password.to_owned();
|
||||
let mining = mining.clone();
|
||||
thread::spawn(move || {
|
||||
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) => {
|
||||
let mut c = context.lock().unwrap();
|
||||
mining.store(false,Ordering::Relaxed);
|
||||
keystore.save(&filename, &password);
|
||||
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> {
|
||||
@@ -168,12 +212,15 @@ fn generate_key(difficulty: usize, mining: Arc<AtomicBool>) -> Option<Keystore>
|
||||
#[derive(Deserialize)]
|
||||
#[serde(tag = "cmd", rename_all = "camelCase")]
|
||||
pub enum Cmd {
|
||||
Loaded,
|
||||
LoadKey{name: String, pass: String},
|
||||
CreateKey{name: String, pass: 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},
|
||||
StopMining,
|
||||
}
|
||||
|
||||
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::{Mutex, Arc, Condvar};
|
||||
use crypto::digest::Digest;
|
||||
use std::sync::atomic::{AtomicBool, Ordering, AtomicU32};
|
||||
use chrono::Utc;
|
||||
use crypto::sha2::Sha256;
|
||||
use std::sync::{Arc, Condvar, Mutex};
|
||||
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use chrono::Utc;
|
||||
use crypto::digest::Digest;
|
||||
use crypto::sha2::Sha256;
|
||||
use num_cpus;
|
||||
|
||||
use crate::{Block, Bytes, Context, hash_is_good, Keystore, Transaction};
|
||||
use crate::event::Event;
|
||||
|
||||
pub struct Miner {
|
||||
context: Arc<Mutex<Context>>,
|
||||
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 {
|
||||
@@ -86,7 +96,8 @@ impl Miner {
|
||||
let mut chain_name= String::new();
|
||||
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();
|
||||
version_flags = c.settings.version_flags;
|
||||
}
|
||||
@@ -142,7 +153,9 @@ impl Miner {
|
||||
},
|
||||
Some(block) => {
|
||||
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);
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
function onLoad() {
|
||||
external.invoke(JSON.stringify({cmd: 'loaded'}));
|
||||
}
|
||||
|
||||
function openTab(element, tabName) {
|
||||
// Declare all variables
|
||||
var i, tabContent, tabLinks;
|
||||
@@ -59,4 +63,56 @@ function transferDomain() {
|
||||
|
||||
function sendAction(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 {
|
||||
pub fn from_str(identity: String, method: String, data: String, pub_key: Bytes) -> Self {
|
||||
let mut buf: [u8; 32] = [0; 32];
|
||||
let mut digest = Sha256::new();
|
||||
digest.input_str(&identity);
|
||||
digest.result(&mut buf);
|
||||
return Self::new(Bytes::from_bytes(&buf), method, data, pub_key);
|
||||
let bytes = Self::hash_identity(&identity);
|
||||
return Self::new(bytes, method, data, pub_key);
|
||||
}
|
||||
|
||||
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
|
||||
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 {
|
||||
|
||||
Reference in New Issue
Block a user