Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Read simulation data from file #2540

Merged
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions crates/services/gas_price_service/simulation/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,10 @@ fuel-core-types = { version = "0.40.2", path = "../../../types", default-feature
fuel-gas-price-algorithm = { path = "../../../fuel-gas-price-algorithm" }
async-trait = "0.1.83"
tokio = "1.41.1"
serde = { version = "1.0.217", features = ["derive"] }
serde-reflection = "0.5.0"
csv = "1.3.1"
clap = { version = "4.5.24", features = ["derive"] }
itertools = "0.14.0"

[workspace]
62 changes: 50 additions & 12 deletions crates/services/gas_price_service/simulation/src/data_sources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ use fuel_core_gas_price_service::{
l2_block_source::L2BlockSource,
utils::BlockInfo,
},
v1::da_source_service::{
service::DaBlockCostsSource,
DaBlockCosts,
v1::{
algorithm::SharedV1Algorithm,
da_source_service::{
service::DaBlockCostsSource,
DaBlockCosts,
},
},
};

Expand All @@ -18,30 +21,65 @@ use fuel_core_types::fuel_types::BlockHeight;

pub struct SimulatedL2Blocks {
recv: tokio::sync::mpsc::Receiver<BlockInfo>,
shared_algo: SharedV1Algorithm,
}

impl SimulatedL2Blocks {
pub fn new(recv: tokio::sync::mpsc::Receiver<BlockInfo>) -> Self {
Self { recv }
pub fn new(
recv: tokio::sync::mpsc::Receiver<BlockInfo>,
shared_algo: SharedV1Algorithm,
) -> Self {
Self { recv, shared_algo }
}

pub fn new_with_sender() -> (Self, tokio::sync::mpsc::Sender<BlockInfo>) {
pub fn new_with_sender(
shared_algo: SharedV1Algorithm,
) -> (Self, tokio::sync::mpsc::Sender<BlockInfo>) {
let (send, recv) = tokio::sync::mpsc::channel(16);
(Self { recv }, send)
(Self { recv, shared_algo }, send)
}
}

#[async_trait]
impl L2BlockSource for SimulatedL2Blocks {
async fn get_l2_block(&mut self) -> GasPriceResult<BlockInfo> {
// TODO: do we want to modify these values to somehow reflect the previously chosen gas
// price better? We might be able to do that by having a handle to the shared algo.

self.recv.recv().await.ok_or({
let block = self.recv.recv().await.ok_or({
GasPriceError::CouldNotFetchL2Block {
source_error: anyhow::anyhow!("no more blocks; channel closed"),
}
})
})?;

let BlockInfo::Block {
gas_used,
height,
block_gas_capacity,
block_bytes,
..
} = block
else {
return Err(GasPriceError::CouldNotFetchL2Block {
source_error: anyhow::anyhow!("unexpected genesis block"),
});
};

let gas = gas_used;
let new_gas_price = self.shared_algo.next_gas_price();
let gas_price_factor = 1_150_000; // TODO: Read from CLI/config

let mut fee = (gas as u128).checked_mul(new_gas_price as u128).expect(
"Impossible to overflow because multiplication of two `u64` <= `u128`",
);
fee = fee.div_ceil(gas_price_factor as u128);

let block = BlockInfo::Block {
height,
gas_used,
block_gas_capacity,
block_bytes,
block_fees: fee.try_into().expect("overflow"),
gas_price: new_gas_price,
};
Ok(block)
}
}

Expand Down
137 changes: 128 additions & 9 deletions crates/services/gas_price_service/simulation/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,72 @@
use std::path::{
Path,
PathBuf,
};

use crate::service::{
get_service_controller,
ServiceController,
};
use clap::{
Parser,
Subcommand,
};
use csv::StringRecord;
use fuel_core_gas_price_service::{
common::utils::BlockInfo,
v1::da_source_service::DaBlockCosts,
};
use itertools::Itertools;
use serde_reflection::{
Samples,
Tracer,
TracerConfig,
};

pub mod data_sources;
pub mod service;

#[allow(dead_code)]
#[derive(Debug, serde::Deserialize)]
struct Record {
l1_block_number: u64,
l1_blob_fee_wei: u64,
l2_block_number: u64,
l2_gas_fullness: u64,
l2_gas_capacity: u64,
l2_byte_size: u64,
l2_byte_capacity: u64,
}

pub(crate) fn fields_of_struct_in_order<T>() -> Vec<String>
where
T: serde::de::DeserializeOwned,
{
let mut tracer = Tracer::new(TracerConfig::default());
let samples = Samples::new();
tracer.trace_type::<T>(&samples).unwrap();
let type_name = std::any::type_name::<T>().split("::").last().unwrap();
let registry = tracer.registry().unwrap();
let Some(serde_reflection::ContainerFormat::Struct(fields)) = registry.get(type_name)
else {
panic!("No fields?")
};

fields.iter().map(|f| f.name.clone()).collect()
}

#[derive(Subcommand, Debug)]
enum DataSource {
/// Load data from a CSV file.
File {
#[arg(short, long)]
path: PathBuf,
},
/// Generate arbitrary data (not supported yet).
Generated,
}

#[derive(Debug)]
pub struct Data {
inner: Vec<(BlockInfo, Option<DaBlockCosts>)>,
}
Expand All @@ -20,25 +77,80 @@ impl Data {
}
}

impl From<&Record> for BlockInfo {
fn from(value: &Record) -> Self {
return BlockInfo::Block {
height: value.l2_block_number.try_into().unwrap(),
gas_used: value.l2_gas_fullness,
block_gas_capacity: value.l2_gas_capacity,
block_bytes: value.l2_byte_size,
block_fees: 0, // Will be overwritten by the simulation code
gas_price: 0, // Will be overwritten by the simulation code
}
}
}

struct SimulationResults {}

fn get_data() -> anyhow::Result<Data> {
// TODO
let data = Data { inner: vec![] };
fn get_data(source: &DataSource) -> anyhow::Result<Data> {
let records = match source {
DataSource::File { path } => {
let headers = csv::StringRecord::from(fields_of_struct_in_order::<Record>());
let mut rdr = csv::ReaderBuilder::new()
.has_headers(true)
.from_path(path)
.unwrap();

let records: Result<Vec<Record>, _> = rdr
.records()
.map(|line_entry| {
let line = line_entry?;
line.deserialize(Some(&headers))
})
.collect();
records?
}
DataSource::Generated => unimplemented!(),
};

let mut data = vec![];
let groups = records.iter().chunk_by(|record| record.l1_block_number);
for (l1_block_number, block_records) in groups.into_iter() {
let blocks: Vec<_> = block_records.into_iter().collect();
let mut blocks_iter = blocks.iter().peekable();

while let Some(block_record) = blocks_iter.next() {
let l2_block: BlockInfo = (*block_record).into();
let da_block_costs = blocks_iter.peek().is_none().then(|| {
// TODO: Check if these are generated correctly.
let bundle_id: u32 = l1_block_number as u32; // Could be an arbitrary number, but we use L1 block number for convenience.
let bundle_size_bytes: u32 = 0; // Modify scrape tool to provide this
let range = blocks.first().unwrap().l2_block_number as u32
..=blocks.last().unwrap().l2_block_number as u32;
let blob_cost_wei = block_record.l1_blob_fee_wei as u128;

DaBlockCosts {
bundle_id,
l2_blocks: range,
bundle_size_bytes,
blob_cost_wei,
}
});
data.push((l2_block, da_block_costs));
}
}

let data = Data { inner: data };
Ok(data)
}

async fn simulation(
data: &Data,
service_controller: &mut ServiceController,
) -> anyhow::Result<SimulationResults> {
let res = SimulationResults {};
for (block, maybe_costs) in data.get_iter() {
service_controller.advance(block, maybe_costs).await?
// GET GAS PRICE

// MODIFY WITH GAS PRICE FACTOR

// RECORD LATEST VALUES
}
Ok(res)
}
Expand All @@ -48,9 +160,16 @@ fn display_results(_results: SimulationResults) -> anyhow::Result<()> {
Ok(())
}

#[derive(Parser, Debug)]
struct Args {
#[command(subcommand)]
data_source: DataSource,
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
let data = get_data()?;
let args = Args::parse();
let data = get_data(&args.data_source)?;
let mut service_controller = get_service_controller().await;
let results = simulation(&data, &mut service_controller).await?;
display_results(results)?;
Expand Down
5 changes: 3 additions & 2 deletions crates/services/gas_price_service/simulation/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ fn read_metadata_from_file(_metadata_path: &str) -> V1Metadata {
// TODO: read from file and/or CLI
V1Metadata {
new_scaled_exec_price: 0,
l2_block_height: 0,
l2_block_height: 999, // TODO: Use first L2 block height from the CSV
new_scaled_da_gas_price: 0,
gas_price_factor: NonZero::new(100).unwrap(),
total_da_rewards_excess: 0,
Expand Down Expand Up @@ -139,7 +139,8 @@ pub async fn get_service_controller() -> ServiceController {
let algo = algorithm_updater.algorithm();
let shared_algo = SharedV1Algorithm::new_with_algorithm(algo);

let (l2_block_source, l2_block_sender) = SimulatedL2Blocks::new_with_sender();
let (l2_block_source, l2_block_sender) =
SimulatedL2Blocks::new_with_sender(shared_algo.clone());
let (da_block_source, da_costs_sender) = SimulatedDACosts::new_with_sender();

let poll_interval = poll_interval();
Expand Down
Loading