-
Notifications
You must be signed in to change notification settings - Fork 2.9k
feat(gas_price_service): update block committer da source with established contract #2265
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
Conversation
fix: remove block height from error
463604c to
b47bf54
Compare
| if let Some(last_value) = &self.last_value { | ||
| res = DaBlockCosts { | ||
| l2_block_range: response.blocks_range.clone(), | ||
| blob_size_bytes: response | ||
| .total_size_bytes | ||
| .checked_sub(last_value.total_size_bytes) | ||
| .ok_or(anyhow!("Blob size bytes underflow"))?, | ||
| blob_cost_wei: response | ||
| .total_cost | ||
| .checked_sub(last_value.total_cost) | ||
| .ok_or(anyhow!("Blob cost wei underflow"))?, | ||
| }; | ||
| } else { | ||
| res = DaBlockCosts { | ||
| l2_block_range: response.blocks_range.clone(), | ||
| blob_size_bytes: response.total_size_bytes, | ||
| blob_cost_wei: response.total_cost, | ||
| }; | ||
| } |
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.
@MitchTurner if the block committer da block cost source is starting up for the first time, we will still get the total_size_bytes and total_cost from the block committer. is it reasonable to expect the consumers of this service to persist state and detect the first value that this source spits out? Or should we include the last_state in the new fn that constructs the BlockCommitterDaBlockCosts? I might lean to the latter approach since it is more testable, and allows consumers to actually bring their own persistence method.
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 consumers shouldn't need to know that info. They should just get the info for each committed batch. So the second sounds like a promising approach.
b3680c1 to
e488068
Compare
MitchTurner
left a comment
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.
Bunch of little things.
| #[derive(Debug, Default, Clone, Eq, Hash, PartialEq)] | ||
| pub struct DaBlockCosts { | ||
| pub l2_block_range: core::ops::Range<u32>, | ||
| pub l2_block_range: core::ops::Range<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.
Why 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.
that's because the sequence number is u64 as we decided. the type DaBlockHeight is also u64
| async fn get_bundles_by_range( | ||
| &self, | ||
| range: core::ops::Range<u64>, | ||
| ) -> DaBlockCostsResult<Vec<Option<RawDaBlockCosts>>>; |
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.
Missing a docstring.
But I'm also unsure what the expected behavior is here. Like if a bundle included a range of blocks 50 -> 100, what would you get back if you asked for range 75 -> 100 or 75 -> 125?
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.
its an inclusive range, so I assume we should actually increment by 1. We aren't using this yet, but it will be a part of a more comprehensive solution.
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 docstring in 5330975
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.
its an inclusive range
I'm not sure if this answered my question. The committer doesn't have data for individual blocks, just bundles.
Are you saying that if I asked for 50..100 and the existing bundles are 25..75 and 75..125, I would get those two bundles back? or nothing, since my requested range didn't encompass any full bundle?
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.
maybe we should rope in @MujkicA here. it shouldn't return anything.
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've updated the issue on the committer side, probably better if we move this discussion there:
| url: Url, | ||
| pub struct BlockCommitterDaBlockCosts<BlockCommitter> { | ||
| client: BlockCommitter, | ||
| last_value: Option<RawDaBlockCosts>, |
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 algorithm currently expects things in order. I'm not suggesting we change anything, I'm just wondering if it would make more sense to have the checks all in one place or the other. We could check that everything is correct here and do no checks in the algo. I'm not sure what makes the most sense, but worth thinking about.
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.
good idea, we should have data validation, but that should be limited to the scope of the block committer data source. it can't know what data is expected on the receiver end, but it can atleast guarantee some things. we can choose to decouple business logic from this service, that will allow some flexibility on consumers of this data.
| // assert_eq!(initial.blob_size_bytes, updated.blob_size_bytes); | ||
| // assert_eq!(initial.blob_cost_wei, updated.blob_cost_wei); |
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.
Do we want these?
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 b5e7cbd
| #[tokio::test] | ||
| async fn request_da_block_cost__when_response_is_none__then_error() { | ||
| // given | ||
| let mock_block_committer = MockBlockCommitterApi::new(None); |
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.
These names are confusing. Maybe mock_api to differentiate it from the block_committer.
This applies to all the tests.
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 28ba81f
| block_committer_da_block_costs, | ||
| DaBlockCosts { | ||
| l2_block_range: da_block_costs.blocks_range.clone(), | ||
| blob_size_bytes: da_block_costs.total_size_bytes, | ||
| blob_cost_wei: da_block_costs.total_cost, | ||
| } |
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: rename block_committer_da_block_costs to actual and the DaBlockCosts to 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.
addressed in 9ea0a9c
| url: Url, | ||
| pub struct BlockCommitterDaBlockCosts<BlockCommitter> { | ||
| client: BlockCommitter, | ||
| last_value: Option<RawDaBlockCosts>, |
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: rename this to last_raw_da_block_costs
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 7d18c22
| BlockCommitter: BlockCommitterApi, | ||
| { | ||
| async fn request_da_block_cost(&mut self) -> DaBlockCostsResult<DaBlockCosts> { | ||
| let response; |
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: rename raw_da_block_costs
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 d545341
| } | ||
|
|
||
| if let Some(response) = response { | ||
| let res; |
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: rename da_block_costs
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 d545341
|
|
||
| if let Some(response) = response { | ||
| let res; | ||
| if let Some(last_value) = &self.last_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: I think I prefer to only have one if let Some(last_value) = &self.last_value and have redundant if let Some(response) = response in each branch.
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.
| pub blob_size_bytes: u32, | ||
| pub blob_cost: u128, | ||
| pub struct RawDaBlockCosts { | ||
| // Sequence number (Monotonically increasing nonce) |
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.
| // Sequence number (Monotonically increasing nonce) | |
| /// Sequence number (Monotonically increasing nonce) |
Same for other fields?
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 5330975
e488068 to
23a41fa
Compare
| &self, | ||
| number: u64, | ||
| ) -> DaBlockCostsResult<Option<RawDaBlockCosts>>; | ||
| async fn get_bundles_by_range( |
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 adding costs somewhere to the name would make it easier to relate this function to the other two.
E.g. get_cost_bundles_by_range
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 552d224
| @@ -1,3 +1,6 @@ | |||
| #![allow(non_snake_case)] | |||
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 line and add #[allow(non_snake_case)] just before the mod tests declaration
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 d562d72
| } | ||
| self.last_value = Some(response.clone()); | ||
| Ok(res) | ||
| } else { |
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 use the let - else syntax to improve code readability:
let Some(response) = response else { return Err(anyhow!("No response from block committer")) };
/* Now you can have the code in the if branch 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.
addressed in edfc1ff
| async fn request_da_block_cost(&mut self) -> DaBlockCostsResult<DaBlockCosts> { | ||
| let response; | ||
|
|
||
| if let Some(last_value) = &self.last_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.
you can pattern match to improve readability. To avoid moving last_value out of the option, you can either pattern match on self.last_value.as_ref(), or use the ref syntax.
let response = match self.last_value {
Some(ref last_value) => self
.client
.get_costs_by_seqno(last_value.sequence_number + 1),
_ => self.client.get_latest_costs(),
}
.await?;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 2893288
|
|
||
| if let Some(response) = response { | ||
| let res; | ||
| if let Some(last_value) = &self.last_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.
If you iter over the Option, you can use the .fold function to remove some code duplication and make the code a bit more clear.
I played a bit with your code and ended up with the following:
let Some(ref response) = response else {
return Err(anyhow!("No response from block committer"))
};
let res = self.last_value.iter().fold(
Ok(response.into()),
|costs: DaBlockCostsResult<DaBlockCosts>, last_value| {
let costs = costs.expect("Defined to be OK");
let blob_size_bytes = costs
.blob_size_bytes
.checked_sub(last_value.total_size_bytes)
.ok_or(anyhow!("Blob size bytes underflow"))?;
let blob_cost_wei = response
.total_cost
.checked_sub(last_value.total_cost)
.ok_or(anyhow!("Blob cost wei underflow"))?;
Ok(DaBlockCosts {
blob_size_bytes,
blob_cost_wei,
..costs
})
},
)?;
self.last_value = Some(response.clone());
Ok(res)This requires to implement From<&RawDaBlockCosts> for DaBlockCosts.
impl From<&RawDaBlockCosts> for DaBlockCosts {
fn from(raw_da_block_costs: &RawDaBlockCosts) -> Self {
DaBlockCosts {
l2_block_range: raw_da_block_costs.blocks_range.clone(),
blob_size_bytes: raw_da_block_costs.total_size_bytes,
blob_cost_wei: raw_da_block_costs.total_cost,
}
}
}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 edfc1ff
2893288 to
e97c3f9
Compare
| let result = block_committer.request_da_block_cost().await; | ||
|
|
||
| // then | ||
| assert!(result.is_err()); |
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 was gonna say we should check the specific error variant here but anyhow :(
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.
in my own experience, I tend to prefer thiserror::Error to anyhow by a lot, especially in codebases where each module has its own error type.
| let mut block_committer = BlockCommitterDaBlockCosts::new(mock_api, None); | ||
|
|
||
| // when | ||
| let actual = block_committer.request_da_block_cost().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.
Isn't this part of the setup? I don't think we need to do any checks on this value and it can be moved to given.
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 0794465
|
|
||
| // then | ||
| assert!(result.is_err()); | ||
| assert!(block_committer.last_raw_da_block_costs.is_none()); |
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 check is unnecessary. From the outside observer, we don't care what is being set internally, just the external result of those internal values.
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 931adfe
|
|
||
| // then | ||
| assert_eq!(actual, expected); | ||
| assert!(block_committer.last_raw_da_block_costs.is_some()); |
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.
Also unnecessary I think.
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 fd44dd4
fd44dd4 to
3f3c251
Compare
acerone85
left a comment
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.
LGTM
Note
This is PR 7/7 for #2139
Linked Issues/PRs
Description
Updates the
BlockCommitterApito implement the contract established with us and the block committer team. There is some business logic that is slightly unclear, need @MitchTurner for insights.Checklist
Before requesting review
After merging, notify other teams
[Add or remove entries as needed]