-
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
Add new FuelGasPriceProvider
#1889
Changes from 21 commits
378dae8
cbebddc
bb24903
fb58594
dc193d5
4af3946
398884e
96f20d9
86f49a7
672f6cb
2e80dec
1134fd5
a818b9b
edf1cb3
a389a13
19a41ac
0f8edce
613c6bb
4b141b5
c3bae38
b693a2a
e9bf624
ada5897
473a1a1
8e5224d
883c695
b92ba83
ca4568d
a8cf8e1
c51e15e
9119cc0
509d64a
0cfb486
1526069
cc7f5e8
6df0036
f4941b1
9cb29c7
a7eff14
0c3b0e0
6349835
c03caf1
b22ab44
1b70458
a0f0a74
ba03b02
fd3fb69
102bdc9
80835e6
8ca7fe6
29a5025
d45d246
daa2c68
f5986aa
7746674
d5f4509
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 |
---|---|---|
@@ -0,0 +1,76 @@ | ||
use criterion::{ | ||
criterion_group, | ||
criterion_main, | ||
Criterion, | ||
}; | ||
use fuel_core::service::adapters::fuel_gas_price_provider::{ | ||
algorithm_adapter::FuelGasPriceAlgorithm, | ||
ports::{ | ||
BlockFullness, | ||
GasPriceAlgorithm, | ||
}, | ||
}; | ||
|
||
use plotters::prelude::*; | ||
use std::path::Path; | ||
|
||
fn gas_price_algo(_c: &mut Criterion) { | ||
// vary the gas price between 10_000 and 30_000 | ||
let da_recording_cost = (0u64..1000) | ||
.map(|val| f64::try_from(val).unwrap() / 100.) | ||
.map(f64::sin) | ||
.map(|x| x * 10_000. + 20_000.) | ||
.map(|x| x as u64); | ||
// for price in da_gas_prices { | ||
// dbg!(price); | ||
// } | ||
let algo = FuelGasPriceAlgorithm::new(1., f32::MAX); | ||
let mut gas_price = 100; | ||
let mut gas_prices = vec![gas_price]; | ||
let gas_spent = 200; | ||
let block_fullness = BlockFullness::new(50, 100); | ||
for cost in da_recording_cost { | ||
let gas_reward = gas_price * gas_spent; | ||
gas_price = algo.calculate_gas_price(gas_price, gas_reward, cost, block_fullness); | ||
gas_prices.push(gas_price); | ||
} | ||
|
||
for cost in da_recording_cost { | ||
let gas_reward = gas_price * gas_spent; | ||
gas_price = algo.calculate_gas_price(gas_price, gas_reward, cost, block_fullness); | ||
gas_prices.push(gas_price); | ||
} | ||
|
||
// Plotting code starts here | ||
let root = BitMapBackend::new("gas_prices.png", (640, 480)).into_drawing_area(); | ||
root.fill(&WHITE).unwrap(); | ||
|
||
let mut chart = ChartBuilder::on(&root) | ||
.caption("Gas Prices Over Time", ("sans-serif", 50).into_font()) | ||
.margin(5) | ||
.x_label_area_size(30) | ||
.y_label_area_size(30) | ||
.build_ranged(0..gas_prices.len(), 0..*gas_prices.iter().max().unwrap()) | ||
.unwrap(); | ||
|
||
chart.configure_mesh().draw().unwrap(); | ||
|
||
chart | ||
.draw_series(LineSeries::new( | ||
gas_prices.iter().enumerate().map(|(x, y)| (x, *y)), | ||
&RED, | ||
)) | ||
.unwrap() | ||
.label("Gas Price") | ||
.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(); | ||
} | ||
|
||
criterion_group!(benches, gas_price_algo); | ||
criterion_main!(benches); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,188 @@ | ||
use crate::service::adapters::fuel_gas_price_provider::ports::GasPriceAlgorithm; | ||
use fuel_core_producer::block_producer::gas_price::{ | ||
GasPriceParams, | ||
GasPriceProvider, | ||
}; | ||
use fuel_core_types::fuel_types::BlockHeight; | ||
use ports::{ | ||
DARecordingCostHistory, | ||
FuelBlockHistory, | ||
}; | ||
use std::cell::RefCell; | ||
|
||
pub mod ports; | ||
|
||
pub mod algorithm_adapter; | ||
|
||
use ports::{ | ||
Error, | ||
Result, | ||
}; | ||
|
||
#[cfg(test)] | ||
mod tests; | ||
|
||
/// Gives the gas price for a given block height, and calculates the gas price if not yet committed. | ||
pub struct FuelGasPriceProvider<FB, DA, A> { | ||
profitablility_totals: ProfitablilityTotals, | ||
|
||
// adapters | ||
block_history: FB, | ||
da_recording_cost_history: DA, | ||
algorithm: A, | ||
} | ||
|
||
impl<FB, DA, A> FuelGasPriceProvider<FB, DA, A> { | ||
pub fn new(block_history: FB, da_recording_cost_history: DA, algorithm: A) -> Self { | ||
Self { | ||
profitablility_totals: ProfitablilityTotals::default(), | ||
block_history, | ||
da_recording_cost_history, | ||
algorithm, | ||
} | ||
} | ||
} | ||
|
||
struct ProfitablilityTotals { | ||
totaled_block_height: RefCell<BlockHeight>, | ||
total_reward: RefCell<u64>, | ||
total_cost: RefCell<u64>, | ||
} | ||
|
||
impl Default for ProfitablilityTotals { | ||
fn default() -> Self { | ||
Self { | ||
totaled_block_height: RefCell::new(0.into()), | ||
total_reward: RefCell::new(0), | ||
total_cost: RefCell::new(0), | ||
} | ||
} | ||
} | ||
|
||
impl ProfitablilityTotals { | ||
#[allow(dead_code)] | ||
pub fn new(block_height: BlockHeight, reward: u64, cost: u64) -> Self { | ||
Self { | ||
totaled_block_height: RefCell::new(block_height), | ||
total_reward: RefCell::new(reward), | ||
total_cost: RefCell::new(cost), | ||
} | ||
} | ||
|
||
fn update(&self, block_height: BlockHeight, reward: u64, cost: u64) { | ||
let mut totaled_block_height = self.totaled_block_height.borrow_mut(); | ||
let mut total_reward = self.total_reward.borrow_mut(); | ||
let mut total_cost = self.total_cost.borrow_mut(); | ||
while *totaled_block_height < block_height { | ||
*totaled_block_height = block_height; | ||
*total_reward += reward; | ||
*total_cost += cost; | ||
} | ||
} | ||
} | ||
|
||
impl<FB, DA, A> FuelGasPriceProvider<FB, DA, A> | ||
where | ||
FB: FuelBlockHistory, | ||
DA: DARecordingCostHistory, | ||
A: GasPriceAlgorithm, | ||
{ | ||
fn inner_gas_price(&self, requested_block_height: BlockHeight) -> Result<u64> { | ||
let latest_block = self | ||
.block_history | ||
.latest_height() | ||
.map_err(Error::UnableToGetLatestBlockHeight)?; | ||
if latest_block > requested_block_height { | ||
self.block_history | ||
.gas_price(requested_block_height) | ||
.map_err(Error::UnableToGetGasPrice)? | ||
.ok_or(Error::GasPriceNotFoundForBlockHeight( | ||
requested_block_height, | ||
)) | ||
} else if Self::asking_for_next_block(latest_block, requested_block_height) { | ||
self.calculate_new_gas_price(latest_block) | ||
} else { | ||
Err(Error::RequestedBlockHeightTooHigh { | ||
requested: requested_block_height, | ||
latest: latest_block, | ||
}) | ||
} | ||
} | ||
|
||
fn asking_for_next_block( | ||
latest_block: BlockHeight, | ||
block_height: BlockHeight, | ||
) -> bool { | ||
*latest_block + 1 == *block_height | ||
} | ||
|
||
fn calculate_new_gas_price(&self, latest_block_height: BlockHeight) -> Result<u64> { | ||
self.update_totals(latest_block_height)?; | ||
let previous_gas_price = self | ||
.block_history | ||
.gas_price(latest_block_height) | ||
.map_err(Error::UnableToGetGasPrice)? | ||
.ok_or(Error::GasPriceNotFoundForBlockHeight(latest_block_height))?; | ||
let block_fullness = self | ||
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. I think the previous block's fullness should be part of the 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. I'm not sure what you mean That's how I imagine it will still work, but just with this new |
||
.block_history | ||
.block_fullness(latest_block_height) | ||
.map_err(Error::UnableToGetBlockFullness)? | ||
.ok_or(Error::BlockFullnessNotFoundForBlockHeight( | ||
latest_block_height, | ||
))?; | ||
let new_gas_price_candidate = self.algorithm.calculate_gas_price( | ||
previous_gas_price, | ||
self.total_reward(), | ||
self.total_cost(), | ||
block_fullness, | ||
); | ||
let new_gas_price = core::cmp::min( | ||
new_gas_price_candidate, | ||
self.algorithm.maximum_next_gas_price(previous_gas_price), | ||
); | ||
MitchTurner marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Ok(new_gas_price) | ||
} | ||
|
||
fn total_reward(&self) -> u64 { | ||
*self.profitablility_totals.total_reward.borrow() | ||
} | ||
|
||
fn total_cost(&self) -> u64 { | ||
*self.profitablility_totals.total_cost.borrow() | ||
} | ||
|
||
fn totaled_block_height(&self) -> BlockHeight { | ||
*self.profitablility_totals.totaled_block_height.borrow() | ||
} | ||
|
||
fn update_totals(&self, latest_block_height: BlockHeight) -> Result<()> { | ||
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. We need to take into account the fact that DA recording has some lag to it. Possibly 12-24 blocks? How does that effect the 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. Implemented a solution for this. The DA gas price is only updated when the da recorded height changes. Might need more testing. |
||
while self.totaled_block_height() < latest_block_height { | ||
let block_height = (*self.totaled_block_height() + 1).into(); | ||
let reward = self | ||
.block_history | ||
.production_reward(block_height) | ||
.map_err(Error::UnableToGetProductionReward)? | ||
.ok_or(Error::ProductionRewardNotFoundForBlockHeight(block_height))?; | ||
let cost = self | ||
.da_recording_cost_history | ||
.recording_cost(block_height) | ||
.map_err(Error::UnableToGetRecordingCost)? | ||
.ok_or(Error::RecordingCostNotFoundForBlockHeight(block_height))?; | ||
self.profitablility_totals | ||
.update(block_height, reward, cost); | ||
} | ||
Ok(()) | ||
} | ||
} | ||
|
||
impl<FB, DA, A> GasPriceProvider for FuelGasPriceProvider<FB, DA, A> | ||
where | ||
FB: FuelBlockHistory, | ||
DA: DARecordingCostHistory, | ||
A: GasPriceAlgorithm, | ||
{ | ||
fn gas_price(&self, params: GasPriceParams) -> Option<u64> { | ||
// TODO: handle error | ||
self.inner_gas_price(params.block_height()).ok() | ||
} | ||
} |
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.
You can remove this file, it is not used anymore=)