1use console::style;
20use log::info;
21use sc_client_api::ClientInfo;
22use sc_network::NetworkStatus;
23use sc_network_sync::{SyncState, SyncStatus, WarpSyncPhase, WarpSyncProgress};
24use sp_runtime::traits::{Block as BlockT, CheckedDiv, NumberFor, Saturating, Zero};
25use std::{fmt, time::Instant};
26
27pub struct InformantDisplay<B: BlockT> {
40 last_number: Option<NumberFor<B>>,
43 last_update: Instant,
45 last_total_bytes_inbound: u64,
47 last_total_bytes_outbound: u64,
49}
50
51impl<B: BlockT> InformantDisplay<B> {
52 pub fn new() -> InformantDisplay<B> {
54 InformantDisplay {
55 last_number: None,
56 last_update: Instant::now(),
57 last_total_bytes_inbound: 0,
58 last_total_bytes_outbound: 0,
59 }
60 }
61
62 pub fn display(
64 &mut self,
65 info: &ClientInfo<B>,
66 net_status: NetworkStatus,
67 sync_status: SyncStatus<B>,
68 num_connected_peers: usize,
69 ) {
70 let best_number = info.chain.best_number;
71 let best_hash = info.chain.best_hash;
72 let finalized_number = info.chain.finalized_number;
73 let speed = speed::<B>(best_number, self.last_number, self.last_update);
74 let total_bytes_inbound = net_status.total_bytes_inbound;
75 let total_bytes_outbound = net_status.total_bytes_outbound;
76
77 let now = Instant::now();
78 let elapsed = (now - self.last_update).as_secs();
79 self.last_update = now;
80 self.last_number = Some(best_number);
81
82 let diff_bytes_inbound = total_bytes_inbound - self.last_total_bytes_inbound;
83 let diff_bytes_outbound = total_bytes_outbound - self.last_total_bytes_outbound;
84 let (avg_bytes_per_sec_inbound, avg_bytes_per_sec_outbound) = if elapsed > 0 {
85 self.last_total_bytes_inbound = total_bytes_inbound;
86 self.last_total_bytes_outbound = total_bytes_outbound;
87 (diff_bytes_inbound / elapsed, diff_bytes_outbound / elapsed)
88 } else {
89 (diff_bytes_inbound, diff_bytes_outbound)
90 };
91
92 let (level, status, target) =
93 match (sync_status.state, sync_status.state_sync, sync_status.warp_sync) {
94 (
100 sync_status,
101 _,
102 Some(WarpSyncProgress { phase: WarpSyncPhase::DownloadingBlocks(n), .. }),
103 ) if !sync_status.is_major_syncing() => ("⏩", "Block history".into(), format!(", #{}", n)),
104 (_, _, Some(warp))
106 if !matches!(warp.phase, WarpSyncPhase::DownloadingBlocks(_)) =>
107 (
108 "⏩",
109 "Warping".into(),
110 format!(
111 ", {}, {:.2} Mib",
112 warp.phase,
113 (warp.total_bytes as f32) / (1024f32 * 1024f32)
114 ),
115 ),
116 (_, Some(state), _) => (
117 "⚙️ ",
118 "State sync".into(),
119 format!(
120 ", {}, {}%, {:.2} Mib",
121 state.phase,
122 state.percentage,
123 (state.size as f32) / (1024f32 * 1024f32)
124 ),
125 ),
126 (SyncState::Idle, _, _) => ("💤", "Idle".into(), "".into()),
127 (SyncState::Downloading { target }, _, _) =>
128 ("⚙️ ", format!("Syncing{}", speed), format!(", target=#{target}")),
129 (SyncState::Importing { target }, _, _) =>
130 ("⚙️ ", format!("Preparing{}", speed), format!(", target=#{target}")),
131 };
132
133 info!(
134 target: "substrate",
135 "{} {}{} ({} peers), best: #{} ({}), finalized #{} ({}), ⬇ {} ⬆ {}",
136 level,
137 style(&status).white().bold(),
138 target,
139 style(num_connected_peers).white().bold(),
140 style(best_number).white().bold(),
141 best_hash,
142 style(finalized_number).white().bold(),
143 info.chain.finalized_hash,
144 style(TransferRateFormat(avg_bytes_per_sec_inbound)).green(),
145 style(TransferRateFormat(avg_bytes_per_sec_outbound)).red(),
146 )
147 }
148}
149
150fn speed<B: BlockT>(
153 best_number: NumberFor<B>,
154 last_number: Option<NumberFor<B>>,
155 last_update: Instant,
156) -> String {
157 let elapsed_ms = {
159 let elapsed = last_update.elapsed();
160 let since_last_millis = elapsed.as_secs() * 1000;
161 let since_last_subsec_millis = elapsed.subsec_millis() as u64;
162 since_last_millis + since_last_subsec_millis
163 };
164
165 let diff = match last_number {
167 None => return String::new(),
168 Some(n) => best_number.saturating_sub(n),
169 };
170
171 if let Ok(diff) = TryInto::<u128>::try_into(diff) {
172 let speed = diff
175 .saturating_mul(10_000)
176 .checked_div(u128::from(elapsed_ms))
177 .map_or(0.0, |s| s as f64) /
178 10.0;
179 format!(" {:4.1} bps", speed)
180 } else {
181 let one_thousand = NumberFor::<B>::from(1_000u32);
184 let elapsed =
185 NumberFor::<B>::from(<u32 as TryFrom<_>>::try_from(elapsed_ms).unwrap_or(u32::MAX));
186
187 let speed = diff
188 .saturating_mul(one_thousand)
189 .checked_div(&elapsed)
190 .unwrap_or_else(Zero::zero);
191 format!(" {} bps", speed)
192 }
193}
194
195struct TransferRateFormat(u64);
198impl fmt::Display for TransferRateFormat {
199 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
200 if self.0 == 0 {
202 return write!(f, "0")
203 }
204
205 if self.0 < 100 {
207 return write!(f, "{} B/s", self.0)
208 }
209
210 if self.0 < 1024 * 1024 {
212 return write!(f, "{:.1}kiB/s", self.0 as f64 / 1024.0)
213 }
214
215 write!(f, "{:.1}MiB/s", self.0 as f64 / (1024.0 * 1024.0))
216 }
217}