-
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 gas prices from actual blocks to calculate estimate gas prices #2501
base: master
Are you sure you want to change the base?
Conversation
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.
left a few questions :)
} | ||
|
||
#[async_trait::async_trait] | ||
impl GasPriceEstimate for ArcGasPriceEstimate<u32, u64> { |
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.
does this have to be async?
pub(crate) fn update_latest_gas_price(&self, block_info: &BlockInfo) { | ||
match block_info { | ||
BlockInfo::GenesisBlock => { | ||
// do nothing |
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.
should we have a test to see what values are stored in the block after genesis? afaiu we test only post genesis transitions
@@ -481,10 +506,12 @@ mod tests { | |||
), | |||
None, | |||
); | |||
let latest_gas_price = Arc::new(parking_lot::RwLock::new((0, 0))); |
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.
curious why we need this when perhaps the algo could contain this internally?
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.
Yeah... That works too. It's kinda a separate value so I thought to keep it separate but that would work.
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.
Yeah, maybe we oversplitting here. Algorithm could answer this, if we store the last used gas price there. But I think Mitch wants to reuse the code for v0. But maybe it can live in common and used in both algorithm.
use super::*; | ||
use proptest::proptest; | ||
|
||
async fn _worst_case__correctly_calculates_value( |
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 function is called later on
async fn _worst_case__correctly_calculates_value( | |
async fn worst_case__correctly_calculates_value( |
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.
Yes. It's the actual test code, but I don't like writing it inside the prop_test
macro because the IDE has a hard time parsing it:
proptest! {
#[test]
fn worst_case__correctly_calculates_value(
price: u64,
starting_height: u32,
block_horizon in 0..10_000u32,
percentage: u64
) {
_worst_case__correctly_calculates_value(price, starting_height, block_horizon, percentage);
}
}
So this is my naming convention for that case.
} | ||
|
||
#[allow(dead_code)] | ||
pub struct ArcGasPriceEstimate<Height, GasPrice> { |
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.
would it be possible to have a bit of documentation here?
In particular, I'd like to understand what the "height" and "price" refer to here, since later they are named as "best_height" and "best_price"
|
||
#[async_trait::async_trait] | ||
impl GasPriceEstimate for ArcGasPriceEstimate<u32, u64> { | ||
async fn worst_case_gas_price(&self, height: BlockHeight) -> Option<u64> { |
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.
so what I get here is that we assume that the price is going to raise by the specified percentage every block until height
is reached, is that correct?
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.
Correct.
best_gas_price: u64, | ||
best_height: u32, | ||
percentage: u64, | ||
target_height: u32, |
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: you can pass directly the difference of target_height
and best_height
to this function and get rid of one parameter, but I don't have a strong opinion here
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.
I'm fine with this for now.
|
||
#[allow(clippy::cast_possible_truncation)] | ||
pub(crate) fn cumulative_percentage_change( | ||
best_gas_price: u64, |
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: maybe start_gas_price
is better?
let multiple = (1.0f64 + percentage_as_decimal).powf(blocks); | ||
let mut approx = best_gas_price as f64 * multiple; | ||
// account for rounding errors and take a slightly higher value | ||
const ROUNDING_ERROR_CUTOFF: f64 = 16948547188989277.0; |
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.
can you put a comment about how this value has been obtained?
// account for rounding errors and take a slightly higher value | ||
const ROUNDING_ERROR_CUTOFF: f64 = 16948547188989277.0; | ||
if approx > ROUNDING_ERROR_CUTOFF { | ||
const ROUNDING_ERROR_COMPENSATION: f64 = 2000.0; |
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.
expected = expected.saturating_add(change_amount); | ||
} | ||
|
||
assert!(actual >= expected); |
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.
I think we can be more precise here, and place an upper bound on actual, e.g.
assert!(actual >= expected); | |
assert!(actual >= expected); | |
assert!(actual <= expected.saturating_add(2000.0)) |
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.
Sure. But we don't actually care about the lower bound of this. In reality, the gas price given by the block producer will be much lower than what we estimate 99% of the time. So we only really care that the block producer never gives a value higher than what we estimate.
I'm not sure I understand what the role of |
let best_gas_price = self.get_gas_price(); | ||
let best_height = self.get_height(); |
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 have a race condition here, because between self.get_gas_price
and self.get_height
the value can change. You can lock it, read both values, and unlock it again to avoid this issue.
@@ -105,6 +126,7 @@ where | |||
pub fn new( | |||
l2_block_source: L2, | |||
shared_algo: SharedV1Algorithm, | |||
latest_gas_price: Arc<parking_lot::RwLock<(u32, u64)>>, |
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.
It would be nice to have a type that hides this lock(Arc<parking_lot::RwLock<(u32, u64)>>
) inside. Later we can easier refactor it, plus you will not expose direct access to the lock.
@@ -481,10 +506,12 @@ mod tests { | |||
), | |||
None, | |||
); | |||
let latest_gas_price = Arc::new(parking_lot::RwLock::new((0, 0))); |
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.
Yeah, maybe we oversplitting here. Algorithm could answer this, if we store the last used gas price there. But I think Mitch wants to reuse the code for v0. But maybe it can live in common and used in both algorithm.
|
Linked Issues/PRs
Description
Because some nodes won't be running the gas price algorithm, or might not be synced properly, we are going to calculate the price based on the latest block gas price. That is a reliable source of truth.
Checklist
Before requesting review
After merging, notify other teams
[Add or remove entries as needed]