-
Notifications
You must be signed in to change notification settings - Fork 927
Use checked arithmetic in types and state proc #1009
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
|
Still WIP, I've noticed a few things in the diff that need correcting |
972de7f to
3e4dc7a
Compare
|
Ok, ready for review. I'm just going to run some benchmarks of state processing to assess the impact on performance. |
|
Benchmarks have revealed no statistically significant deviation from
Worst-case blocks, i7-8550U, minimal spec for the 32K validators, mainnet spec for the 300K, Weirdly the benchmark is faster for the larger blocks, and the bulk signature verification is a lot slower, I don't know what's going on there but we can investigate that separately. |
|
|
||
| pub fn epoch(self, slots_per_epoch: u64) -> Epoch { | ||
| Epoch::from(self.0 / slots_per_epoch) | ||
| Epoch::from(self.0) / Epoch::from(slots_per_epoch) |
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 use checked_div or safe_div
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 yeah, I figured this was quite innocuous here, because slots_per_epoch is unlikely to ever be 0, and it would at least panic rather than wrapping in a strange way. I'll see how much flow-on effect returning a Result would have though.
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.
Perhaps we do an explicit panic if propagating the Result is too onerous (I suspect this will be the case). That way we get the panic in release too.
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 used Epoch division here because it will explicitly panic:
lighthouse/eth2/types/src/slot_epoch_macros.rs
Lines 108 to 114 in 3e4dc7a
| fn div(self, rhs: $other) -> $main { | |
| let rhs: u64 = rhs.into(); | |
| $main::from( | |
| self.0 | |
| .checked_div(rhs) | |
| .expect("Cannot divide by zero-valued Slot/Epoch"), | |
| ) |
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 going to leave this as-is, if that's OK with everyone.. I count 60 uses of .epoch() all throughout different crates, almost always with T::slots_per_epoch() as the argument, so I don't think it's worth 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.
Fine by me. Setting SLOTS_PER_EPOCH is not reasonable and there's plenty of other panics we could cause by setting "constants" to unreasonable 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.
It's also not really "user supplied input". I'd say it's "programmer supplied input".
| if let Some(root) = self.root { | ||
| break Ok(root); | ||
| } else if let Some(node) = self.half_nodes.last() { | ||
| let right_child = node.id * 2 + 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.
mul and add can be checked here since we return Result
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.
Oh yeah, we could. This refactor was just leftover from me trying to fix generic clippy lints project-wide (which I ended up disabling for now), and this crate isn't being linted currently. I agree we could expand the usage of checked arithmetic though, so I've made an issue to track that: #1013
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.
Great effort! This would have been tedious but it's really great to see this overflow behavior locked down!
I just have a couple of questions, nothing major at all.
eth2/state_processing/src/per_block_processing/block_signature_verifier.rs
Show resolved
Hide resolved
eth2/state_processing/src/per_epoch_processing/process_slashings.rs
Outdated
Show resolved
Hide resolved
|
|
||
| pub fn epoch(self, slots_per_epoch: u64) -> Epoch { | ||
| Epoch::from(self.0 / slots_per_epoch) | ||
| Epoch::from(self.0) / Epoch::from(slots_per_epoch) |
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.
Perhaps we do an explicit panic if propagating the Result is too onerous (I suspect this will be the case). That way we get the panic in release too.
|
Feel free to squerge when the tests pass :) |
Proposed Changes
typesandstate_processingcrates, so that we're forced to be explicit about what sort of arithmetic we want, and can catch unwanted overflow and consider it an invalid state transition (as per Clarity onuintoverflow ethereum/consensus-specs#1701)checked_functions from the standard library. To make things more ergonomic, I introduced asafe_arithcrate which returnsResults from the checked operations instead ofOptions.