diff --git a/crates/fuel-gas-price-algorithm/gas-price-analysis/.gitignore b/crates/fuel-gas-price-algorithm/gas-price-analysis/.gitignore deleted file mode 100644 index 69795b805a7..00000000000 --- a/crates/fuel-gas-price-algorithm/gas-price-analysis/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -Cargo.lock -gas-prices.png -predefined_data/ -charts/ \ No newline at end of file diff --git a/crates/fuel-gas-price-algorithm/gas-price-analysis/Cargo.toml b/crates/fuel-gas-price-algorithm/gas-price-analysis/Cargo.toml deleted file mode 100644 index 200977ff82a..00000000000 --- a/crates/fuel-gas-price-algorithm/gas-price-analysis/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "fuel-gas-price-analysis" -version = "0.1.0" -edition = "2021" -publish = false - -[workspace] - -[dependencies] -anyhow = "1.0.86" -clap = { version = "4.5.16", features = ["derive"] } -csv = "1.3.0" -fuel-gas-price-algorithm = { path = ".." } -futures = "0.3.30" -plotters = "0.3.5" -rand = "0.8.5" -rand_distr = "0.4.3" -serde = { version = "1.0.209", features = ["derive"] } -tokio = { version = "1.40.0", features = ["macros", "rt", "rt-multi-thread"] } diff --git a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/charts.rs b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/charts.rs deleted file mode 100644 index aa3f401ddcc..00000000000 --- a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/charts.rs +++ /dev/null @@ -1,377 +0,0 @@ -use super::*; -use std::{ - fs, - path::PathBuf, -}; - -const ONE_GWEI: u64 = 1_000_000_000; - -pub fn draw_chart( - results: SimulationResults, - p_comp: i64, - d_comp: i64, - da_finalization_period: usize, - file_path: &str, -) -> anyhow::Result<()> { - let SimulationResults { - gas_prices, - exec_gas_prices, - da_gas_prices, - fullness, - bytes_and_costs, - actual_profit, - projected_profit, - pessimistic_costs, - } = results; - - let plot_width = 640 * 2 * 2; - let plot_height = 480 * 3; - - let path: PathBuf = file_path.into(); - - if path.is_dir() { - println!("Creating chart at: {}", file_path); - fs::create_dir_all(file_path)?; - } else { - let new_path = path.parent().ok_or(anyhow::anyhow!("Path has no parent"))?; - println!("Creating chart at: {}", new_path.display()); - fs::create_dir_all(new_path)?; - } - let root = - BitMapBackend::new(file_path, (plot_width, plot_height)).into_drawing_area(); - root.fill(&WHITE).unwrap(); - let (window_one, lower) = root.split_vertically(plot_height / 4); - let (window_two, new_lower) = lower.split_vertically(plot_height / 4); - let (window_three, window_four) = new_lower.split_vertically(plot_height / 4); - - draw_fullness(&window_one, &fullness, "Fullness")?; - - draw_bytes_and_cost_per_block(&window_two, &bytes_and_costs, "Bytes Per Block")?; - - draw_profit( - &window_three, - &actual_profit, - &projected_profit, - &pessimistic_costs, - &format!( - "Profit p_comp: {}, d_comp: {}, da_finalization_period: {}", - prettify_number(p_comp), - prettify_number(d_comp), - prettify_number(da_finalization_period) - ), - )?; - draw_gas_prices( - &window_four, - &gas_prices, - &exec_gas_prices, - &da_gas_prices, - "Gas Prices", - )?; - - root.present()?; - - Ok(()) -} - -pub fn draw_gas_prices( - drawing_area: &DrawingArea, - gas_prices: &[u64], - _exec_gas_prices: &[u64], - da_gas_prices: &[u64], - title: &str, -) -> anyhow::Result<()> { - let gas_prices_gwei: Vec<_> = gas_prices.iter().map(|x| x / ONE_GWEI).collect(); - let _exec_gas_prices_gwei: Vec<_> = - _exec_gas_prices.iter().map(|x| x / ONE_GWEI).collect(); - let da_gas_prices_gwei: Vec<_> = da_gas_prices.iter().map(|x| x / ONE_GWEI).collect(); - // const GAS_PRICE_COLOR: RGBColor = BLACK; - // const EXEC_GAS_PRICE_COLOR: RGBColor = RED; - const DA_GAS_PRICE_COLOR: RGBColor = BLUE; - let min = 0; - let max = *da_gas_prices_gwei.iter().max().unwrap(); - - let mut chart = ChartBuilder::on(drawing_area) - .caption(title, ("sans-serif", 50).into_font()) - .margin(5) - .x_label_area_size(40) - .y_label_area_size(100) - .right_y_label_area_size(100) - .build_cartesian_2d(0..gas_prices_gwei.len(), min..max)?; - - chart - .configure_mesh() - .y_desc("DA Gas Price") - .x_desc("Block") - .draw()?; - - // chart - // .draw_series(LineSeries::new( - // gas_prices.iter().enumerate().map(|(x, y)| (x, *y)), - // GAS_PRICE_COLOR, - // )) - // .unwrap() - // .label("Gas Price") - // .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], GAS_PRICE_COLOR)); - - // Draw the exec gas prices - // chart - // .draw_series(LineSeries::new( - // exec_gas_prices.iter().enumerate().map(|(x, y)| (x, *y)), - // EXEC_GAS_PRICE_COLOR, - // )) - // .unwrap() - // .label("Exec Gas Price") - // .legend(|(x, y)| { - // PathElement::new(vec![(x, y), (x + 20, y)], EXEC_GAS_PRICE_COLOR) - // }); - - // Draw the da gas prices - chart - .draw_series(LineSeries::new( - da_gas_prices_gwei.iter().enumerate().map(|(x, y)| (x, *y)), - DA_GAS_PRICE_COLOR, - ))? - .label("DA Gas Price") - .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], DA_GAS_PRICE_COLOR)); - - chart - .configure_series_labels() - .background_style(WHITE.mix(0.8)) - .border_style(BLACK) - .draw()?; - - Ok(()) -} - -pub fn draw_fullness( - drawing_area: &DrawingArea, - fullness: &[(u64, u64)], - title: &str, -) -> anyhow::Result<()> { - const FULLNESS_COLOR: RGBColor = BLACK; - - let min = 0; - let max = 100; - - let mut chart = ChartBuilder::on(drawing_area) - .caption(title, ("sans-serif", 50).into_font()) - .margin(5) - .x_label_area_size(40) - .y_label_area_size(100) - .right_y_label_area_size(100) - .build_cartesian_2d(0..fullness.len(), min..max)?; - - chart - .configure_mesh() - .y_desc("Fullness Percentage") - .x_desc("Block") - .draw()?; - - chart - .draw_series(LineSeries::new( - fullness - .iter() - .map(|(x, y)| (*x as f64 / *y as f64) * 100.) - .map(|x| x as i32) - .enumerate(), - FULLNESS_COLOR, - )) - .unwrap() - .label("Fullness") - .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], FULLNESS_COLOR)); - - chart - .configure_series_labels() - .background_style(WHITE.mix(0.8)) - .border_style(BLACK) - .draw()?; - - Ok(()) -} - -pub fn draw_bytes_and_cost_per_block( - drawing_area: &DrawingArea, - bytes_and_costs_per_block: &[(u32, u64)], - title: &str, -) -> anyhow::Result<()> { - const BYTES_PER_BLOCK_COLOR: RGBColor = BLACK; - let (bytes, costs): (Vec, Vec) = - bytes_and_costs_per_block.iter().cloned().unzip(); - let costs_gwei: Vec<_> = costs.into_iter().map(|x| x / ONE_GWEI).collect(); - - let min = 0; - let max_left = *bytes.iter().max().unwrap() as u64; - let max_right = *costs_gwei.iter().max().unwrap(); - - let mut chart = ChartBuilder::on(drawing_area) - .caption(title, ("sans-serif", 50).into_font()) - .margin(5) - .x_label_area_size(40) - .y_label_area_size(100) - .right_y_label_area_size(100) - .build_cartesian_2d(0..bytes_and_costs_per_block.len(), min..max_left) - .unwrap() - .set_secondary_coord(0..bytes_and_costs_per_block.len(), min..max_right); - - chart - .configure_mesh() - .y_desc("Bytes Per Block") - .x_desc("Block") - .draw() - .unwrap(); - - chart - .configure_secondary_axes() - .y_desc("Cost Per Block") - .draw() - .unwrap(); - - chart - .draw_series(LineSeries::new( - bytes.iter().enumerate().map(|(x, y)| (x, *y as u64)), - BYTES_PER_BLOCK_COLOR, - )) - .unwrap() - .label("Bytes Per Block") - .legend(|(x, y)| { - PathElement::new(vec![(x, y), (x + 20, y)], BYTES_PER_BLOCK_COLOR) - }); - - chart - .draw_secondary_series(LineSeries::new( - costs_gwei.iter().enumerate().map(|(x, y)| (x, *y)), - RED, - )) - .unwrap() - .label("Cost Per Block") - .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], RED)); - - chart - .configure_series_labels() - .background_style(WHITE.mix(0.8)) - .border_style(BLACK) - .draw() - .unwrap(); - - Ok(()) -} - -fn one_gwei_i128() -> i128 { - i128::from(ONE_GWEI) -} - -fn one_gwei_u128() -> u128 { - u128::from(ONE_GWEI) -} - -pub fn draw_profit( - drawing_area: &DrawingArea, - actual_profit: &[i128], - projected_profit: &[i128], - pessimistic_block_costs: &[u128], - title: &str, -) -> anyhow::Result<()> { - const ACTUAL_PROFIT_COLOR: RGBColor = BLACK; - const PROJECTED_PROFIT_COLOR: RGBColor = RED; - const PESSIMISTIC_BLOCK_COST_COLOR: RGBColor = BLUE; - let actual_profit_gwei: Vec<_> = - actual_profit.iter().map(|x| x / one_gwei_i128()).collect(); - let projected_profit_gwei: Vec<_> = projected_profit - .iter() - .map(|x| x / one_gwei_i128()) - .collect(); - let pessimistic_block_costs_gwei: Vec<_> = pessimistic_block_costs - .iter() - .map(|x| x / one_gwei_u128()) - .collect(); - let min = *std::cmp::min( - actual_profit_gwei - .iter() - .min() - .ok_or(anyhow::anyhow!("Path has no parent"))?, - projected_profit_gwei - .iter() - .min() - .ok_or(anyhow::anyhow!("Path has no parent"))?, - ); - - let max = *std::cmp::max( - actual_profit_gwei - .iter() - .max() - .ok_or(anyhow::anyhow!("Path has no parent"))?, - projected_profit_gwei - .iter() - .max() - .ok_or(anyhow::anyhow!("Path has no parent"))?, - ); - - let mut chart = ChartBuilder::on(drawing_area) - .caption(title, ("sans-serif", 50).into_font()) - .margin(5) - .x_label_area_size(40) - .y_label_area_size(100) - .right_y_label_area_size(100) - .build_cartesian_2d(0..projected_profit.len(), min..max) - .unwrap() - .set_secondary_coord( - 0..projected_profit.len(), - 0..*pessimistic_block_costs_gwei.iter().max().unwrap(), - ); - - chart - .configure_mesh() - .y_desc("Profit (Gwei)") - .x_desc("Block") - .draw()?; - - chart - .configure_secondary_axes() - .y_desc("Pessimistic cost (Gwei)") - .draw()?; - - chart - .draw_series(LineSeries::new( - actual_profit_gwei.iter().enumerate().map(|(x, y)| (x, *y)), - ACTUAL_PROFIT_COLOR, - ))? - .label("Actual Profit") - .legend(|(x, y)| { - PathElement::new(vec![(x, y), (x + 20, y)], ACTUAL_PROFIT_COLOR) - }); - - chart - .draw_series(LineSeries::new( - projected_profit_gwei - .iter() - .enumerate() - .map(|(x, y)| (x, *y)), - PROJECTED_PROFIT_COLOR, - ))? - .label("Projected Profit") - .legend(|(x, y)| { - PathElement::new(vec![(x, y), (x + 20, y)], PROJECTED_PROFIT_COLOR) - }); - - // draw the block bytes - chart - .draw_secondary_series(LineSeries::new( - pessimistic_block_costs_gwei - .iter() - .enumerate() - .map(|(x, y)| (x, *y)), - PESSIMISTIC_BLOCK_COST_COLOR, - ))? - .label("Pessimistic Block Costs") - .legend(|(x, y)| { - PathElement::new(vec![(x, y), (x + 20, y)], PESSIMISTIC_BLOCK_COST_COLOR) - }); - - chart - .configure_series_labels() - .background_style(WHITE.mix(0.8)) - .border_style(BLACK) - .draw()?; - - Ok(()) -} diff --git a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs deleted file mode 100644 index 998f508151b..00000000000 --- a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs +++ /dev/null @@ -1,218 +0,0 @@ -use crate::{ - charts::draw_chart, - simulation::da_cost_per_byte::get_da_cost_per_byte_from_source, -}; -use plotters::prelude::*; -use rand::{ - rngs::StdRng, - Rng, - SeedableRng, -}; - -use plotters::coord::Shift; - -use crate::{ - optimisation::naive_optimisation, - simulation::{ - SimulationResults, - Simulator, - }, -}; - -mod optimisation; -mod simulation; - -mod charts; - -use clap::{ - Parser, - Subcommand, -}; - -#[derive(Parser)] -#[command(version, about, long_about = None)] -struct Arg { - #[command(subcommand)] - mode: Mode, - /// File path to save the chart to. Will not generate chart if left blank - #[arg(short, long)] - file_path: Option, - #[arg(short, long)] - /// DA finalization period in L2 blocks - da_finalization_period: usize, -} - -#[derive(Subcommand)] -enum Mode { - /// Run the simulation with the given P and D values - WithValues { - /// Source of blocks - #[command(subcommand)] - source: Source, - /// P value - p: i64, - /// D value - d: i64, - }, - /// Run an optimization to find the best P and D values - Optimization { - /// Source of blocks - #[command(subcommand)] - source: Source, - /// Number of iterations to run the optimization for - iterations: u64, - }, -} - -#[derive(Subcommand)] -enum Source { - Generated { - size: usize, - }, - Predefined { - file_path: String, - /// The number of L2 blocks to include from source - #[arg(short, long)] - sample_size: Option, - }, -} - -#[tokio::main] -async fn main() -> anyhow::Result<()> { - let args = Arg::parse(); - - const UPDATE_PERIOD: usize = 12; - - let da_finalization_period = args.da_finalization_period; - - let (results, (p_comp, d_comp)) = match args.mode { - Mode::WithValues { p, d, source } => { - let da_cost_per_byte = - get_da_cost_per_byte_from_source(source, UPDATE_PERIOD); - let size = da_cost_per_byte.len(); - println!( - "Running simulation with P: {}, D: {}, {} blocks and {} da_finalization_period", - prettify_number(p), - prettify_number(d), - prettify_number(size), - prettify_number(da_finalization_period) - ); - let simulator = Simulator::new(da_cost_per_byte); - let result = - simulator.run_simulation(p, d, UPDATE_PERIOD, da_finalization_period); - (result, (p, d)) - } - Mode::Optimization { iterations, source } => { - let da_cost_per_byte = - get_da_cost_per_byte_from_source(source, UPDATE_PERIOD); - let size = da_cost_per_byte.len(); - println!( - "Running optimization with {iterations} iterations and {size} blocks" - ); - let simulator = Simulator::new(da_cost_per_byte); - let (results, (p, d)) = naive_optimisation( - &simulator, - iterations as usize, - UPDATE_PERIOD, - da_finalization_period, - ) - .await; - println!( - "Optimization results: P: {}, D: {}, da_finalization_period: {}", - prettify_number(p), - prettify_number(d), - prettify_number(da_finalization_period) - ); - (results, (p, d)) - } - }; - - print_info(&results); - - if let Some(file_path) = &args.file_path { - draw_chart(results, p_comp, d_comp, da_finalization_period, file_path)?; - } - - Ok(()) -} - -fn print_info(results: &SimulationResults) { - let SimulationResults { - da_gas_prices, - actual_profit, - projected_profit, - .. - } = results; - - // Max actual profit - let (index, max_actual_profit) = actual_profit - .iter() - .enumerate() - .max_by(|(_, a), (_, b)| a.cmp(b)) - .unwrap(); - let eth = *max_actual_profit as f64 / (10_f64).powf(18.); - println!("max actual profit: {} ETH at {}", eth, index); - - // Max projected profit - let (index, max_projected_profit) = projected_profit - .iter() - .enumerate() - .max_by(|(_, a), (_, b)| a.cmp(b)) - .unwrap(); - let eth = *max_projected_profit as f64 / (10_f64).powf(18.); - println!("max projected profit: {} ETH at {}", eth, index); - - // Min actual profit - let (index, min_actual_profit) = actual_profit - .iter() - .enumerate() - .min_by(|(_, a), (_, b)| a.cmp(b)) - .unwrap(); - let eth = *min_actual_profit as f64 / (10_f64).powf(18.); - println!("min actual profit: {} ETH at {}", eth, index); - - // Min projected profit - let (index, min_projected_profit) = projected_profit - .iter() - .enumerate() - .min_by(|(_, a), (_, b)| a.cmp(b)) - .unwrap(); - let eth = *min_projected_profit as f64 / (10_f64).powf(18.); - println!("min projected profit: {} ETH at {}", eth, index); - - // Max DA Gas Price - let (index, max_da_gas_price) = da_gas_prices - .iter() - .enumerate() - .max_by(|(_, a), (_, b)| a.cmp(b)) - .unwrap(); - let eth = *max_da_gas_price as f64 / (10_f64).powf(9.); - println!( - "max DA gas price: {} Wei ({} Gwei) at {}", - max_da_gas_price, eth, index - ); - - // Min Da Gas Price - let (index, min_da_gas_price) = da_gas_prices - .iter() - .enumerate() - .min_by(|(_, a), (_, b)| a.cmp(b)) - .unwrap(); - let eth = *min_da_gas_price as f64 / (10_f64).powf(9.); - println!( - "min DA gas price: {} Wei ({} Gwei) at {}", - min_da_gas_price, eth, index - ); -} - -pub fn prettify_number(input: T) -> String { - input - .to_string() - .as_bytes() - .rchunks(3) - .rev() - .map(std::str::from_utf8) - .collect::, _>>() - .unwrap() - .join(",") // separator -} diff --git a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/optimisation.rs b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/optimisation.rs deleted file mode 100644 index 4943091fb84..00000000000 --- a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/optimisation.rs +++ /dev/null @@ -1,44 +0,0 @@ -use super::*; -use futures::future::join_all; - -fn da_pid_factors(size: usize) -> Vec<(i64, i64)> { - let mut rng = StdRng::seed_from_u64(10902); - (0usize..size) - .map(|_| { - let p = rng.gen_range(100_000..5_000_000); - let d = rng.gen_range(100_000..5_000_000); - (p, d) - }) - .collect() -} - -pub async fn naive_optimisation( - simulator: &Simulator, - iterations: usize, - update_period: usize, - da_recording_rate: usize, -) -> (SimulationResults, (i64, i64)) { - let tasks = da_pid_factors(iterations) - .into_iter() - .map(|(p, d)| { - let new_simulator = simulator.clone(); - let f = move || { - ( - new_simulator.run_simulation(p, d, update_period, da_recording_rate), - (p, d), - ) - }; - tokio::task::spawn_blocking(f) - }) - .collect::>(); - join_all(tasks) - .await - .into_iter() - .map(Result::unwrap) - .min_by_key(|(results, _)| { - let SimulationResults { actual_profit, .. } = results; - let err = actual_profit.iter().map(|p| p.abs()).sum::(); - err - }) - .unwrap() -} diff --git a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/simulation.rs b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/simulation.rs deleted file mode 100644 index 9ab8d905fe3..00000000000 --- a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/simulation.rs +++ /dev/null @@ -1,318 +0,0 @@ -use std::{ - collections::BTreeMap, - num::NonZeroU64, -}; - -use fuel_gas_price_algorithm::v1::{ - AlgorithmUpdaterV1, - L2ActivityTracker, -}; - -use super::*; - -pub mod da_cost_per_byte; - -pub struct SimulationResults { - pub gas_prices: Vec, - pub exec_gas_prices: Vec, - pub da_gas_prices: Vec, - pub fullness: Vec<(u64, u64)>, - pub bytes_and_costs: Vec<(u32, u64)>, - pub actual_profit: Vec, - pub projected_profit: Vec, - pub pessimistic_costs: Vec, -} - -#[derive(Clone, Debug)] -pub struct Simulator { - da_cost_per_byte: Vec, -} - -// (usize, ((u64, u64), &'a Option<(Range, u128) -struct BlockData { - fullness: u64, - bytes: u32, - maybe_da_bundle: Option, -} - -#[derive(Clone, Debug)] -struct Bundle { - heights: Vec, - bytes: u32, - cost: u128, -} - -impl Simulator { - pub fn new(da_cost_per_byte: Vec) -> Self { - Simulator { da_cost_per_byte } - } - - pub fn run_simulation( - &self, - da_p_component: i64, - da_d_component: i64, - update_period: usize, - da_finalization_rate: usize, - ) -> SimulationResults { - let capacity = 30_000_000; - let gas_per_byte = 63; - let max_block_bytes = capacity / gas_per_byte; - let size = self.da_cost_per_byte.len(); - let fullness_and_bytes = fullness_and_bytes_per_block(size, capacity); - let l2_blocks = fullness_and_bytes.clone().into_iter(); - let da_blocks = self.calculate_da_blocks( - update_period, - da_finalization_rate, - &fullness_and_bytes, - ); - - let blocks = l2_blocks - .zip(da_blocks.iter()) - .map(|((fullness, bytes), maybe_da_block)| BlockData { - fullness, - bytes, - maybe_da_bundle: maybe_da_block.clone(), - }) - .enumerate(); - - let updater = self.build_updater(da_p_component, da_d_component); - - self.execute_simulation( - capacity, - max_block_bytes, - fullness_and_bytes, - blocks, - updater, - ) - } - - fn build_updater( - &self, - da_p_component: i64, - da_d_component: i64, - ) -> AlgorithmUpdaterV1 { - // Scales the gas price internally, value is arbitrary - let gas_price_factor = 100; - let always_normal_activity = L2ActivityTracker::new_always_normal(); - AlgorithmUpdaterV1 { - min_exec_gas_price: 10_000_000, - min_da_gas_price: 100_000_000, - // Change to adjust where the exec gas price starts on block 0 - new_scaled_exec_price: 10_000_000 * gas_price_factor, - // Change to adjust where the da gas price starts on block 0 - new_scaled_da_gas_price: 10_000_000 * gas_price_factor, - gas_price_factor: NonZeroU64::new(gas_price_factor).unwrap(), - l2_block_height: 0, - // Choose the ideal fullness percentage for the L2 block - l2_block_fullness_threshold_percent: 50u8.into(), - // Increase to make the exec price change faster - exec_gas_price_change_percent: 2, - // Increase to make the da price change faster - max_da_gas_price_change_percent: 15, - total_da_rewards: 0, - // Change to adjust the cost per byte of the DA on block 0 - latest_da_cost_per_byte: 0, - projected_total_da_cost: 0, - latest_known_total_da_cost: 0, - da_p_component, - da_d_component, - last_profit: 0, - second_to_last_profit: 0, - l2_activity: always_normal_activity, - unrecorded_blocks_bytes: 0, - } - } - - fn execute_simulation( - &self, - capacity: u64, - max_block_bytes: u64, - fullness_and_bytes: Vec<(u64, u32)>, - blocks: impl Iterator, - mut updater: AlgorithmUpdaterV1, - ) -> SimulationResults { - let mut gas_prices = vec![]; - let mut exec_gas_prices = vec![]; - let mut da_gas_prices = vec![]; - let mut actual_reward_totals = vec![]; - let mut projected_cost_totals = vec![]; - let mut actual_costs = vec![]; - let mut pessimistic_costs = vec![]; - let mut unrecorded_blocks = BTreeMap::new(); - for (index, block_data) in blocks { - let BlockData { - fullness, - bytes, - maybe_da_bundle, - } = block_data; - let height = index as u32 + 1; - exec_gas_prices.push(updater.new_scaled_exec_price); - da_gas_prices.push(updater.new_scaled_da_gas_price); - let gas_price = updater.algorithm().calculate(); - gas_prices.push(gas_price); - let total_fee = gas_price as u128 * fullness as u128; - updater - .update_l2_block_data( - height, - fullness, - capacity.try_into().unwrap(), - bytes as u64, - total_fee, - &mut unrecorded_blocks, - ) - .unwrap(); - pessimistic_costs - .push(max_block_bytes as u128 * updater.latest_da_cost_per_byte); - actual_reward_totals.push(updater.total_da_rewards); - projected_cost_totals.push(updater.projected_total_da_cost); - - // Update DA blocks on the occasion there is one - if let Some(bundle) = maybe_da_bundle { - let Bundle { - heights, - bytes, - cost, - } = bundle; - for height in heights { - let block_heights: Vec = (height..(height) + 1).collect(); - updater - .update_da_record_data( - &block_heights, - bytes, - cost, - &mut unrecorded_blocks, - ) - .unwrap(); - actual_costs.push(updater.latest_known_total_da_cost) - } - } - } - let (fullness_without_capacity, bytes): (Vec<_>, Vec<_>) = - fullness_and_bytes.iter().cloned().unzip(); - let fullness: Vec<_> = fullness_without_capacity - .iter() - .map(|&fullness| (fullness, capacity)) - .collect(); - let bytes_and_costs: Vec<_> = bytes - .iter() - .zip(self.da_cost_per_byte.iter()) - .map(|(bytes, da_cost_per_byte)| (*bytes, *bytes as u64 * da_cost_per_byte)) - .collect(); - - let actual_profit: Vec = actual_costs - .iter() - .zip(actual_reward_totals.iter()) - .map(|(cost, reward)| *reward as i128 - *cost as i128) - .collect(); - - let projected_profit: Vec = projected_cost_totals - .iter() - .zip(actual_reward_totals.iter()) - .map(|(cost, reward)| *reward as i128 - *cost as i128) - .collect(); - - SimulationResults { - gas_prices, - exec_gas_prices, - da_gas_prices, - fullness, - bytes_and_costs, - actual_profit, - projected_profit, - pessimistic_costs, - } - } - - fn calculate_da_blocks( - &self, - da_recording_rate: usize, - da_finalization_rate: usize, - fullness_and_bytes: &[(u64, u32)], - ) -> Vec> { - let l2_blocks_with_no_da_blocks = - std::iter::repeat(None).take(da_finalization_rate); - let (_, da_blocks) = fullness_and_bytes - .iter() - .zip(self.da_cost_per_byte.iter()) - .enumerate() - .fold( - (vec![], vec![]), - |(mut delayed, mut recorded), - (index, ((_fullness, bytes), cost_per_byte))| { - let total_cost = *bytes as u64 * cost_per_byte; - let height = index as u32 + 1; - let converted = (height, bytes, total_cost); - delayed.push(converted); - if delayed.len() == da_recording_rate { - recorded.push(Some(delayed)); - (vec![], recorded) - } else { - recorded.push(None); - (delayed, recorded) - } - }, - ); - let da_block_ranges = da_blocks.into_iter().map(|maybe_recorded_blocks| { - maybe_recorded_blocks.map(|list| { - let heights_iter = list.iter().map(|(height, _, _)| *height); - let bytes = list.iter().map(|(_, bytes, _)| *bytes).sum(); - let cost = list.iter().map(|(_, _, cost)| *cost as u128).sum(); - Bundle { - heights: heights_iter.collect(), - bytes, - cost, - } - }) - }); - l2_blocks_with_no_da_blocks.chain(da_block_ranges).collect() - } -} - -// Naive Fourier series -fn gen_noisy_signal(input: f64, components: &[f64]) -> f64 { - components - .iter() - .fold(0f64, |acc, &c| acc + f64::sin(input / c)) - / components.len() as f64 -} - -fn noisy_fullness>(input: T) -> f64 -where - >::Error: core::fmt::Debug, -{ - const COMPONENTS: &[f64] = &[-30.0, 40.0, 700.0, -340.0, 400.0]; - let input = input.try_into().unwrap(); - gen_noisy_signal(input, COMPONENTS) -} - -fn fullness_and_bytes_per_block(size: usize, capacity: u64) -> Vec<(u64, u32)> { - let mut rng = StdRng::seed_from_u64(888); - - let fullness_noise: Vec<_> = std::iter::repeat(()) - .take(size) - .map(|_| rng.gen_range(-0.5..0.5)) - .collect(); - - const ROUGH_GAS_TO_BYTE_RATIO: f64 = 0.01; - let bytes_scale: Vec<_> = std::iter::repeat(()) - .take(size) - .map(|_| rng.gen_range(0.5..1.0)) - .map(|x| x * ROUGH_GAS_TO_BYTE_RATIO) - .collect(); - - (0usize..size) - .map(|val| val as f64) - .map(noisy_fullness) - .map(|signal| (0.01 * signal + 0.01) * capacity as f64) // Scale and shift so it's between 0 and capacity - .zip(fullness_noise) - .map(|(fullness, noise)| fullness + noise) - .map(|x| f64::min(x, capacity as f64)) - .map(|x| f64::max(x, 5.0)) - .zip(bytes_scale) - .map(|(fullness, bytes_scale)| { - let bytes = fullness * bytes_scale; - (fullness, bytes) - }) - .map(|(fullness, bytes)| (fullness as u64, std::cmp::max(bytes as u32, 1))) - .collect() -} diff --git a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/simulation/da_cost_per_byte.rs b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/simulation/da_cost_per_byte.rs deleted file mode 100644 index 911dcf713d8..00000000000 --- a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/simulation/da_cost_per_byte.rs +++ /dev/null @@ -1,95 +0,0 @@ -use crate::{ - prettify_number, - simulation::gen_noisy_signal, - Source, -}; -use std::iter; - -pub fn get_da_cost_per_byte_from_source( - source: Source, - update_period: usize, -) -> Vec { - match source { - Source::Generated { size } => arbitrary_cost_per_byte(size, update_period), - Source::Predefined { - file_path, - sample_size, - } => { - let original = get_costs_from_csv_file(&file_path, sample_size); - original - .into_iter() - .flat_map(|x| iter::repeat(x).take(update_period)) - .collect() - } - } -} - -#[allow(dead_code)] -#[derive(Debug, serde::Deserialize)] -struct Record { - block_number: u64, - excess_blob_gas: u64, - blob_gas_used: u64, - blob_fee_wei: u64, - blob_fee_wei_for_1_blob: u64, - blob_fee_wei_for_2_blobs: u64, - blob_fee_wei_for_3_blobs: u64, -} - -fn get_costs_from_csv_file(file_path: &str, sample_size: Option) -> Vec { - let mut rdr = csv::ReaderBuilder::new() - .has_headers(true) - .from_path(file_path) - .unwrap(); - let mut costs = vec![]; - let headers = csv::StringRecord::from(vec![ - "block_number", - "excess_blob_gas", - "blob_gas_used", - "blob_fee_wei", - "blob_fee_wei_for_1_blob", - "blob_fee_wei_for_2_blobs", - "blob_fee_wei_for_3_blobs", - ]); - let mut max_cost = 0; - for record in rdr.records().skip(1) { - const L2_BLOCKS_PER_L1_BLOCK: usize = 12; - if let Some(size) = sample_size { - if costs.len() >= size / L2_BLOCKS_PER_L1_BLOCK { - break; - } - }; - let record: Record = record.unwrap().deserialize(Some(&headers)).unwrap(); - let cost = record.blob_fee_wei; - if cost > max_cost { - max_cost = cost; - } - costs.push(cost); - } - println!("Max cost: {}", prettify_number(max_cost)); - costs -} - -fn noisy_eth_price>(input: T) -> f64 -where - >::Error: core::fmt::Debug, -{ - const COMPONENTS: &[f64] = &[3.0, 4.0]; - let input = input.try_into().unwrap(); - gen_noisy_signal(input, COMPONENTS) -} - -fn arbitrary_cost_per_byte(size: usize, update_period: usize) -> Vec { - let actual_size = size.div_ceil(update_period); - - const ROUGH_COST_AVG: f64 = 5.0; - - (0u32..actual_size as u32) - .map(noisy_eth_price) - .map(|x| x * ROUGH_COST_AVG + ROUGH_COST_AVG) // Sine wave is between -1 and 1, scale and shift - .map(|x| x as u64) - .map(|x| std::cmp::max(x, 1)) - .flat_map(|x| iter::repeat(x).take(update_period)) - .take(size) - .collect() -}