Implemented status bar with some status information: sync process, connected nodes and blockchain height.

pull/3/head
Revertron 3 years ago
parent 0d3b8c3db0
commit e53245315f

@ -14,6 +14,7 @@ pub struct Blockchain {
pub version: u32,
pub blocks: Vec<Block>,
last_block: Option<Block>,
max_height: u64,
db: Connection,
zones: RefCell<HashSet<String>>
}
@ -24,7 +25,7 @@ impl Blockchain {
let version = settings.version;
let db = sqlite::open(DB_NAME).expect("Unable to open blockchain DB");
let mut blockchain = Blockchain{ origin, version, blocks: Vec::new(), last_block: None, db, zones: RefCell::new(HashSet::new()) };
let mut blockchain = Blockchain{ origin, version, blocks: Vec::new(), last_block: None, max_height: 0, db, zones: RefCell::new(HashSet::new()) };
blockchain.init_db();
blockchain
}
@ -243,6 +244,16 @@ impl Blockchain {
}
}
pub fn max_height(&self) -> u64 {
self.max_height
}
pub fn update_max_height(&mut self, height: u64) {
if height > self.max_height {
self.max_height = height;
}
}
/*pub fn check(&self) -> bool {
let mut prev_block = None;
for block in self.blocks.iter() {

@ -4,10 +4,13 @@ pub enum Event {
MinerStopped,
KeyGeneratorStarted,
KeyGeneratorStopped,
KeyCreated {path: String, public: String},
KeyLoaded {path: String, public: String},
KeySaved {path: String, public: String},
KeyCreated { path: String, public: String },
KeyLoaded { path: String, public: String },
KeySaved { path: String, public: String },
NewBlockReceived,
BlockchainChanged,
ActionStopMining,
StatsCount { nodes: usize, blocks: u64 },
SyncStarted { have: u64, height: u64 },
ActionIdle,
}

@ -56,11 +56,11 @@ fn main() {
let program = args[0].clone();
let mut opts = Options::new();
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", "");
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", "");
let opt_matches = match opts.parse(&args[1..]) {
Ok(m) => m,
@ -161,8 +161,8 @@ 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!("webview/index.html");
let mut styles= inline_style(include_str!("webview/bulma.css"));
styles.push_str(&inline_style(include_str!("webview/miner.css")));
let mut styles = inline_style(include_str!("webview/bulma.css"));
styles.push_str(&inline_style(include_str!("webview/busy_indicator.css")));
let scripts = inline_script(include_str!("webview/scripts.js"));
let html = Content::Html(file_content.to_owned().replace("{styles}", &styles).replace("{scripts}", &scripts));
@ -189,10 +189,19 @@ fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
Event::KeyCreated { path, public } => { format!("keystoreChanged('{}', '{}');", &path, &public) }
Event::KeyLoaded { path, public } => { format!("keystoreChanged('{}', '{}');", &path, &public) }
Event::KeySaved { path, public } => { format!("keystoreChanged('{}', '{}');", &path, &public) }
Event::MinerStarted => { format!("showMiningIndicator({});", true) }
Event::KeyGeneratorStarted => { format!("showMiningIndicator({});", true) }
Event::MinerStopped => { format!("showMiningIndicator({});", false) }
Event::KeyGeneratorStopped => { format!("showMiningIndicator({});", false) }
Event::MinerStarted => { format!("showMiningIndicator({}, false);", true) }
Event::KeyGeneratorStarted => { format!("showMiningIndicator({}, false);", true) }
Event::MinerStopped => { format!("showMiningIndicator({}, false);", false) }
Event::KeyGeneratorStopped => { format!("showMiningIndicator({}, false);", false) }
Event::SyncStarted { have, height } => {
format!("setLeftStatusBarText('Synchronizing {}/{}'); showMiningIndicator(true, true);", have, height)
}
Event::ActionIdle => {
format!("setLeftStatusBarText('Idle'); showMiningIndicator(false, true);")
}
Event::StatsCount { nodes, blocks } => {
format!("setRightStatusBarText('Nodes: {}, Blocks: {}')", nodes, blocks)
}
_ => { String::new() }
};
@ -216,17 +225,17 @@ fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
match Keystore::from_file(&file_name, "") {
None => {
error!("Error loading keystore '{}'!", &file_name);
},
}
Some(keystore) => {
info!("Loaded keystore with key: {:?}", &keystore.get_public());
let mut c = context.lock().unwrap();
c.bus.post(Event::KeyLoaded {path: keystore.get_path().to_owned(), public: keystore.get_public().to_string()});
c.bus.post(Event::KeyLoaded { path: keystore.get_path().to_owned(), public: keystore.get_public().to_string() });
c.set_keystore(keystore);
}
}
}
}
},
}
CreateKey {} => {
create_key(context.clone());
}
@ -240,11 +249,11 @@ fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
let public = c.keystore.get_public().to_string();
c.keystore.save(&new_path, "");
info!("Key file saved to {}", &path);
c.bus.post(Event::KeySaved {path, public });
c.bus.post(Event::KeySaved { path, public });
}
}
}
CheckDomain { name} => {
CheckDomain { name } => {
let name = name.to_lowercase();
let c = context.lock().unwrap();
let available = c.get_blockchain().is_domain_available(&name, &c.get_keystore());
@ -282,7 +291,7 @@ fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
ChangeDomain { .. } => {}
RenewDomain { .. } => {}
TransferDomain { .. } => {}
CheckZone { name} => {
CheckZone { name } => {
let name = name.to_lowercase();
if !check_domain(&name, false) {
web_view.eval("zoneAvailable(false)").expect("Error evaluating!");
@ -398,8 +407,8 @@ fn create_key(context: Arc<Mutex<Context>>) {
Some(keystore) => {
info!("Key mined successfully: {:?}", &keystore.get_public());
let mut c = context.lock().unwrap();
mining.store(false,Ordering::Relaxed);
c.bus.post(Event::KeyCreated {path: keystore.get_path().to_owned(), public: keystore.get_public().to_string()});
mining.store(false, Ordering::Relaxed);
c.bus.post(Event::KeyCreated { path: keystore.get_path().to_owned(), public: keystore.get_public().to_string() });
c.set_keystore(keystore);
}
}
@ -440,7 +449,7 @@ fn create_server_context(context: Arc<Mutex<Context>>, settings: &Settings) -> A
server_context.dns_port = settings.dns.port;
server_context.resolve_strategy = match settings.dns.forwarders.is_empty() {
true => { ResolveStrategy::Recursive }
false => { ResolveStrategy::Forward { upstreams: settings.dns.forwarders.clone() }}
false => { ResolveStrategy::Forward { upstreams: settings.dns.forwarders.clone() } }
};
server_context.filters.push(Box::new(BlockchainFilter::new(context)));
match server_context.initialize() {
@ -455,16 +464,16 @@ fn create_server_context(context: Arc<Mutex<Context>>, settings: &Settings) -> A
#[serde(tag = "cmd", rename_all = "camelCase")]
pub enum Cmd {
Loaded,
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},
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 },
StopMining,
}

@ -91,6 +91,9 @@ impl Network {
token => {
if !handle_connection_event(context.clone(), &mut peers, &poll.registry(), &event) {
let _ = peers.close_peer(poll.registry(), &token);
let mut context = context.lock().unwrap();
let blocks_count = context.blockchain.height();
context.bus.post(crate::event::Event::StatsCount { nodes: peers.get_peers_active_count(), blocks: blocks_count });
}
}
}
@ -123,9 +126,10 @@ fn handle_connection_event(context: Arc<Mutex<Context>>, peers: &mut Peers, regi
let data = data.unwrap();
match Message::from_bytes(data) {
Ok(message) => {
debug!("Got message from socket {}: {:?}", &event.token().0, &message);
let m = format!("{:?}", &message);
let new_state = handle_message(context.clone(), message, peers, &event.token());
let peer = peers.get_mut_peer(&event.token()).unwrap();
debug!("Got message from {}: {:?}", &peer.get_addr(), &m);
let stream = peer.get_stream();
match new_state {
State::Message { data } => {
@ -163,17 +167,17 @@ fn handle_connection_event(context: Arc<Mutex<Context>>, peers: &mut Peers, regi
Some(peer) => {
match peer.get_state().clone() {
State::Connecting => {
debug!("Sending hello to socket {}", event.token().0);
debug!("Sending hello to {}", &peer.get_addr());
let data: String = {
let c = context.lock().unwrap();
let message = Message::hand(&c.settings.origin, c.settings.version, c.settings.public);
serde_json::to_string(&message).unwrap()
};
send_message(peer.get_stream(), &data.into_bytes());
debug!("Sent hello through socket {}", event.token().0);
debug!("Sent hello to {}", &peer.get_addr());
}
State::Message { data } => {
debug!("Sending data to socket {}: {}", event.token().0, &String::from_utf8(data.clone()).unwrap());
debug!("Sending data to {}: {}", &peer.get_addr(), &String::from_utf8(data.clone()).unwrap());
send_message(peer.get_stream(), &data);
}
State::Connected => {}
@ -273,9 +277,16 @@ fn handle_message(context: Arc<Mutex<Context>>, message: Message, peers: &mut Pe
return State::Error;
}
if ok {
let active_count = peers.get_peers_active_count();
let peer = peers.get_mut_peer(token).unwrap();
peer.set_height(height);
peer.set_active(true);
let mut context = context.lock().unwrap();
let blocks_count = context.blockchain.height();
context.bus.post(crate::event::Event::StatsCount { nodes: active_count, blocks: blocks_count });
if peer.is_higher(my_height) {
context.blockchain.update_max_height(height);
context.bus.post(crate::event::Event::SyncStarted { have: my_height, height});
State::message(Message::GetBlock { index: my_height })
} else {
State::message(Message::GetPeers)
@ -288,6 +299,7 @@ fn handle_message(context: Arc<Mutex<Context>>, message: Message, peers: &mut Pe
Message::Ping { height } => {
let peer = peers.get_mut_peer(token).unwrap();
peer.set_height(height);
peer.set_active(true);
if peer.is_higher(my_height) {
State::message(Message::GetBlock { index: my_height })
} else {
@ -297,9 +309,14 @@ fn handle_message(context: Arc<Mutex<Context>>, message: Message, peers: &mut Pe
Message::Pong { height } => {
let peer = peers.get_mut_peer(token).unwrap();
peer.set_height(height);
peer.set_active(true);
if peer.is_higher(my_height) {
State::message(Message::GetBlock { index: my_height })
} else {
let mut context = context.lock().unwrap();
let blocks_count = context.blockchain.height();
context.bus.post(crate::event::Event::ActionIdle);
context.bus.post(crate::event::Event::StatsCount { nodes: peers.get_peers_active_count(), blocks: blocks_count });
State::idle()
}
}
@ -324,12 +341,22 @@ fn handle_message(context: Arc<Mutex<Context>>, message: Message, peers: &mut Pe
Ok(block) => block,
Err(_) => return State::Error
};
// TODO check if the block is good
// TODO check here if the block is good before trying to add
let context = context.clone();
thread::spawn(move || {
let mut context = context.lock().unwrap();
let max_height = context.blockchain.max_height();
match context.blockchain.add_block(block) {
Ok(_) => { context.bus.post(crate::event::Event::BlockchainChanged); }
Ok(_) => {
let my_height = context.blockchain.height();
context.bus.post(crate::event::Event::BlockchainChanged);
// If it was the last block to sync
if my_height == max_height {
context.bus.post(crate::event::Event::ActionIdle);
} else {
context.bus.post(crate::event::Event::SyncStarted { have: my_height, height: max_height});
}
}
Err(_) => { warn!("Discarded received block"); }
}
});

@ -10,11 +10,12 @@ pub struct Peer {
height: u64,
inbound: bool,
public: bool,
active: bool,
}
impl Peer {
pub fn new(addr: SocketAddr, stream: TcpStream, state: State, inbound: bool) -> Self {
Peer { addr, stream, state, height: 0, inbound, public: false }
Peer { addr, stream, state, height: 0, inbound, public: false, active: false }
}
pub fn get_addr(&self) -> SocketAddr {
@ -53,8 +54,12 @@ impl Peer {
self.public = public;
}
pub fn set_active(&mut self, active: bool) {
self.active = active;
}
pub fn active(&self) -> bool {
self.state.active()
self.active
}
pub fn disabled(&self) -> bool {

@ -45,6 +45,7 @@ impl Peers {
if !peer.disabled() && !peer.is_inbound() {
peer.set_state(State::offline());
peer.set_active(false);
} else {
self.peers.remove(token);
}
@ -112,6 +113,16 @@ impl Peers {
result
}
pub fn get_peers_active_count(&self) -> usize {
let mut count = 0;
for (_, peer) in self.peers.iter() {
if peer.active() {
count += 1;
}
}
count
}
pub fn skip_peer_connection(&self, addr: &SocketAddr) -> bool {
for (_, peer) in self.peers.iter() {
if peer.equals(addr) && (!peer.is_public() || peer.active() || peer.disabled()) {

@ -26,16 +26,6 @@ impl State {
State::Message {data: Vec::from(response.as_bytes()) }
}
pub fn active(&self) -> bool {
match self {
State::Connecting => { true }
State::Connected => { true }
State::Idle { .. } => { true }
State::Message { .. } => { true }
_ => { false }
}
}
pub fn is_idle(&self) -> bool {
match self {
State::Idle { .. } => { true }

@ -10846,4 +10846,13 @@ html {
width: 50%;
top: 10pt;
right: 10pt;
}
.footer {
background-color: #f4f4f4;
padding: 0.2rem 0.5rem 0.2rem 0.5rem;
width: 100%;
position: absolute;
bottom: 0px;
z-index: 9;
}

@ -0,0 +1,34 @@
.busy_indicator {
width:24px;
height:24px;
display:inline-block;
padding:0px;
text-align:left;
float:left;
margin-left: -5px;
}
.busy_indicator span {
position:absolute;
display:inline-block;
width:24px;
height:24px;
border-radius:100%;
background:#ee3333;
-webkit-animation:busy_indicator 1.6s linear infinite;
animation:busy_indicator 1.6s linear infinite;
}
.busy_indicator span:last-child {
animation-delay:-0.8s;
-webkit-animation-delay:-0.8s;
}
@keyframes busy_indicator {
0% {transform: scale(0, 0);opacity:0.9;}
100% {transform: scale(1, 1);opacity:0;}
}
@-webkit-keyframes busy_indicator {
0% {-webkit-transform: scale(0, 0);opacity:0.9;}
100% {-webkit-transform: scale(1, 1);opacity:0;}
}
.busy_blue span {
background:#3273dc;
}

@ -9,10 +9,6 @@
{scripts}
</head>
<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>
@ -328,6 +324,25 @@
</div> <!-- columns -->
</div>
<div class="footer is-hidden">Some footer text is here</div>
<div class="footer is-family-code">
<div class="level">
<div class="level-left">
<div class="level-item" id="indicator_parent">
<div class="busy_indicator busy_blue" id="busy_indicator" onclick="miningIndicatorClick(this)">
<span></span>
<span></span>
</div>
</div>
<div class="level-item">
<div id="status_bar_left">Connecting and syncing...</div>
</div>
</div>
<div class="level-right">
<div class="level-item" id="status_bar_right">No data</div>
</div>
</div>
</div>
</body>
</html>

@ -1,34 +0,0 @@
.mining_indicator {
position:absolute;
left:0px;
bottom:0px;
width:30px;
height:30px;
display:inline-block;
padding:0px;
text-align:left;
float:left;
z-index:10;
}
.mining_indicator span {
position:absolute;
display:inline-block;
width:30px;
height:30px;
border-radius:100%;
background:#ee3333;
-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;}
}

@ -249,12 +249,19 @@ function showWarning(text) {
setTimeout(button.onclick, 5000);
}
function showMiningIndicator(visible) {
indicator = document.getElementById("mining_indicator");
function showMiningIndicator(visible, blue) {
var indicator = document.getElementById("busy_indicator");
var parent = document.getElementById("indicator_parent");
var add = "";
if (blue) {
add = " busy_blue";
}
if (visible) {
indicator.style.visibility = 'visible';
indicator.className = 'busy_indicator' + add;
parent.style.display = 'flex';
} else {
indicator.style.visibility = 'hidden';
indicator.className = 'busy_indicator is-hidden';
parent.style.display = 'none';
}
}
@ -264,6 +271,16 @@ function miningIndicatorClick(element) {
});
}
function setLeftStatusBarText(text) {
var bar = document.getElementById("status_bar_left");
bar.innerHTML = text;
}
function setRightStatusBarText(text) {
var bar = document.getElementById("status_bar_right");
bar.innerHTML = text;
}
function keystoreChanged(path, pub_key) {
if (path == '') {
path = "In memory";

Loading…
Cancel
Save