iuna

iuna - experimental devnet protocol
git clone https://iuna.jhx.app/git/iuna.git
Log | Files | Refs | README | LICENSE

commit 673f95ad3ef80ca0ddf176bee50d54ab6816bd61
parent 0538e3023134498e0d7c196bec68106647a681c2
Author: Joris Hartog <jorishartog@hotmail.com>
Date:   Thu, 30 Jul 2026 12:51:15 +0200

Fix metrics chart bootstrap points

Diffstat:
Msrc/adapters/http.rs | 84+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 78 insertions(+), 6 deletions(-)

diff --git a/src/adapters/http.rs b/src/adapters/http.rs @@ -1079,7 +1079,11 @@ fn metrics_response(enabled: bool, rows: Vec<BlockMetricRow>) -> MetricsResponse "s", MetricsValueKind::Seconds, &rows, - |row| row.block_time_ms.map(|ms| ms as f64 / 1_000.0), + |row| { + row.block_time_ms + .filter(|_| row.height > 1) + .map(|ms| ms as f64 / 1_000.0) + }, ), metrics_chart( "difficulty", @@ -1151,7 +1155,7 @@ fn metrics_response(enabled: bool, rows: Vec<BlockMetricRow>) -> MetricsResponse "rounds", MetricsValueKind::Number, &rows, - |row| Some(row.vdf_rounds as f64), + |row| (row.vdf_rounds > 0).then_some(row.vdf_rounds as f64), ), ], } @@ -2433,8 +2437,8 @@ const INDEX_HTML: &str = r#"<!doctype html> .metric-chart-line { fill: none; stroke: #d5f55f; stroke-width: 2.2; stroke-linejoin: round; stroke-linecap: round; } .metric-chart-points { position: absolute; inset: 0; } .metric-chart-point-hit { position: absolute; width: 18px; height: 18px; border: 0; border-radius: 50%; padding: 0; background: transparent; cursor: crosshair; transform: translate(-50%, -50%); } - .metric-chart-point-hit::after { content: ""; position: absolute; left: 50%; top: 50%; width: 5px; height: 5px; border-radius: 50%; background: #d5f55f; opacity: .2; transform: translate(-50%, -50%); transition: opacity .12s ease, width .12s ease, height .12s ease; } - .metric-chart-point-hit:hover::after, .metric-chart-point-hit:focus-visible::after { width: 8px; height: 8px; opacity: 1; } + .metric-chart-point-hit::after { content: ""; position: absolute; left: 50%; top: 50%; width: 5px; height: 5px; border-radius: 50%; background: #d5f55f; opacity: .2; transform: translate(-50%, -50%); } + .metric-chart-point-hit:hover::after, .metric-chart-point-hit:focus-visible::after, .metric-chart-point-hit.is-active::after { width: 8px; height: 8px; opacity: 1; } .metric-chart-tooltip { position: absolute; z-index: 1; max-width: min(180px, 80%); border: 1px solid #566d25; border-radius: 6px; padding: 5px 7px; background: #202615; color: #e8edf0; font-size: 11px; font-weight: 850; font-variant-numeric: tabular-nums; line-height: 1.25; pointer-events: none; box-shadow: 0 8px 20px rgba(0, 0, 0, .28); white-space: nowrap; } .metrics-empty { border: 1px dashed #3a4248; border-radius: 8px; padding: 14px; color: #8d989f; background: #111316; } .wallet-grid { width: 100%; display: grid; grid-template-columns: minmax(0, 1fr) minmax(300px, .8fr); gap: 12px; align-items: start; } @@ -3103,10 +3107,12 @@ const INDEX_HTML: &str = r#"<!doctype html> </svg> <div class="metric-chart-points"> <template x-for="marker in metricChartPointMarkers(chart)" :key="`${chart.id}-point-${marker.height}`"> - <button class="metric-chart-point-hit" type="button" :style="metricPointStyle(marker)" :title="marker.label" @focus="setMetricHover(chart, marker)" @blur="clearMetricHover(chart)" :aria-label="marker.label"></button> + <button class="metric-chart-point-hit" type="button" :class="{ 'is-active': metricHover?.chartId === chart.id && metricHover?.height === marker.height }" :style="metricPointStyle(marker)" :title="marker.label" @focus="setMetricHover(chart, marker)" @blur="clearMetricHover(chart)" :aria-label="marker.label"></button> </template> </div> - <div class="metric-chart-tooltip" x-cloak x-show="metricHover?.chartId === chart.id" :style="metricTooltipStyle(chart)" x-text="metricTooltipLabel(chart)"></div> + <template x-if="metricHover?.chartId === chart.id"> + <div class="metric-chart-tooltip" :style="metricTooltipStyle(chart)" x-text="metricTooltipLabel(chart)"></div> + </template> </div> <div class="metric-chart-x-axis"> <template x-for="tick in metricXAxisTicks(chart)" :key="`${chart.id}-x-${tick}`"> @@ -4228,6 +4234,72 @@ mod tests { } } + fn metric_row( + height: u64, + block_time_ms: Option<u64>, + vdf_rounds: u32, + ) -> crate::adapters::chain_store::BlockMetricRow { + crate::adapters::chain_store::BlockMetricRow { + height, + block_hash: format!("hash-{height}"), + timestamp_ms: height, + block_time_ms, + mine_difficulty_bits: 12, + circulating_supply: 100, + transaction_count: 0, + transfer_count: 0, + burn_count: 0, + mine_count: 0, + burned_amount: 0, + total_burned_amount: 0, + fees_amount: 0, + reward_amount: 0, + vdf_rounds, + finalizer_rank: 0, + } + } + + #[test] + fn metrics_response_skips_bootstrap_points_for_block_time_and_vdf_rounds() { + let response = super::metrics_response( + true, + vec![ + metric_row(0, None, 0), + metric_row(1, Some(1_764_000_000_000), 0), + metric_row(2, Some(600_000), 120), + metric_row(3, Some(610_000), 130), + ], + ); + + let block_time = response + .charts + .iter() + .find(|chart| chart.id == "block-time") + .expect("block time chart should exist"); + assert_eq!( + block_time + .points + .iter() + .map(|point| (point.height, point.value)) + .collect::<Vec<_>>(), + vec![(2, 600.0), (3, 610.0)] + ); + + let vdf_rounds = response + .charts + .iter() + .find(|chart| chart.id == "vdf-rounds") + .expect("VDF rounds chart should exist"); + assert_eq!( + vdf_rounds + .points + .iter() + .map(|point| (point.height, point.value)) + .collect::<Vec<_>>(), + vec![(2, 120.0), (3, 130.0)] + ); + } + async fn auth_test_state(config_path: std::path::PathBuf, config: UiConfig) -> HttpState { config_store::save(&config_path, &config).unwrap(); let wallet_path = config_path.with_file_name("wallet.json");