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
Improve lock time errors #2417
Improve lock time errors #2417
Conversation
Pull Request Test Coverage Report for Build 7727384831
💛 - Coveralls |
94e612f
to
670c16b
Compare
Self::from_consensus(n).map_err(Into::into) | ||
} | ||
*/ | ||
} |
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 670c16b:
Did you mean to leave this commented out code 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.
Definitely not, thanks!
let n = u32::try_from(n).map_err(|_| ParseError::Conversion(n))?; | ||
Self::from_consensus(n).map_err(Into::into) | ||
} | ||
} |
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 670c16b:
There are two error paths here which both wind up returning the ParseError::Conversion
variant. Is there a way this function could return ConversionError
instead?
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, this whole method is not needed, I messed up hex parsing for Time
.
But to give a bit of context, it was originally intentional since it's a private method that is only used as parsing primitive and it helps to return the internal type because in theory it makes constructing the other errors simpler. This worked well in my other projects so I just reused the pattern however it turns out it's not really that useful in this code.
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.
A few more questions please:
- With this applied we have three functions in this file called
from_hex_str_no_prefix
each with a different implementation, its not obvious why they are different? - Why does
parse_hex
exist to be only called once? (Related to point above.) - We are parsing hex, I don't understand why all the signed stuff? Are you trying to improve the error if someone attempts to parse in negative hex as a lock time?
#[cfg(feature = "std")] | ||
impl std::error::Error for ParseHeightError { | ||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { | ||
self.0.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.
Why is it written like this instead of Some(&self.0)
? Won't this loose one layer of nesting because source
is supposed to return the error source witch is ParseError
not ParseError
's 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 intentionally want to hide ParseError
because it's internal and in addition as you can see that method is intentionally not std::error::Error
method because that would require Display
but displaying requires additional context. And the method is needed because I want to filter-out overflow errors.
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 it's worth a comment here because the code looks weird.
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.
OK, I've copied the comment from the other source
fn.
impl From<ParseError> for ParseHeightError { | ||
fn from(value: ParseError) -> Self { | ||
Self(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.
impl From<ParseError> for ParseHeightError { | |
fn from(value: ParseError) -> Self { | |
Self(value) | |
impl From<ParseError> for ParseHeightError { | |
fn from(e: ParseError) -> Self { Self(e) } |
This is an extremely trivial review suggestion but I get the feeling that having all the error code super uniform is going to pay dividends in the future. For the rest of time (hyperbolic I know) devs will want to flick to error code and quickly parse it. Any differences cause one to double take. I rekon we should only have differences when they are meaningful, to help catch peoples eye.
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'll try to run fmt and see if it makes diff too crazy or not.
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.
This whole not being able to format PRs because of one devs requirement to not use nightly, which is now required for clippy anyways is really a PITA.
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.
clippy warnings aren't too common and are easy to fix in the reasonably-rare case they show up in CI. Vs formatting issues which show up in probably 98% of all PRs unless the user is running a format tool locally. So the clippy nightly requirement isn't really a burden on individual developers.
The real issue is the Rust ecosystem's total lack of a stable working formatting tool. And even the unstable one is unable to format diffs. I hardly blame individual developers who are uncomfortable frequently downloading binary blobs from the developers of that mess.
let height = i64::from_str_radix(s.as_ref(), 16) | ||
.map_err(ParseError::invalid_int(s))?; |
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 don't understand this change, why call from_str_radix
on a signed type? I'm guessing you are trying to improve the error reporting coming from std
but I don't see how?
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 see // we use i64 to have nicer messages for negative values
but I don't see where we get negative values from?
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.
From parsing, it's in the PR description. For -42
std
says "invalid digit" which is confusing, I want to just say that the value is below the limit which is 0. And for that it's easiest to parse i64
because if there actually is another invalid digit (-42blocks
) then the error makes sense and i64
losslessly holds u32
. (Technically i33
would be enough...)
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 probably elaborate the comment here as well.
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 comment on the variant. I can copy 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.
When minus sign is present std reports it as invalid digit which is less helpful than saying negative numbers are not allowed.
Ha! Guess I did not read the PR description close enough. A wee code comment on the i64
would be great please.
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 you've pushed already - excuse the noise, I was mindlessly reading the thread.
Because I messed up, it's supposed to be used twice and the two of the methods should be same. I've explained it above.
Yes, though it's a side effect of improving decimal parsing. I think that error message about |
The errors returned from various lock time functions had several issues. Among the obvious - `Error` being returned from all operations even when some of its variants were unreachable, there were subtle issues around error messages: * `ParseIntError` didn't contain information whether the parsed object is `Height` or `Time`. * Logically overflow and out-of-bounds should be the same thing but produced different error messages. * Mentioning integers is too technical for a user, talking about upper and lower bound is easier to understand. * When minus sign is present `std` reports it as invalid digit which is less helpful than saying negative numbers are not allowed. It is also possible that `ParseIntError` will need to be removed from public API during crate smashing or stabilization, so avoiding it may be better. This commit significantly refactors the errors. It adds separate types for parsing `Height` and `Time`. Notice that we don't compose them from `ParseIntError` and `ConversionError` - that's not helpful because they carry information that wouldn't be used when displaying which is wasteful. Keeping errors small can be important. It's also worth noting that exposing the inner representation could cause confusion since the same thing: out of bounds can be represented as an overflow or as a conversion error. So for now we conservatively hide the details and even pretend there's no `source` in case of overflow. This can be expanded in the future if needed. The returned errors are now minimal. `LockTime` parsing errors are currentlly unchanged.
670c16b
to
93dba89
Compare
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.
ACK 93dba89
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.
ACK 93dba89
Thanks for formatting! |
The errors returned from various lock time functions had several issues. Among the obvious -
Error
being returned from all operations even when some of its variants were unreachable, there were subtle issues around error messages:ParseIntError
didn't contain information whether the parsed object isHeight
orTime
.std
reports it as invalid digit which is less helpful than saying negative numbers are not allowed.It is also possible that
ParseIntError
will need to be removed from public API during crate smashing or stabilization, so avoiding it may be better.This commit significantly refactors the errors. It adds separate types for parsing
Height
andTime
. Notice that we don't compose them fromParseIntError
andConversionError
- that's not helpful because they carry information that wouldn't be used when displaying which is wasteful. Keeping errors small can be important.It's also worth noting that exposing the inner representation could cause confusion since the same thing: out of bounds can be represented as an overflow or as a conversion error. So for now we conservatively hide the details and even pretend there's no
source
in case of overflow. This can be expanded in the future if needed.The returned errors are now minimal.
LockTime
parsing errors are currentlly unchanged.I can add
LockTime
changes in the same commit or separate within this PR if you want. Just wanted to push something for review before I go to sleep.