-
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
feat(gas_price_service_v1): define RunnableTask for GasPriceServiceV1 #2416
Conversation
} | ||
|
||
#[tokio::test] | ||
async fn run__updates_gas_price_with_da_block_cost_source() { |
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.
cc: @MitchTurner this test seems to be failing with the following error -
called `Result::unwrap()` on an `Err` value: L2 block expected but not found in unrecorded blocks: 1
which i narrowed down to
fuel-core/crates/fuel-gas-price-algorithm/src/v1.rs
Lines 546 to 562 in a14922e
fn drain_l2_block_bytes_for_range( | |
&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() | |
.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) | |
} |
This is what happens when the da block cost that is received refers to an L2 block that hasn't been processed by the algorithm yet. that is fine, seems like defined behaviour.
Below, on line 432-434, I define the cost and bytes. Something seems to be wrong in the drain_l2_block_bytes_for_range
function
fn drain_l2_block_bytes_for_range( |
range_bytes
it yields after being called in let range_bytes = self.drain_l2_block_bytes_for_range(height_range)?; |
0
, which doesn't seem too 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.
wondering if you have any thoughts about the params I'm using.
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 discussed this offline someplace, but I'll record it here.
This is what happens when the da block cost that is received refers to an L2 block that hasn't been processed by the algorithm yet. that is fine, seems like defined behaviour.
We should always receive DA recorded blocks after L2 blocks. This is because we can't commit a block to DA until after it is produced on L2.
when I call it with just one item in the range. because the range_bytes it yields after being called in
Good catch. I've changed the behavior anyway in this pr: #2415
Should solve it.
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.
wondering if you have any thoughts about the params I'm using.
With these tests, the values don't exactly matter. We use an assert_ne!
and as long as that's true we're not concerned with the details. The problem with this approach is the test writer/reader still needs to know what values would result in a different gas price. This is part of the reason I originally had an abstraction of gas price updater (which we've (you've) removed)
/// The range of blocks that the costs apply to | ||
pub blocks_range: core::ops::Range<u64>, | ||
pub blocks_range: core::ops::Range<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.
We'll need to change this to just be a vec with these changes:
#2415
// arbitrary logic to generate a new value | ||
let mut value = self.value.clone(); | ||
if let Some(value) = &mut value { | ||
value.sequence_number = seq_no; | ||
value.blocks_range = | ||
value.blocks_range.end * seq_no..value.blocks_range.end * seq_no + 10; | ||
value.da_block_height = value.da_block_height + (seq_no + 1).into(); | ||
value.da_block_height = | ||
value.da_block_height + ((seq_no + 1) as u64).into(); | ||
value.total_cost += 1; | ||
value.total_size_bytes += 1; | ||
} |
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.
There is a lot of unchecked arithmetic and casting going on here. How do you feel about switching to checked and into
/from
here?
} | ||
|
||
#[tokio::test] | ||
async fn run__updates_gas_price_with_da_block_cost_source() { |
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 discussed this offline someplace, but I'll record it here.
This is what happens when the da block cost that is received refers to an L2 block that hasn't been processed by the algorithm yet. that is fine, seems like defined behaviour.
We should always receive DA recorded blocks after L2 blocks. This is because we can't commit a block to DA until after it is produced on L2.
when I call it with just one item in the range. because the range_bytes it yields after being called in
Good catch. I've changed the behavior anyway in this pr: #2415
Should solve it.
} | ||
|
||
#[tokio::test] | ||
async fn run__updates_gas_price_with_da_block_cost_source() { |
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.
wondering if you have any thoughts about the params I'm using.
With these tests, the values don't exactly matter. We use an assert_ne!
and as long as that's true we're not concerned with the details. The problem with this approach is the test writer/reader still needs to know what values would result in a different gas price. This is part of the reason I originally had an abstraction of gas price updater (which we've (you've) removed)
|
||
// when | ||
service.run(&mut watcher).await.unwrap(); | ||
l2_block_sender.send(l2_block).await.unwrap(); |
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.
Is the bug you're seeing the reason we aren't submitting a new DA block 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.
not seeing the bug anymore :)
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 this test should be "sending" a block to the DA record service, not the l2_block_sender
.
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 still need to trigger update_da_gas_price()
, so I've added it in the update_da_record_data
fn of the AlgorithmV1
impl in c516ea9
@@ -103,12 +104,14 @@ where | |||
async fn run(&mut self, state_watcher: &mut StateWatcher) -> Result<bool> { | |||
let continue_running; | |||
|
|||
let tick: BoxFuture<tokio::time::Instant> = Box::pin(self.poll_interval.tick()); |
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.
Why do we need the BoxFuture
? I ran it on my machine without and it compiled.
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're right, I had it that way when i was testing something. removed in 53291a5
@@ -313,6 +313,7 @@ impl AlgorithmUpdaterV1 { | |||
if !height_range.is_empty() { | |||
self.da_block_update(height_range, range_cost)?; | |||
self.recalculate_projected_cost(); | |||
self.update_da_gas_price(); |
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.
Ah. Haha. I get it now... I think it makes sense either way but this probably better.
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.
Probably should have some tests in the algo for this.
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.
addressed in 8ae983f
pub capped_range_size: u16, | ||
pub decrease_range_size: u16, | ||
pub block_activity_threshold: u8, | ||
pub unrecorded_blocks: Vec<(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.
Hmm. I guess it doesn't hurt. I don't know why we'd need this if it wasn't already in the metadata.
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.
need this to bootstrap the AlgorithmV1
with a state that we can test only DA data being sent :)
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.
Hmm. K. Technically you could provide a metadata with it, but that's expensive.
if let Ok(v1_metadata) = V1Metadata::try_from(updater_metadata.clone()) { | ||
algorithm_updater = v1_algorithm_from_metadata(v1_metadata, config); | ||
} else { | ||
let v0_metadata = V0Metadata::try_from(updater_metadata).map_err(|_| { | ||
crate::common::utils::Error::CouldNotInitUpdater(anyhow::anyhow!( | ||
"Could not convert metadata to V0Metadata" | ||
)) | ||
})?; | ||
let v1_metadata = V1Metadata::construct_from_v0_metadata(v0_metadata, config) | ||
.map_err(|err| { | ||
crate::common::utils::Error::CouldNotInitUpdater(anyhow::anyhow!(err)) | ||
})?; | ||
algorithm_updater = v1_algorithm_from_metadata(v1_metadata, config); | ||
} |
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: I think we should just unify this branch and add a method fn best_v1_metadata(metadata: &Metadata, config: &V1AlgorithmConfig)
. In both branches we end up calling v1_algorithm_from_metadata
, so something like:
if let Ok(v1_metadata) = V1Metadata::try_from(updater_metadata.clone()) { | |
algorithm_updater = v1_algorithm_from_metadata(v1_metadata, config); | |
} else { | |
let v0_metadata = V0Metadata::try_from(updater_metadata).map_err(|_| { | |
crate::common::utils::Error::CouldNotInitUpdater(anyhow::anyhow!( | |
"Could not convert metadata to V0Metadata" | |
)) | |
})?; | |
let v1_metadata = V1Metadata::construct_from_v0_metadata(v0_metadata, config) | |
.map_err(|err| { | |
crate::common::utils::Error::CouldNotInitUpdater(anyhow::anyhow!(err)) | |
})?; | |
algorithm_updater = v1_algorithm_from_metadata(v1_metadata, config); | |
} | |
let v1_metadata = best_v1_metadata(updater_metadata, config)?; | |
algorithm_updater = v1_algorithm_from_metadata(v1_metadata, config); |
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.
addressed in 2291207
|
||
// then | ||
let new_da_gas_price = updater.new_scaled_da_gas_price; | ||
assert_ne!(old_da_gas_price, new_da_gas_price); |
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.
We should be more specific at this level (and not just say it is different) You can copy most of the code from
update_l2_block_data__below_threshold_will_decrease_exec_gas_price
, update_l2_block_data__above_threshold_will_increase_exec_gas_price
, and update_l2_block_data__even_threshold_will_not_change_exec_gas_price
It feels redundant, but they are different endpoints so they change independently.
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.
The code looks clean and understandable from an external POV.
1e3b3f5
Note
Some values for the tests need expert opinion from @MitchTurner. A follow up PR will be created to define the
UninitializedTask
that wraps over this task.Linked Issues/PRs
part of #2140, but doesn't close it yet.
Description
RunnableTask
,GasPriceServiceV1
, which uses the da block cost source in tandem with the l2 block sourceChecklist
Before requesting review
After merging, notify other teams
[Add or remove entries as needed]