Files
grin-node/src/bin/tui/status.rs
T
hashmap d8c6eef485 Refactor SyncState (#3297)
* Refactor SyncState

Method sync_error() retrun type was simplified.
update_txhashset_download() was  made type safe, which eliminates a runtime enum variant's  check, added an atomic status update
2020-04-20 12:30:04 +02:00

310 lines
10 KiB
Rust

// Copyright 2020 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Basic status view definition
use chrono::prelude::Utc;
use cursive::direction::Orientation;
use cursive::traits::Identifiable;
use cursive::view::View;
use cursive::views::{LinearLayout, ResizedView, TextView};
use cursive::Cursive;
use crate::tui::constants::VIEW_BASIC_STATUS;
use crate::tui::types::TUIStatusListener;
use crate::chain::SyncStatus;
use crate::servers::ServerStats;
const NANO_TO_MILLIS: f64 = 1.0 / 1_000_000.0;
pub struct TUIStatusView;
impl TUIStatusView {
fn update_sync_status(sync_status: SyncStatus) -> String {
match sync_status {
SyncStatus::Initial => "Initializing".to_string(),
SyncStatus::NoSync => "Running".to_string(),
SyncStatus::AwaitingPeers(_) => "Waiting for peers".to_string(),
SyncStatus::HeaderSync {
current_height,
highest_height,
} => {
let percent = if highest_height == 0 {
0
} else {
current_height * 100 / highest_height
};
format!("Sync step 1/7: Downloading headers: {}%", percent)
}
SyncStatus::TxHashsetDownload(stat) => {
if stat.total_size > 0 {
let percent = stat.downloaded_size * 100 / stat.total_size;
let start = stat.prev_update_time.timestamp_nanos();
let fin = Utc::now().timestamp_nanos();
let dur_ms = (fin - start) as f64 * NANO_TO_MILLIS;
format!("Sync step 2/7: Downloading {}(MB) chain state for state sync: {}% at {:.1?}(kB/s)",
stat.total_size / 1_000_000,
percent,
if dur_ms > 1.0f64 { stat.downloaded_size.saturating_sub(stat.prev_downloaded_size) as f64 / dur_ms as f64 } else { 0f64 },
)
} else {
let start = stat.start_time.timestamp_millis();
let fin = Utc::now().timestamp_millis();
let dur_secs = (fin - start) / 1000;
format!("Sync step 2/7: Downloading chain state for state sync. Waiting remote peer to start: {}s",
dur_secs,
)
}
}
SyncStatus::TxHashsetSetup => {
"Sync step 3/7: Preparing chain state for validation".to_string()
}
SyncStatus::TxHashsetRangeProofsValidation {
rproofs,
rproofs_total,
} => {
let r_percent = if rproofs_total > 0 {
(rproofs * 100) / rproofs_total
} else {
0
};
format!(
"Sync step 4/7: Validating chain state - range proofs: {}%",
r_percent
)
}
SyncStatus::TxHashsetKernelsValidation {
kernels,
kernels_total,
} => {
let k_percent = if kernels_total > 0 {
(kernels * 100) / kernels_total
} else {
0
};
format!(
"Sync step 5/7: Validating chain state - kernels: {}%",
k_percent
)
}
SyncStatus::TxHashsetSave => {
"Sync step 6/7: Finalizing chain state for state sync".to_string()
}
SyncStatus::TxHashsetDone => {
"Sync step 6/7: Finalized chain state for state sync".to_string()
}
SyncStatus::BodySync {
current_height,
highest_height,
} => {
let percent = if highest_height == 0 {
0
} else {
current_height * 100 / highest_height
};
format!("Sync step 7/7: Downloading blocks: {}%", percent)
}
SyncStatus::Shutdown => "Shutting down, closing connections".to_string(),
}
}
}
impl TUIStatusListener for TUIStatusView {
/// Create basic status view
fn create() -> Box<dyn View> {
let basic_status_view = ResizedView::with_full_screen(
LinearLayout::new(Orientation::Vertical)
.child(
LinearLayout::new(Orientation::Horizontal)
.child(TextView::new("Current Status: "))
.child(TextView::new("Starting").with_name("basic_current_status")),
)
.child(
LinearLayout::new(Orientation::Horizontal)
.child(TextView::new("Connected Peers: "))
.child(TextView::new("0").with_name("connected_peers")),
)
.child(
LinearLayout::new(Orientation::Horizontal)
.child(TextView::new("Disk Usage (GB): "))
.child(TextView::new("0").with_name("disk_usage")),
)
.child(
LinearLayout::new(Orientation::Horizontal).child(TextView::new(
"--------------------------------------------------------",
)),
)
.child(
LinearLayout::new(Orientation::Horizontal)
.child(TextView::new("Header Tip Hash: "))
.child(TextView::new(" ").with_name("basic_header_tip_hash")),
)
.child(
LinearLayout::new(Orientation::Horizontal)
.child(TextView::new("Header Chain Height: "))
.child(TextView::new(" ").with_name("basic_header_chain_height")),
)
.child(
LinearLayout::new(Orientation::Horizontal)
.child(TextView::new("Header Cumulative Difficulty: "))
.child(TextView::new(" ").with_name("basic_header_total_difficulty")),
)
.child(
LinearLayout::new(Orientation::Horizontal)
.child(TextView::new("Header Tip Timestamp: "))
.child(TextView::new(" ").with_name("basic_header_timestamp")),
)
.child(
LinearLayout::new(Orientation::Horizontal).child(TextView::new(
"--------------------------------------------------------",
)),
)
.child(
LinearLayout::new(Orientation::Horizontal)
.child(TextView::new("Chain Tip Hash: "))
.child(TextView::new(" ").with_name("tip_hash")),
)
.child(
LinearLayout::new(Orientation::Horizontal)
.child(TextView::new("Chain Height: "))
.child(TextView::new(" ").with_name("chain_height")),
)
.child(
LinearLayout::new(Orientation::Horizontal)
.child(TextView::new("Chain Cumulative Difficulty: "))
.child(TextView::new(" ").with_name("basic_total_difficulty")),
)
.child(
LinearLayout::new(Orientation::Horizontal)
.child(TextView::new("Chain Tip Timestamp: "))
.child(TextView::new(" ").with_name("chain_timestamp")),
)
.child(
LinearLayout::new(Orientation::Horizontal).child(TextView::new(
"--------------------------------------------------------",
)),
)
.child(
LinearLayout::new(Orientation::Horizontal)
.child(TextView::new("Transaction Pool Size: "))
.child(TextView::new("0").with_name("tx_pool_size"))
.child(TextView::new(" ("))
.child(TextView::new("0").with_name("tx_pool_kernels"))
.child(TextView::new(")")),
)
.child(
LinearLayout::new(Orientation::Horizontal)
.child(TextView::new("Stem Pool Size: "))
.child(TextView::new("0").with_name("stem_pool_size"))
.child(TextView::new(" ("))
.child(TextView::new("0").with_name("stem_pool_kernels"))
.child(TextView::new(")")),
)
.child(
LinearLayout::new(Orientation::Horizontal).child(TextView::new(
"--------------------------------------------------------",
)),
)
.child(
LinearLayout::new(Orientation::Horizontal)
.child(TextView::new(" ").with_name("basic_mining_config_status")),
)
.child(
LinearLayout::new(Orientation::Horizontal)
.child(TextView::new(" ").with_name("basic_mining_status")),
)
.child(
LinearLayout::new(Orientation::Horizontal)
.child(TextView::new(" ").with_name("basic_network_info")),
), //.child(logo_view)
);
Box::new(basic_status_view.with_name(VIEW_BASIC_STATUS))
}
fn update(c: &mut Cursive, stats: &ServerStats) {
let basic_status = TUIStatusView::update_sync_status(stats.sync_status);
c.call_on_name("basic_current_status", |t: &mut TextView| {
t.set_content(basic_status);
});
c.call_on_name("connected_peers", |t: &mut TextView| {
t.set_content(stats.peer_count.to_string());
});
c.call_on_name("disk_usage", |t: &mut TextView| {
t.set_content(stats.disk_usage_gb.clone());
});
c.call_on_name("tip_hash", |t: &mut TextView| {
t.set_content(stats.chain_stats.last_block_h.to_string() + "...");
});
c.call_on_name("chain_height", |t: &mut TextView| {
t.set_content(stats.chain_stats.height.to_string());
});
c.call_on_name("basic_total_difficulty", |t: &mut TextView| {
t.set_content(stats.chain_stats.total_difficulty.to_string());
});
c.call_on_name("chain_timestamp", |t: &mut TextView| {
t.set_content(stats.chain_stats.latest_timestamp.to_string());
});
c.call_on_name("basic_header_tip_hash", |t: &mut TextView| {
t.set_content(stats.header_stats.last_block_h.to_string() + "...");
});
c.call_on_name("basic_header_chain_height", |t: &mut TextView| {
t.set_content(stats.header_stats.height.to_string());
});
c.call_on_name("basic_header_total_difficulty", |t: &mut TextView| {
t.set_content(stats.header_stats.total_difficulty.to_string());
});
c.call_on_name("basic_header_timestamp", |t: &mut TextView| {
t.set_content(stats.header_stats.latest_timestamp.to_string());
});
if let Some(tx_stats) = &stats.tx_stats {
c.call_on_name("tx_pool_size", |t: &mut TextView| {
t.set_content(tx_stats.tx_pool_size.to_string());
});
c.call_on_name("stem_pool_size", |t: &mut TextView| {
t.set_content(tx_stats.stem_pool_size.to_string());
});
c.call_on_name("tx_pool_kernels", |t: &mut TextView| {
t.set_content(tx_stats.tx_pool_kernels.to_string());
});
c.call_on_name("stem_pool_kernels", |t: &mut TextView| {
t.set_content(tx_stats.stem_pool_kernels.to_string());
});
}
}
}
#[test]
fn test_status_txhashset_kernels() {
let status = SyncStatus::TxHashsetKernelsValidation {
kernels: 201,
kernels_total: 5000,
};
let basic_status = TUIStatusView::update_sync_status(status);
assert!(basic_status.contains("4%"), basic_status);
}
#[test]
fn test_status_txhashset_rproofs() {
let status = SyncStatus::TxHashsetRangeProofsValidation {
rproofs: 643,
rproofs_total: 1000,
};
let basic_status = TUIStatusView::update_sync_status(status);
assert!(basic_status.contains("64%"), basic_status);
}