-
Notifications
You must be signed in to change notification settings - Fork 2.8k
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
Use bytes from unrecorded_blocks
rather from the block from DA
#2252
Changes from 17 commits
ffa4fdb
e82170c
fc3cf91
8cd972f
8fd6616
49e521e
cc28922
e00319c
93a6e98
41c26f0
364ce4a
e40a857
ea5469f
6a845ba
bfafd03
97fcc32
a51a32c
dd8a252
7880500
76b904a
5848462
8ec9c18
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -1,10 +1,10 @@ | ||||||
use fuel_gas_price_algorithm::v1::{ | ||||||
AlgorithmUpdaterV1, | ||||||
RecordedBlock, | ||||||
}; | ||||||
use std::num::NonZeroU64; | ||||||
|
||||||
use super::*; | ||||||
use fuel_gas_price_algorithm::v1::AlgorithmUpdaterV1; | ||||||
use std::{ | ||||||
collections::BTreeMap, | ||||||
num::NonZeroU64, | ||||||
ops::Range, | ||||||
}; | ||||||
|
||||||
pub mod da_cost_per_byte; | ||||||
|
||||||
|
@@ -90,7 +90,7 @@ impl Simulator { | |||||
latest_da_cost_per_byte: 0, | ||||||
projected_total_da_cost: 0, | ||||||
latest_known_total_da_cost_excess: 0, | ||||||
unrecorded_blocks: vec![], | ||||||
unrecorded_blocks: BTreeMap::new(), | ||||||
da_p_component, | ||||||
da_d_component, | ||||||
last_profit: 0, | ||||||
|
@@ -104,8 +104,7 @@ impl Simulator { | |||||
capacity: u64, | ||||||
max_block_bytes: u64, | ||||||
fullness_and_bytes: Vec<(u64, u64)>, | ||||||
// blocks: Enumerate<Zip<Iter<(u64, u64)>, Iter<Option<Vec<RecordedBlock>>>>>, | ||||||
blocks: impl Iterator<Item = (usize, ((u64, u64), &'a Option<Vec<RecordedBlock>>))>, | ||||||
blocks: impl Iterator<Item = (usize, ((u64, u64), &'a Option<(Range<u32>, u128)>))>, | ||||||
mut updater: AlgorithmUpdaterV1, | ||||||
) -> SimulationResults { | ||||||
let mut gas_prices = vec![]; | ||||||
|
@@ -121,7 +120,7 @@ impl Simulator { | |||||
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 * fullness; | ||||||
let total_fee = gas_price as u128 * fullness as u128; | ||||||
updater | ||||||
.update_l2_block_data( | ||||||
height, | ||||||
|
@@ -137,13 +136,13 @@ impl Simulator { | |||||
projected_cost_totals.push(updater.projected_total_da_cost); | ||||||
|
||||||
// Update DA blocks on the occasion there is one | ||||||
if let Some(da_blocks) = &da_block { | ||||||
let mut total_cost = updater.latest_known_total_da_cost_excess; | ||||||
for block in da_blocks { | ||||||
total_cost += block.block_cost as u128; | ||||||
actual_costs.push(total_cost); | ||||||
if let Some((range, cost)) = da_block { | ||||||
for height in range.to_owned() { | ||||||
updater | ||||||
.update_da_record_data(height..(height + 1), *cost) | ||||||
.unwrap(); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unfortunately, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Willing to reconsider if generics seems better. |
||||||
actual_costs.push(updater.latest_known_total_da_cost_excess) | ||||||
} | ||||||
updater.update_da_record_data(&da_blocks).unwrap(); | ||||||
} | ||||||
} | ||||||
let (fullness_without_capacity, bytes): (Vec<_>, Vec<_>) = | ||||||
|
@@ -187,7 +186,7 @@ impl Simulator { | |||||
da_recording_rate: usize, | ||||||
da_finalization_rate: usize, | ||||||
fullness_and_bytes: &Vec<(u64, u64)>, | ||||||
) -> Vec<Option<Vec<RecordedBlock>>> { | ||||||
) -> Vec<Option<(Range<u32>, u128)>> { | ||||||
let l2_blocks_with_no_da_blocks = | ||||||
std::iter::repeat(None).take(da_finalization_rate); | ||||||
let (_, da_blocks) = fullness_and_bytes | ||||||
|
@@ -199,12 +198,9 @@ impl Simulator { | |||||
|(mut delayed, mut recorded), | ||||||
(index, ((_fullness, bytes), cost_per_byte))| { | ||||||
let total_cost = *bytes * cost_per_byte; | ||||||
|
||||||
let height = index as u32 + 1; | ||||||
let converted = RecordedBlock { | ||||||
height, | ||||||
block_bytes: *bytes, | ||||||
block_cost: total_cost as u64, | ||||||
}; | ||||||
let converted = (height, bytes, total_cost); | ||||||
delayed.push(converted); | ||||||
if delayed.len() == da_recording_rate { | ||||||
recorded.push(Some(delayed)); | ||||||
|
@@ -215,7 +211,16 @@ impl Simulator { | |||||
} | ||||||
}, | ||||||
); | ||||||
l2_blocks_with_no_da_blocks.chain(da_blocks).collect() | ||||||
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 min = heights_iter.clone().min().unwrap(); | ||||||
let max = heights_iter.max().unwrap(); | ||||||
let cost: u128 = list.iter().map(|(_, _, cost)| *cost as u128).sum(); | ||||||
rafal-ch marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
(min..(max + 1), cost) | ||||||
}) | ||||||
}); | ||||||
l2_blocks_with_no_da_blocks.chain(da_block_ranges).collect() | ||||||
} | ||||||
} | ||||||
|
||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,14 @@ | ||
use crate::utils::cumulative_percentage_change; | ||
use std::{ | ||
cmp::max, | ||
collections::BTreeMap, | ||
num::NonZeroU64, | ||
ops::Div, | ||
ops::{ | ||
Div, | ||
Range, | ||
}, | ||
}; | ||
|
||
use crate::utils::cumulative_percentage_change; | ||
|
||
#[cfg(test)] | ||
mod tests; | ||
|
||
|
@@ -16,9 +19,11 @@ pub enum Error { | |
#[error("Skipped DA block update: expected {expected:?}, got {got:?}")] | ||
SkippedDABlock { expected: u32, got: u32 }, | ||
#[error("Could not calculate cost per byte: {bytes:?} bytes, {cost:?} cost")] | ||
CouldNotCalculateCostPerByte { bytes: u64, cost: u64 }, | ||
CouldNotCalculateCostPerByte { bytes: u128, cost: u128 }, | ||
#[error("Failed to include L2 block data: {0}")] | ||
FailedTooIncludeL2BlockData(String), | ||
#[error("L2 block expected but not found in unrecorded blocks: {0}")] | ||
L2BlockExpectedNotFound(u32), | ||
} | ||
|
||
#[derive(Debug, Clone, PartialEq)] | ||
|
@@ -94,6 +99,8 @@ impl AlgorithmV1 { | |
/// The DA portion also uses a moving average of the profits over the last `avg_window` blocks | ||
/// instead of the actual profit. Setting the `avg_window` to 1 will effectively disable the | ||
/// moving average. | ||
type Height = u32; | ||
type Bytes = u64; | ||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)] | ||
pub struct AlgorithmUpdaterV1 { | ||
// Execution | ||
|
@@ -144,8 +151,9 @@ pub struct AlgorithmUpdaterV1 { | |
pub second_to_last_profit: i128, | ||
/// The latest known cost per byte for recording blocks on the DA chain | ||
pub latest_da_cost_per_byte: u128, | ||
|
||
/// The unrecorded blocks that are used to calculate the projected cost of recording blocks | ||
pub unrecorded_blocks: Vec<BlockBytes>, | ||
pub unrecorded_blocks: BTreeMap<Height, Bytes>, | ||
} | ||
|
||
/// A value that represents a value between 0 and 100. Higher values are clamped to 100 | ||
|
@@ -176,29 +184,17 @@ impl core::ops::Deref for ClampedPercentage { | |
} | ||
} | ||
|
||
#[derive(Debug, Clone)] | ||
pub struct RecordedBlock { | ||
pub height: u32, | ||
pub block_bytes: u64, | ||
pub block_cost: u64, | ||
} | ||
|
||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)] | ||
pub struct BlockBytes { | ||
pub height: u32, | ||
pub block_bytes: u64, | ||
} | ||
|
||
impl AlgorithmUpdaterV1 { | ||
pub fn update_da_record_data( | ||
&mut self, | ||
blocks: &[RecordedBlock], | ||
height_range: Range<u32>, | ||
range_cost: u128, | ||
) -> Result<(), Error> { | ||
for block in blocks { | ||
self.da_block_update(block.height, block.block_bytes, block.block_cost)?; | ||
if !height_range.is_empty() { | ||
self.da_block_update(height_range, range_cost)?; | ||
self.recalculate_projected_cost(); | ||
// self.normalize_rewards_and_costs(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this commented-out on purpose? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. I added the TODO with the related issue. |
||
} | ||
self.recalculate_projected_cost(); | ||
self.normalize_rewards_and_costs(); | ||
Ok(()) | ||
} | ||
|
||
|
@@ -208,7 +204,7 @@ impl AlgorithmUpdaterV1 { | |
used: u64, | ||
capacity: NonZeroU64, | ||
block_bytes: u64, | ||
fee_wei: u64, | ||
fee_wei: u128, | ||
) -> Result<(), Error> { | ||
let expected = self.l2_block_height.saturating_add(1); | ||
if height != expected { | ||
|
@@ -226,6 +222,8 @@ impl AlgorithmUpdaterV1 { | |
// costs | ||
self.update_projected_cost(block_bytes); | ||
let projected_total_da_cost = self.clamped_projected_cost_as_i128(); | ||
|
||
// profit | ||
let last_profit = rewards.saturating_sub(projected_total_da_cost); | ||
self.update_last_profit(last_profit); | ||
|
||
|
@@ -234,19 +232,15 @@ impl AlgorithmUpdaterV1 { | |
self.update_da_gas_price(); | ||
|
||
// metadata | ||
self.unrecorded_blocks.push(BlockBytes { | ||
height, | ||
block_bytes, | ||
}); | ||
self.unrecorded_blocks.insert(height, block_bytes); | ||
Ok(()) | ||
} | ||
} | ||
|
||
fn update_rewards(&mut self, fee_wei: u64) { | ||
fn update_rewards(&mut self, fee_wei: u128) { | ||
let block_da_reward = self.da_portion_of_fee(fee_wei); | ||
self.total_da_rewards_excess = self | ||
.total_da_rewards_excess | ||
.saturating_add(block_da_reward.into()); | ||
self.total_da_rewards_excess = | ||
self.total_da_rewards_excess.saturating_add(block_da_reward); | ||
} | ||
|
||
fn update_projected_cost(&mut self, block_bytes: u64) { | ||
|
@@ -258,12 +252,11 @@ impl AlgorithmUpdaterV1 { | |
} | ||
|
||
// Take the `fee_wei` and return the portion of the fee that should be used for paying DA costs | ||
fn da_portion_of_fee(&self, fee_wei: u64) -> u64 { | ||
fn da_portion_of_fee(&self, fee_wei: u128) -> u128 { | ||
// fee_wei * (da_price / (exec_price + da_price)) | ||
let numerator = fee_wei.saturating_mul(self.descaled_da_price()); | ||
let denominator = self | ||
.descaled_exec_price() | ||
.saturating_add(self.descaled_da_price()); | ||
let numerator = fee_wei.saturating_mul(self.descaled_da_price() as u128); | ||
let denominator = (self.descaled_exec_price() as u128) | ||
.saturating_add(self.descaled_da_price() as u128); | ||
if denominator == 0 { | ||
0 | ||
} else { | ||
|
@@ -374,43 +367,60 @@ impl AlgorithmUpdaterV1 { | |
|
||
fn da_block_update( | ||
&mut self, | ||
height: u32, | ||
block_bytes: u64, | ||
block_cost: u64, | ||
height_range: Range<u32>, | ||
range_cost: u128, | ||
) -> Result<(), Error> { | ||
let expected = self.da_recorded_block_height.saturating_add(1); | ||
if height != expected { | ||
let first = height_range.start; | ||
if first != expected { | ||
Err(Error::SkippedDABlock { | ||
expected: self.da_recorded_block_height.saturating_add(1), | ||
got: height, | ||
expected, | ||
got: first, | ||
}) | ||
} else { | ||
let new_cost_per_byte: u128 = (block_cost as u128) | ||
.checked_div(block_bytes as u128) | ||
.ok_or(Error::CouldNotCalculateCostPerByte { | ||
bytes: block_bytes, | ||
cost: block_cost, | ||
})?; | ||
self.da_recorded_block_height = height; | ||
let last = height_range.end.saturating_sub(1); | ||
let range_bytes = self.drain_l2_block_bytes_for_range(height_range)?; | ||
let new_cost_per_byte: u128 = range_cost.checked_div(range_bytes).ok_or( | ||
Error::CouldNotCalculateCostPerByte { | ||
bytes: range_bytes, | ||
cost: range_cost, | ||
}, | ||
)?; | ||
self.da_recorded_block_height = last; | ||
let new_block_cost = self | ||
.latest_known_total_da_cost_excess | ||
.saturating_add(block_cost as u128); | ||
.saturating_add(range_cost); | ||
self.latest_known_total_da_cost_excess = new_block_cost; | ||
self.latest_da_cost_per_byte = new_cost_per_byte; | ||
Ok(()) | ||
} | ||
} | ||
|
||
fn drain_l2_block_bytes_for_range( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So, we can consider improving performance here still. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Switched to |
||
&mut self, | ||
height_range: Range<u32>, | ||
) -> Result<u128, Error> { | ||
let mut total: u128 = 0; | ||
for expected_height in height_range { | ||
let (actual_height, bytes) = self | ||
.unrecorded_blocks | ||
.pop_first() | ||
rafal-ch marked this conversation as resolved.
Show resolved
Hide resolved
|
||
.ok_or(Error::L2BlockExpectedNotFound(expected_height))?; | ||
if actual_height != expected_height { | ||
return Err(Error::L2BlockExpectedNotFound(expected_height)); | ||
} | ||
total = total.saturating_add(bytes as u128); | ||
} | ||
Ok(total) | ||
} | ||
|
||
fn recalculate_projected_cost(&mut self) { | ||
// remove all blocks that have been recorded | ||
self.unrecorded_blocks | ||
.retain(|block| block.height > self.da_recorded_block_height); | ||
// add the cost of the remaining blocks | ||
let projection_portion: u128 = self | ||
.unrecorded_blocks | ||
.iter() | ||
.map(|block| { | ||
(block.block_bytes as u128).saturating_mul(self.latest_da_cost_per_byte) | ||
.map(|(_, &bytes)| { | ||
(bytes as u128).saturating_mul(self.latest_da_cost_per_byte) | ||
}) | ||
.sum(); | ||
self.projected_total_da_cost = self | ||
|
@@ -436,9 +446,12 @@ impl AlgorithmUpdaterV1 { | |
} | ||
} | ||
|
||
// TODO: This breaks our simulation now that we are using an extended finalization period. We | ||
// can either keep this function and fix the simulation, or we might decide to remove it. | ||
// https://github.com/FuelLabs/fuel-core/issues/2264 | ||
// We only need to track the difference between the rewards and costs after we have true DA data | ||
// Normalize, or zero out the lower value and subtract it from the higher value | ||
fn normalize_rewards_and_costs(&mut self) { | ||
fn _normalize_rewards_and_costs(&mut self) { | ||
let (excess, projected_cost_excess) = | ||
if self.total_da_rewards_excess > self.latest_known_total_da_cost_excess { | ||
( | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: this type seems complicated enough that a named type should make it more readable
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.