-
Notifications
You must be signed in to change notification settings - Fork 997
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
Clarity on uint
overflow
#1701
Comments
I'd say the desired outcome is that these overflows are (provably, e.g. using formal verification) not possible. If for some reason we (the spec designers) messed up then overflows should probably trigger an exception.
I'd say "no", this is an invalid state transition.
Agreed. To the sentence "State transitions that trigger an unhandled exception (e.g. a failed assert or an out-of-range list access) are considered invalid." we could add "Integer overflows are also considered exceptions." Alternatively, I believe we can redefine the behaviour of Python's arithmetic operators (e.g. |
This is already implemented. However, it's difficult to catch the cases where it doesn't go through these checked coercions (a + b returns a python int, which is then coerced safely, checking bounds, back into the spec type). It's difficult because python is very different from rust here, where it accepts everything by default, and doesn't know unsigned integers natively. Adding it to the spec as invalid-transition explicitly would be great 👍 |
I'm not a Python expert, but the |
While doing overflow analysis in k for |
def __add__(self, other):
return self.__class__(super().__add__(self.__class__.coerce_view(other)))
def __sub__(self, other):
return self.__class__(super().__sub__(self.__class__.coerce_view(other))) Adding these in next release, missed them earlier (thanks @michaelsproul): def __mul__(self, other):
return self.__class__(super().__mul__(self.__class__.coerce_view(other)))
def __floordiv__(self, other): # Better known as "//"
return self.__class__(super().__floordiv__(self.__class__.coerce_view(other))) The other value is converted to the same type (raising an exception if the byte length of the types does not match), then the regular python integer operation runs, and then the result is converted into the uint type of the self value (the constructor checks underflows/overflows). Formal verification/review are still good to have though, these python checks only help during runtime. |
Clarify that state transitions with `uint64` overflows are invalid.
Spec declares that
consensus-specs/specs/phase0/beacon-chain.md Line 1249 in 1e552f1
|
We (Lighthouse) are interested in building a "panic-free" implementation of the state transitions. I think we're fairly close when compiled in Rust's "release" mode (optimized, production-ready), however for Rust's "debug" mode (slow but useful for troubleshooting) any integer overflow that is not explicitly handled causes a panic.
When it comes to these overflows we have two choices:
The specification uses
uint...
types that, as I understand it, have undefined behavior when it comes to overflow (perhaps this can be derived from the tests, but this doesn't feel authoritative enough to me). From "common sense", I would assume thatuint64_max_value() + 1 == 0
and0 - 1 == uint64_max_value()
. I also note that a few comments that inferuint64
overflows:So, I'm inferring that uint64 overflows, assuming that it's desired behavior and assuming the specifics of that behavior.
It would be nice to have some clarity around this behavior. I understand that there's an idea that "valid state transitions shouldn't lead to overflows", but when it comes to things like fuzzing it's nice to have a distinction between "expected wrapping overflow" and "unexpected wrapping overflow".
So in summary, I have a question and a request please:
As always, thanks! :)
The text was updated successfully, but these errors were encountered: