explorerd/service/statistics.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
/* This file is part of DarkFi (https://dark.fi)
*
* Copyright (C) 2020-2025 Dyne.org foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use tinyjson::JsonValue;
use darkfi::{Error, Result};
use darkfi_sdk::blockchain::block_epoch;
use crate::{service::ExplorerService, store::metrics::GasMetrics};
#[derive(Debug, Clone)]
/// Structure representing basic statistic extracted from the database.
pub struct BaseStatistics {
/// Current blockchain height
pub height: u32,
/// Current blockchain epoch (based on current height)
pub epoch: u8,
/// Blockchains' last block hash
pub last_block: String,
/// Blockchain total blocks
pub total_blocks: usize,
/// Blockchain total transactions
pub total_txs: usize,
}
impl BaseStatistics {
/// Auxiliary function to convert `BaseStatistics` into a `JsonValue` array.
pub fn to_json_array(&self) -> JsonValue {
JsonValue::Array(vec![
JsonValue::Number(self.height as f64),
JsonValue::Number(self.epoch as f64),
JsonValue::String(self.last_block.clone()),
JsonValue::Number(self.total_blocks as f64),
JsonValue::Number(self.total_txs as f64),
])
}
}
/// Structure representing metrics extracted from the database.
#[derive(Default)]
pub struct MetricStatistics {
/// Metrics used to store explorer statistics
pub metrics: GasMetrics,
}
impl MetricStatistics {
pub fn new(metrics: GasMetrics) -> Self {
Self { metrics }
}
/// Auxiliary function to convert [`MetricStatistics`] into a [`JsonValue`] array.
pub fn to_json_array(&self) -> JsonValue {
JsonValue::Array(vec![
JsonValue::Number(self.metrics.avg_total_gas_used() as f64),
JsonValue::Number(self.metrics.total_gas.min as f64),
JsonValue::Number(self.metrics.total_gas.max as f64),
JsonValue::Number(self.metrics.avg_wasm_gas_used() as f64),
JsonValue::Number(self.metrics.wasm_gas.min as f64),
JsonValue::Number(self.metrics.wasm_gas.max as f64),
JsonValue::Number(self.metrics.avg_zk_circuits_gas_used() as f64),
JsonValue::Number(self.metrics.zk_circuits_gas.min as f64),
JsonValue::Number(self.metrics.zk_circuits_gas.max as f64),
JsonValue::Number(self.metrics.avg_signatures_gas_used() as f64),
JsonValue::Number(self.metrics.signatures_gas.min as f64),
JsonValue::Number(self.metrics.signatures_gas.max as f64),
JsonValue::Number(self.metrics.avg_deployments_gas_used() as f64),
JsonValue::Number(self.metrics.deployments_gas.min as f64),
JsonValue::Number(self.metrics.deployments_gas.max as f64),
JsonValue::Number(self.metrics.timestamp.inner() as f64),
])
}
}
impl ExplorerService {
/// Fetches the latest [`BaseStatistics`] from the explorer database, or returns `None` if no block exists.
pub fn get_base_statistics(&self) -> Result<Option<BaseStatistics>> {
let last_block = self.last_block();
Ok(last_block
// Throw database error if last_block retrievals fails
.map_err(|e| {
Error::DatabaseError(format!(
"[get_base_statistics] Retrieving last block failed: {:?}",
e
))
})?
// Calculate base statistics and return result
.map(|(height, header_hash)| {
let epoch = block_epoch(height);
let total_blocks = self.get_block_count();
let total_txs = self.get_transaction_count();
BaseStatistics { height, epoch, last_block: header_hash, total_blocks, total_txs }
}))
}
/// Fetches the latest metrics from the explorer database, returning a vector of
/// [`MetricStatistics`] if found, or an empty Vec if no metrics exist.
pub async fn get_metrics_statistics(&self) -> Result<Vec<MetricStatistics>> {
// Fetch all metrics from the metrics store, handling any potential errors
let metrics = self.db.metrics_store.get_all_metrics().map_err(|e| {
Error::DatabaseError(format!(
"[get_metrics_statistics] Retrieving metrics failed: {:?}",
e
))
})?;
// Transform the fetched metrics into `MetricStatistics`, collect them into a vector
let metric_statistics =
metrics.iter().map(|metrics| MetricStatistics::new(metrics.clone())).collect();
Ok(metric_statistics)
}
/// Fetches the latest metrics from the explorer database, returning [`MetricStatistics`] if found,
/// or zero-initialized defaults when not.
pub async fn get_latest_metrics_statistics(&self) -> Result<MetricStatistics> {
// Fetch the latest metrics, handling any potential errors
match self.db.metrics_store.get_last().map_err(|e| {
Error::DatabaseError(format!(
"[get_metrics_statistics] Retrieving latest metrics failed: {:?}",
e
))
})? {
// Transform metrics into `MetricStatistics` when found
Some((_, metrics)) => Ok(MetricStatistics::new(metrics)),
// Return default statistics when no metrics exist
None => Ok(MetricStatistics::default()),
}
}
}