-
Notifications
You must be signed in to change notification settings - Fork 270
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
Introduce new error handling convention #635
base: master
Are you sure you want to change the base?
Introduce new error handling convention #635
Conversation
CC @Kixunil for when he comes back because this will be of interest to him. |
609a514
to
cd28e09
Compare
cd28e09
to
50cf4f8
Compare
Got some issues with Rust 1.48, will wait for concept ack before continuing. |
Innnnteresting. I guess, if we accept this, the new macros should probably be generalized a bit and moved into I continue to be skeptical of macros but here they're really pulling a lot of weight, and this is mostly write-only code anyway. Not sure how I feel about it. |
strong concept ACK. This will be a major breaking change, we also need to carefully audit the places we will not need non-exhuastive stuff. Not sure about the macros vs manual typing. I don't mind having a lot of boilerplate code vs macros. The error code is nicely hidden in a error module and does not really hinder the main code. For secp code, I feel comfortable with macros as they are nicely self contained. But as soon we start hacking more stuff into it to support weird nested error structures etc, it starts to be unreadable. I also don't mind just typing the from impl, it's not that much boilerplate as serde stuff. |
d4cd52a
to
eced889
Compare
eced889
to
384ad31
Compare
I found a bunch more error improvements that can be done, converting to draft while I hack them up. |
b18475c
to
67e8c7e
Compare
I forget what was the policy on feature gating enum variants within error types? Is that frowned upon? (Currently includes an variant feature gated on "recovery" feature.) |
f626506
to
17d7734
Compare
I do not know what is causing the CI fail, @apoelstra can you put me out of my misery? |
b981b7c
to
148bc48
Compare
Well, the 1.48 one is because The other one is because the latest nightly changed the format of the |
Thanks man.
More pinning, awesome.
Bother, that is what I thought happened (if you saw the closed PR) but I guess I fell over at the "grep for the right thing" stage. Will try again, cheers. |
148bc48
to
462cbd6
Compare
Seems there are multiple problems with the nightly job. Attempting to fix them in #641 |
Note please, this PR is not currently on top of the latest version of #641 |
On ice until after v.0.28.0 release is done because using these new errors in |
462cbd6
to
5e0839c
Compare
My thoughts: This is close to what I wanted originally and the only downside is complexity/boilerplate which I'm willing to accept. Not sure about others. There are no For enums, Finally, IIUC this change doesn't add error information, only rearranges it (didn't check fully). That's an OK step, just let's not forget about adding some important things. |
5e0839c
to
708da6e
Compare
Thanks for the review @Kixunil! Added |
If we return `Self, Self::Err` then the implementations of `FromStr` are easier to maintain.
As is customary put the associated type at the start of the `FromStr` impl block.
0a8f8c8
to
34e0f0e
Compare
In preparation for adding separate errors move the hex code to its own private module.
9fad4af
to
f71b678
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.
Crap, I had these comments pending and I forgot about it. I will review the rest later.
examples/sign_verify_recovery.rs
Outdated
@@ -15,7 +17,7 @@ fn recover<C: Verification>( | |||
let id = ecdsa::RecoveryId::from_i32(recovery_id as i32)?; | |||
let sig = ecdsa::RecoverableSignature::from_compact(&sig, id)?; | |||
|
|||
secp.recover_ecdsa(&msg, &sig) | |||
Ok(secp.recover_ecdsa(&msg, &sig)?) |
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 prefer this over secp.recover_ecdsa(&msg, &sig).map(Into::into)
? I personally tend to use the latter but I don't really care much.
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 like yours. Will use.
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 couldn't get it to work, used
let pk = secp.recover_ecdsa(&msg, &sig)?;
Ok(pk)
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.
I meant map_err
, sorry.
src/context.rs
Outdated
|
||
impl fmt::Display for NotEnoughMemoryError { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
f.write_str("not enough preallocated memory for the requested buffer size") |
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.
Just realized what this type is used for and it could help having required
and available
fields in the error.
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.
Sure thing, thanks.
src/context.rs
Outdated
pub fn preallocated_gen_new(buf: &'buf mut [AlignedType]) -> Result<Secp256k1<C>, Error> { | ||
pub fn preallocated_gen_new( | ||
buf: &'buf mut [AlignedType], | ||
) -> Result<Secp256k1<C>, NotEnoughMemoryError> { |
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'd be nicer to accept a newtype rather than slice to avoid the error entirely.
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.
Added a patch to do this, as suggested. Thanks
match bytes.len() { | ||
SHARED_SECRET_SIZE => { | ||
let mut ret = [0u8; SHARED_SECRET_SIZE]; | ||
ret[..].copy_from_slice(bytes); | ||
Ok(SharedSecret(ret)) | ||
} | ||
_ => Err(Error::InvalidSharedSecret), | ||
_ => Err(SharedSecretError), | ||
} | ||
} | ||
} |
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 effectively duplicates the functionality of <[u8; SHARED_SECRET_SIZE] as TryFrom<[u8]>>::try_from
. Maybe we should just use the same error type? Although our own could carry more information. Anyway, we use this error type in many places, so I think we should have a shared one. This applies to bitcoin_hashes
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.
I did a refactor patch to use TryFrom
, also I removed the SharedSecretError
and added an InvalidSliceLengthError
. Does not include the core::array::TryFromSliceError
because that type does not implement PartialEq
and Eq
.
src/ecdsa/mod.rs
Outdated
@@ -36,22 +37,21 @@ impl fmt::Display for Signature { | |||
} | |||
|
|||
impl str::FromStr for Signature { | |||
type Err = Error; | |||
fn from_str(s: &str) -> Result<Signature, Error> { | |||
type Err = SignatureFromStrError; |
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.
SignatureParseError
would be shorter.
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.
Used as suggested.
Add a wrapper type `PreallocatedBuffer` that maintains the invariant that the inner buffer is big enough. This allows us to take this as a parameter for preallocated context constructors and remove the error path.
Using `TryFrom` is more terse with no loss of clarity. Refactor only, no logic changes.
65a955b
to
ddd3176
Compare
Currently we have a large general error type. Create specific error types for each function as needed so that a function returns ever variant available in its return type.
ddd3176
to
4e72db6
Compare
@@ -320,30 +321,51 @@ unsafe impl<'buf> PreallocatedContext<'buf> for AllPreallocated<'buf> {} | |||
unsafe impl<'buf> PreallocatedContext<'buf> for SignOnlyPreallocated<'buf> {} | |||
unsafe impl<'buf> PreallocatedContext<'buf> for VerifyOnlyPreallocated<'buf> {} | |||
|
|||
/// A preallocated buffer, enforces the invariant that the buffer is big enough. | |||
#[allow(missing_debug_implementations)] | |||
pub struct PreallocatedBuffer<'buf>(&'buf [AlignedType]); |
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 needs to be mut
as it's unsound otherwise.
Also FYI I probably meant something else but can't remember what it was. I guess I meant &mut AlignedType
which is unsound. We could also just use unsized type pub struct PreallocatedBuffer([AligneType]);
which is more idiomatic.
In ideal world we would use an extern type but those aren't even stable.
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 also probably make AlignedType
contain MaybeUninit
or something like that, as the C code might write padding bytes to it.
I think it wasn't stable (enough) back when I implemented this logic
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.
Yeah, it wasn't stable back then. Definitely should. I promoted this to #707 because I expect this old PR to get closed.
return Err(NotEnoughMemoryError); | ||
} | ||
Ok(PreallocatedBuffer(buf)) | ||
} |
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 also should have an unsafe
constructor to initialize it from pointer and size.
See also #665
Please do not consider this PR for merge until after the upcoming v0.28.0 is released.
This PR is a proof of concept based on ideas discussed:
address
error handling rust-bitcoin#1950The aim is to come up with a new error handling convention that can be applied across the whole organisation.
Now includes a general error enum that has a variant for each other "specific error" type in the lib as well as a macro that users can use to implement
From<err> for CustomError
for their ownCustomError
whereerr
done for every error type in the lib (see example for usage).