mirror of
https://github.com/logos-blockchain/logos-blockchain-pocs.git
synced 2026-01-04 06:03:08 +00:00
441 lines
14 KiB
Rust
441 lines
14 KiB
Rust
//! Storage capacity estimator
|
|
//!
|
|
//! Computes block and DA storage requirements for various time periods and
|
|
//! network scenarios. Produces summaries, time breakdowns, and simple hardware
|
|
//! recommendations.
|
|
|
|
use std::{collections::HashMap, fs};
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
use storage_benchmarks::BenchConfig;
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct TimePeriod {
|
|
/// Number of days represented by the period
|
|
pub days: u64,
|
|
/// Human-readable label (e.g., "1 year")
|
|
pub description: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct NetworkConfig {
|
|
/// Block time in seconds
|
|
pub block_time_seconds: u64,
|
|
/// Average block size in bytes
|
|
pub avg_block_size_bytes: u64,
|
|
/// Total DA subnets
|
|
pub total_subnets: u64,
|
|
/// DA share size in bytes
|
|
pub da_share_size_bytes: u64,
|
|
/// DA commitment size in bytes
|
|
pub da_commitment_size_bytes: u64,
|
|
/// Shares per blob
|
|
pub shares_per_blob: u64,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct NetworkScenario {
|
|
/// Scenario name
|
|
pub name: String,
|
|
/// Blobs per block
|
|
pub blobs_per_block: u64,
|
|
/// Total validators used to estimate DA responsibility
|
|
pub total_validators: u64,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct CalculationConfig {
|
|
/// Time window for the calculation
|
|
pub time_period: TimePeriod,
|
|
/// Network parameters used across scenarios
|
|
pub network: NetworkConfig,
|
|
/// Scenarios to evaluate
|
|
pub scenarios: Vec<NetworkScenario>,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
pub struct BlockDataResults {
|
|
/// Blocks produced per day
|
|
pub blocks_per_day: u64,
|
|
/// Total blocks in the period
|
|
pub blocks_for_period: u64,
|
|
/// Average block size in KiB
|
|
pub avg_block_size_kb: u64,
|
|
/// Total block data size in GiB for the period
|
|
pub total_block_data_gb: f64,
|
|
/// Period label
|
|
pub time_period_description: String,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
pub struct ScenarioResults {
|
|
/// Scenario label
|
|
pub scenario_name: String,
|
|
/// Blobs per block for this scenario
|
|
pub blobs_per_block: u64,
|
|
/// Total validators
|
|
pub total_validators: u64,
|
|
/// Typical subnets assigned per validator
|
|
pub typical_subnets_per_validator: u64,
|
|
/// Percent of subnets likely assigned to a validator
|
|
pub subnet_assignment_percent: f64,
|
|
/// Count of DA shares stored by the validator over the period
|
|
pub shares_stored_count: u64,
|
|
/// Count of blobs assigned over the period
|
|
pub blobs_assigned_count: u64,
|
|
/// DA shares size in GiB
|
|
pub da_shares_gb: f64,
|
|
/// DA commitments size in GiB
|
|
pub da_commitments_gb: f64,
|
|
/// Total DA data size in GiB
|
|
pub total_da_gb: f64,
|
|
/// Total validator storage in GiB (blocks + DA)
|
|
pub total_validator_storage_gb: f64,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
pub struct TimeBreakdown {
|
|
/// Sequential period index
|
|
pub period_number: u64,
|
|
/// Label (Month/Week/Day N)
|
|
pub period_description: String,
|
|
/// Cumulative storage at this step in GiB
|
|
pub cumulative_gb: f64,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
pub struct HardwareRecommendation {
|
|
/// Scenario label
|
|
pub scenario: String,
|
|
/// Required storage in GiB for the period
|
|
pub storage_gb_for_period: u64,
|
|
/// Recommended device size
|
|
pub recommended_storage: String,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
pub struct StorageCalculationResults {
|
|
/// Input config used to compute results
|
|
pub calculation_config: CalculationConfig,
|
|
/// Aggregate block data for the period
|
|
pub block_data: BlockDataResults,
|
|
/// Per-scenario storage summaries
|
|
pub scenarios: Vec<ScenarioResults>,
|
|
/// Time-based accumulation for visualization
|
|
pub time_breakdown: Vec<TimeBreakdown>,
|
|
/// Simple hardware sizing suggestions
|
|
pub hardware_recommendations: Vec<HardwareRecommendation>,
|
|
/// Notes for stress testing considerations
|
|
pub stress_testing_notes: Vec<String>,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
pub struct CapacityCalculationReport {
|
|
pub calculation_results: std::collections::HashMap<String, StorageCalculationResults>,
|
|
pub summary: CalculationSummary,
|
|
pub metadata: ReportMetadata,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
pub struct CalculationSummary {
|
|
pub scenarios_calculated: usize,
|
|
pub total_time_periods: usize,
|
|
pub calculation_timestamp: String,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
pub struct ReportMetadata {
|
|
pub tool: String,
|
|
pub version: String,
|
|
pub description: String,
|
|
}
|
|
|
|
impl Default for NetworkConfig {
|
|
fn default() -> Self {
|
|
Self {
|
|
block_time_seconds: 30,
|
|
avg_block_size_bytes: 34_371,
|
|
total_subnets: 2048,
|
|
da_share_size_bytes: 1_024,
|
|
da_commitment_size_bytes: 220_000,
|
|
shares_per_blob: 512,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Default for CalculationConfig {
|
|
fn default() -> Self {
|
|
Self {
|
|
time_period: TimePeriod {
|
|
days: 365,
|
|
description: "1 year".to_string(),
|
|
},
|
|
network: NetworkConfig::default(),
|
|
scenarios: vec![
|
|
NetworkScenario {
|
|
name: "Conservative".to_string(),
|
|
blobs_per_block: 50,
|
|
total_validators: 2000,
|
|
},
|
|
NetworkScenario {
|
|
name: "Active".to_string(),
|
|
blobs_per_block: 100,
|
|
total_validators: 2000,
|
|
},
|
|
NetworkScenario {
|
|
name: "High Activity".to_string(),
|
|
blobs_per_block: 200,
|
|
total_validators: 3000,
|
|
},
|
|
NetworkScenario {
|
|
name: "Peak".to_string(),
|
|
blobs_per_block: 500,
|
|
total_validators: 5000,
|
|
},
|
|
],
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Compute storage with blob/share separation for DA
|
|
///
|
|
/// - Includes blocks, DA shares, and commitments
|
|
/// - Returns summaries, breakdowns, and recommendations
|
|
fn calculate_storage_requirements(config: &CalculationConfig) -> StorageCalculationResults {
|
|
let blocks_per_day = (24 * 60 * 60) / config.network.block_time_seconds;
|
|
let total_blocks_for_period = config.time_period.days * blocks_per_day;
|
|
|
|
let block_data_for_period_gb = (total_blocks_for_period as f64
|
|
* config.network.avg_block_size_bytes as f64)
|
|
/ (1024.0 * 1024.0 * 1024.0);
|
|
|
|
let block_data = BlockDataResults {
|
|
blocks_per_day,
|
|
blocks_for_period: total_blocks_for_period,
|
|
avg_block_size_kb: config.network.avg_block_size_bytes / 1024,
|
|
total_block_data_gb: block_data_for_period_gb,
|
|
time_period_description: config.time_period.description.clone(),
|
|
};
|
|
|
|
let mut scenarios = Vec::new();
|
|
let mut scenario_storage_map = HashMap::new();
|
|
|
|
for scenario in &config.scenarios {
|
|
let typical_subnets_per_validator =
|
|
config.network.total_subnets / (scenario.total_validators / 10).max(1);
|
|
let subnet_assignment_probability =
|
|
typical_subnets_per_validator as f64 / config.network.total_subnets as f64;
|
|
|
|
let total_blobs_for_period = total_blocks_for_period * scenario.blobs_per_block;
|
|
|
|
let validator_assigned_blobs =
|
|
(total_blobs_for_period as f64 * subnet_assignment_probability) as u64;
|
|
|
|
let shares_per_assigned_blob =
|
|
config.network.shares_per_blob / config.network.total_subnets;
|
|
let total_shares_stored = validator_assigned_blobs * shares_per_assigned_blob.max(1);
|
|
let da_shares_size_gb = (total_shares_stored * config.network.da_share_size_bytes) as f64
|
|
/ (1024.0 * 1024.0 * 1024.0);
|
|
|
|
let da_commitments_size_gb = (validator_assigned_blobs
|
|
* config.network.da_commitment_size_bytes) as f64
|
|
/ (1024.0 * 1024.0 * 1024.0);
|
|
|
|
let total_da_size_gb = da_shares_size_gb + da_commitments_size_gb;
|
|
let total_storage_for_period = block_data_for_period_gb + total_da_size_gb;
|
|
|
|
scenario_storage_map.insert(scenario.name.clone(), total_da_size_gb);
|
|
|
|
scenarios.push(ScenarioResults {
|
|
scenario_name: scenario.name.clone(),
|
|
blobs_per_block: scenario.blobs_per_block,
|
|
total_validators: scenario.total_validators,
|
|
typical_subnets_per_validator,
|
|
subnet_assignment_percent: subnet_assignment_probability * 100.0,
|
|
shares_stored_count: total_shares_stored,
|
|
blobs_assigned_count: validator_assigned_blobs,
|
|
da_shares_gb: da_shares_size_gb,
|
|
da_commitments_gb: da_commitments_size_gb,
|
|
total_da_gb: total_da_size_gb,
|
|
total_validator_storage_gb: total_storage_for_period,
|
|
});
|
|
}
|
|
|
|
let breakdown_periods = if config.time_period.days >= 365 {
|
|
12
|
|
} else if config.time_period.days >= 30 {
|
|
config.time_period.days / 7
|
|
} else {
|
|
config.time_period.days
|
|
};
|
|
|
|
let first_scenario_da_gb = scenario_storage_map.values().next().copied().unwrap_or(0.0);
|
|
let total_gb_per_period = block_data_for_period_gb + first_scenario_da_gb;
|
|
let increment_gb = total_gb_per_period / breakdown_periods as f64;
|
|
|
|
let mut time_breakdown = Vec::new();
|
|
for period in 1..=breakdown_periods {
|
|
let cumulative_gb = increment_gb * period as f64;
|
|
let period_desc = if config.time_period.days >= 365 {
|
|
format!("Month {}", period)
|
|
} else if config.time_period.days >= 30 {
|
|
format!("Week {}", period)
|
|
} else {
|
|
format!("Day {}", period)
|
|
};
|
|
|
|
time_breakdown.push(TimeBreakdown {
|
|
period_number: period,
|
|
period_description: period_desc,
|
|
cumulative_gb,
|
|
});
|
|
}
|
|
|
|
let mut hardware_recommendations = Vec::new();
|
|
for scenario in &scenarios {
|
|
let storage_gb = scenario.total_validator_storage_gb as u64;
|
|
let recommended = if storage_gb < 50 {
|
|
"100GB+ storage"
|
|
} else if storage_gb < 100 {
|
|
"200GB+ storage"
|
|
} else if storage_gb < 200 {
|
|
"500GB+ storage"
|
|
} else if storage_gb < 500 {
|
|
"1TB+ storage"
|
|
} else {
|
|
"2TB+ storage"
|
|
};
|
|
|
|
hardware_recommendations.push(HardwareRecommendation {
|
|
scenario: scenario.scenario_name.clone(),
|
|
storage_gb_for_period: storage_gb,
|
|
recommended_storage: recommended.to_string(),
|
|
});
|
|
}
|
|
|
|
let stress_testing_notes = vec![
|
|
"Memory pressure increases with database size".to_string(),
|
|
"Cache efficiency decreases as dataset grows beyond memory".to_string(),
|
|
"Compaction overhead increases with write frequency".to_string(),
|
|
"Range scan performance degrades with database size".to_string(),
|
|
"Storage benchmarks should test multi-GB datasets for realism".to_string(),
|
|
format!(
|
|
"Test with datasets representing {}-{} days of operation",
|
|
config.time_period.days / 4,
|
|
config.time_period.days / 2
|
|
),
|
|
];
|
|
|
|
StorageCalculationResults {
|
|
calculation_config: config.clone(),
|
|
block_data,
|
|
scenarios,
|
|
time_breakdown,
|
|
hardware_recommendations,
|
|
stress_testing_notes,
|
|
}
|
|
}
|
|
|
|
fn main() {
|
|
let default_config = CalculationConfig::default();
|
|
|
|
let monthly_config = CalculationConfig {
|
|
time_period: TimePeriod {
|
|
days: 30,
|
|
description: "30 days".to_string(),
|
|
},
|
|
network: NetworkConfig::default(),
|
|
scenarios: vec![
|
|
NetworkScenario {
|
|
name: "Testnet Conservative".to_string(),
|
|
blobs_per_block: 25,
|
|
total_validators: 100,
|
|
},
|
|
NetworkScenario {
|
|
name: "Testnet Active".to_string(),
|
|
blobs_per_block: 50,
|
|
total_validators: 100,
|
|
},
|
|
],
|
|
};
|
|
|
|
let weekly_config = CalculationConfig {
|
|
time_period: TimePeriod {
|
|
days: 7,
|
|
description: "1 week".to_string(),
|
|
},
|
|
network: NetworkConfig {
|
|
block_time_seconds: 15,
|
|
shares_per_blob: 256,
|
|
..NetworkConfig::default()
|
|
},
|
|
scenarios: vec![NetworkScenario {
|
|
name: "Development".to_string(),
|
|
blobs_per_block: 10,
|
|
total_validators: 10,
|
|
}],
|
|
};
|
|
|
|
let configs = vec![
|
|
("annual", default_config),
|
|
("monthly", monthly_config),
|
|
("weekly", weekly_config),
|
|
];
|
|
|
|
let mut all_results = HashMap::new();
|
|
|
|
for (name, config) in configs {
|
|
let results = calculate_storage_requirements(&config);
|
|
all_results.insert(name, results);
|
|
}
|
|
|
|
save_capacity_results(&all_results);
|
|
|
|
match serde_json::to_string_pretty(&all_results) {
|
|
Ok(json) => println!("{}", json),
|
|
Err(e) => eprintln!("Error serializing results: {}", e),
|
|
}
|
|
}
|
|
|
|
fn save_capacity_results(all_results: &HashMap<&str, StorageCalculationResults>) {
|
|
let results_dir = BenchConfig::results_path();
|
|
let timestamp = chrono::Utc::now().format("%Y%m%d_%H%M%S");
|
|
let filename = format!("storage_capacity_calculation_{}.json", timestamp);
|
|
let filepath = results_dir.join(filename);
|
|
|
|
let calculation_results: std::collections::HashMap<String, StorageCalculationResults> =
|
|
all_results
|
|
.iter()
|
|
.map(|(k, v)| (k.to_string(), v.clone()))
|
|
.collect();
|
|
|
|
let report = CapacityCalculationReport {
|
|
calculation_results,
|
|
summary: CalculationSummary {
|
|
scenarios_calculated: all_results.len(),
|
|
total_time_periods: all_results
|
|
.values()
|
|
.map(|r| r.scenarios.len())
|
|
.sum::<usize>(),
|
|
calculation_timestamp: chrono::Utc::now().to_rfc3339(),
|
|
},
|
|
metadata: ReportMetadata {
|
|
tool: "storage_capacity_calculator".to_string(),
|
|
version: env!("CARGO_PKG_VERSION").to_string(),
|
|
description: "Storage capacity estimates for Nomos validator scenarios".to_string(),
|
|
},
|
|
};
|
|
|
|
match fs::write(&filepath, serde_json::to_string_pretty(&report).unwrap()) {
|
|
Ok(_) => eprintln!(
|
|
"Capacity calculation results saved to: {}",
|
|
filepath.display()
|
|
),
|
|
Err(e) => eprintln!(
|
|
"Failed to save capacity results to {}: {}",
|
|
filepath.display(),
|
|
e
|
|
),
|
|
}
|
|
}
|