Skip to content

Commit f626506

Browse files
committed
Introduce specific errors for individual use cases
This PR attempts to introduce a new convention for error handling. The aim of which is to make errors more informative to help debugging, to make the code easier to read, and to help stabalize the API. Each function that returns a `Result` returns is made to return an error that is either a struct or an enum in which _every_ variant is used. We then provide a general purpose error for the crate and conversion functions to it from all the other error types.
1 parent c06263d commit f626506

12 files changed

+746
-268
lines changed

examples/sign_verify_recovery.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ extern crate secp256k1;
44
use bitcoin_hashes::{sha256, Hash};
55
use secp256k1::{ecdsa, Error, Message, PublicKey, Secp256k1, SecretKey, Signing, Verification};
66

7+
// Notice that we provide a general error type for this crate and conversion
8+
// functions to it from all the other error types so `?` works as expected.
79
fn recover<C: Verification>(
810
secp: &Secp256k1<C>,
911
msg: &[u8],
@@ -15,7 +17,7 @@ fn recover<C: Verification>(
1517
let id = ecdsa::RecoveryId::from_i32(recovery_id as i32)?;
1618
let sig = ecdsa::RecoverableSignature::from_compact(&sig, id)?;
1719

18-
secp.recover_ecdsa(&msg, &sig)
20+
Ok(secp.recover_ecdsa(&msg, &sig)?)
1921
}
2022

2123
fn sign_recovery<C: Signing>(

src/context.rs

+22-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// SPDX-License-Identifier: CC0-1.0
22

3+
use core::fmt;
34
use core::marker::PhantomData;
45
use core::mem::ManuallyDrop;
56
use core::ptr::NonNull;
@@ -8,7 +9,7 @@ use core::ptr::NonNull;
89
pub use self::alloc_only::*;
910
use crate::ffi::types::{c_uint, c_void, AlignedType};
1011
use crate::ffi::{self, CPtr};
11-
use crate::{Error, Secp256k1};
12+
use crate::Secp256k1;
1213

1314
#[cfg(all(feature = "global-context", feature = "std"))]
1415
/// Module implementing a singleton pattern for a global `Secp256k1` context.
@@ -320,14 +321,29 @@ unsafe impl<'buf> PreallocatedContext<'buf> for AllPreallocated<'buf> {}
320321
unsafe impl<'buf> PreallocatedContext<'buf> for SignOnlyPreallocated<'buf> {}
321322
unsafe impl<'buf> PreallocatedContext<'buf> for VerifyOnlyPreallocated<'buf> {}
322323

324+
/// Not enough preallocated memory for the requested buffer size.
325+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
326+
pub struct NotEnoughMemoryError;
327+
328+
impl fmt::Display for NotEnoughMemoryError {
329+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
330+
f.write_str("not enough preallocated memory for the requested buffer size")
331+
}
332+
}
333+
334+
#[cfg(feature = "std")]
335+
impl std::error::Error for NotEnoughMemoryError {}
336+
323337
impl<'buf, C: Context + PreallocatedContext<'buf>> Secp256k1<C> {
324338
/// Lets you create a context with a preallocated buffer in a generic manner (sign/verify/all).
325-
pub fn preallocated_gen_new(buf: &'buf mut [AlignedType]) -> Result<Secp256k1<C>, Error> {
339+
pub fn preallocated_gen_new(
340+
buf: &'buf mut [AlignedType],
341+
) -> Result<Secp256k1<C>, NotEnoughMemoryError> {
326342
#[cfg(target_arch = "wasm32")]
327343
ffi::types::sanity_checks_for_wasm();
328344

329345
if buf.len() < Self::preallocate_size_gen() {
330-
return Err(Error::NotEnoughMemory);
346+
return Err(NotEnoughMemoryError);
331347
}
332348
// Safe because buf is not null since it is not empty.
333349
let buf = unsafe { NonNull::new_unchecked(buf.as_mut_c_ptr() as *mut c_void) };
@@ -343,7 +359,7 @@ impl<'buf> Secp256k1<AllPreallocated<'buf>> {
343359
/// Creates a new Secp256k1 context with all capabilities.
344360
pub fn preallocated_new(
345361
buf: &'buf mut [AlignedType],
346-
) -> Result<Secp256k1<AllPreallocated<'buf>>, Error> {
362+
) -> Result<Secp256k1<AllPreallocated<'buf>>, NotEnoughMemoryError> {
347363
Secp256k1::preallocated_gen_new(buf)
348364
}
349365
/// Uses the ffi `secp256k1_context_preallocated_size` to check the memory size needed for a context.
@@ -378,7 +394,7 @@ impl<'buf> Secp256k1<SignOnlyPreallocated<'buf>> {
378394
/// Creates a new Secp256k1 context that can only be used for signing.
379395
pub fn preallocated_signing_only(
380396
buf: &'buf mut [AlignedType],
381-
) -> Result<Secp256k1<SignOnlyPreallocated<'buf>>, Error> {
397+
) -> Result<Secp256k1<SignOnlyPreallocated<'buf>>, NotEnoughMemoryError> {
382398
Secp256k1::preallocated_gen_new(buf)
383399
}
384400

@@ -402,7 +418,7 @@ impl<'buf> Secp256k1<VerifyOnlyPreallocated<'buf>> {
402418
/// Creates a new Secp256k1 context that can only be used for verification
403419
pub fn preallocated_verification_only(
404420
buf: &'buf mut [AlignedType],
405-
) -> Result<Secp256k1<VerifyOnlyPreallocated<'buf>>, Error> {
421+
) -> Result<Secp256k1<VerifyOnlyPreallocated<'buf>>, NotEnoughMemoryError> {
406422
Secp256k1::preallocated_gen_new(buf)
407423
}
408424

src/ecdh.rs

+17-6
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44
//!
55
66
use core::borrow::Borrow;
7-
use core::{ptr, str};
7+
use core::{fmt, ptr, str};
88

99
use secp256k1_sys::types::{c_int, c_uchar, c_void};
1010

11+
use crate::constants;
1112
use crate::ffi::{self, CPtr};
1213
use crate::key::{PublicKey, SecretKey};
13-
use crate::{constants, Error};
1414

1515
// The logic for displaying shared secrets relies on this (see `secret.rs`).
1616
const SHARED_SECRET_SIZE: usize = constants::SECRET_KEY_SIZE;
@@ -65,25 +65,25 @@ impl SharedSecret {
6565

6666
/// Creates a shared secret from `bytes` slice.
6767
#[inline]
68-
pub fn from_slice(bytes: &[u8]) -> Result<SharedSecret, Error> {
68+
pub fn from_slice(bytes: &[u8]) -> Result<SharedSecret, SharedSecretError> {
6969
match bytes.len() {
7070
SHARED_SECRET_SIZE => {
7171
let mut ret = [0u8; SHARED_SECRET_SIZE];
7272
ret[..].copy_from_slice(bytes);
7373
Ok(SharedSecret(ret))
7474
}
75-
_ => Err(Error::InvalidSharedSecret),
75+
_ => Err(SharedSecretError),
7676
}
7777
}
7878
}
7979

8080
impl str::FromStr for SharedSecret {
81-
type Err = Error;
81+
type Err = SharedSecretError;
8282
fn from_str(s: &str) -> Result<Self, Self::Err> {
8383
let mut res = [0u8; SHARED_SECRET_SIZE];
8484
match crate::from_hex(s, &mut res) {
8585
Ok(SHARED_SECRET_SIZE) => Ok(SharedSecret::from_bytes(res)),
86-
_ => Err(Error::InvalidSharedSecret),
86+
_ => Err(SharedSecretError),
8787
}
8888
}
8989
}
@@ -183,6 +183,17 @@ impl<'de> ::serde::Deserialize<'de> for SharedSecret {
183183
}
184184
}
185185

186+
/// Share secret is invalid.
187+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
188+
pub struct SharedSecretError;
189+
190+
impl fmt::Display for SharedSecretError {
191+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str("shared secret is invalid") }
192+
}
193+
194+
#[cfg(feature = "std")]
195+
impl std::error::Error for SharedSecretError {}
196+
186197
#[cfg(test)]
187198
#[allow(unused_imports)]
188199
mod tests {

src/ecdsa/mod.rs

+94-20
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,14 @@ use core::{fmt, ptr, str};
1212
#[cfg(feature = "recovery")]
1313
pub use self::recovery::{RecoverableSignature, RecoveryId};
1414
pub use self::serialized_signature::SerializedSignature;
15+
#[cfg(feature = "recovery")]
16+
pub use crate::ecdsa::recovery::InvalidRecoveryIdError;
17+
use crate::error::{write_err, SysError};
1518
use crate::ffi::CPtr;
1619
#[cfg(feature = "global-context")]
1720
use crate::SECP256K1;
1821
use crate::{
19-
ffi, from_hex, Error, Message, PublicKey, Secp256k1, SecretKey, Signing, Verification,
22+
ffi, from_hex, FromHexError, Message, PublicKey, Secp256k1, SecretKey, Signing, Verification,
2023
};
2124

2225
/// An ECDSA signature
@@ -36,22 +39,21 @@ impl fmt::Display for Signature {
3639
}
3740

3841
impl str::FromStr for Signature {
39-
type Err = Error;
42+
type Err = SignatureFromStrError;
4043
fn from_str(s: &str) -> Result<Self, Self::Err> {
4144
let mut res = [0u8; 72];
42-
match from_hex(s, &mut res) {
43-
Ok(x) => Signature::from_der(&res[0..x]),
44-
_ => Err(Error::InvalidSignature),
45-
}
45+
let len = from_hex(s, &mut res)?;
46+
let sig = Signature::from_der(&res[0..len])?;
47+
Ok(sig)
4648
}
4749
}
4850

4951
impl Signature {
5052
#[inline]
5153
/// Converts a DER-encoded byte slice to a signature
52-
pub fn from_der(data: &[u8]) -> Result<Signature, Error> {
54+
pub fn from_der(data: &[u8]) -> Result<Signature, SignatureError> {
5355
if data.is_empty() {
54-
return Err(Error::InvalidSignature);
56+
return Err(SignatureError::InvalidLength(0));
5557
}
5658

5759
unsafe {
@@ -65,15 +67,15 @@ impl Signature {
6567
{
6668
Ok(Signature(ret))
6769
} else {
68-
Err(Error::InvalidSignature)
70+
Err(SignatureError::Sys(SysError))
6971
}
7072
}
7173
}
7274

7375
/// Converts a 64-byte compact-encoded byte slice to a signature
74-
pub fn from_compact(data: &[u8]) -> Result<Signature, Error> {
76+
pub fn from_compact(data: &[u8]) -> Result<Signature, SignatureError> {
7577
if data.len() != 64 {
76-
return Err(Error::InvalidSignature);
78+
return Err(SignatureError::InvalidLength(data.len()));
7779
}
7880

7981
unsafe {
@@ -86,7 +88,7 @@ impl Signature {
8688
{
8789
Ok(Signature(ret))
8890
} else {
89-
Err(Error::InvalidSignature)
91+
Err(SignatureError::Sys(SysError))
9092
}
9193
}
9294
}
@@ -95,9 +97,9 @@ impl Signature {
9597
/// only useful for validating signatures in the Bitcoin blockchain from before
9698
/// 2016. It should never be used in new applications. This library does not
9799
/// support serializing to this "format"
98-
pub fn from_der_lax(data: &[u8]) -> Result<Signature, Error> {
100+
pub fn from_der_lax(data: &[u8]) -> Result<Signature, SignatureError> {
99101
if data.is_empty() {
100-
return Err(Error::InvalidSignature);
102+
return Err(SignatureError::InvalidLength(0));
101103
}
102104

103105
unsafe {
@@ -111,7 +113,7 @@ impl Signature {
111113
{
112114
Ok(Signature(ret))
113115
} else {
114-
Err(Error::InvalidSignature)
116+
Err(SignatureError::Sys(SysError))
115117
}
116118
}
117119
}
@@ -194,7 +196,7 @@ impl Signature {
194196
/// The signature must be normalized or verification will fail (see [`Signature::normalize_s`]).
195197
#[inline]
196198
#[cfg(feature = "global-context")]
197-
pub fn verify(&self, msg: &Message, pk: &PublicKey) -> Result<(), Error> {
199+
pub fn verify(&self, msg: &Message, pk: &PublicKey) -> Result<(), SysError> {
198200
SECP256K1.verify_ecdsa(msg, self, pk)
199201
}
200202
}
@@ -366,7 +368,7 @@ impl<C: Verification> Secp256k1<C> {
366368
///
367369
/// ```rust
368370
/// # #[cfg(feature = "rand-std")] {
369-
/// # use secp256k1::{rand, Secp256k1, Message, Error};
371+
/// # use secp256k1::{ecdsa, rand, Secp256k1, Message, SysError};
370372
/// #
371373
/// # let secp = Secp256k1::new();
372374
/// # let (secret_key, public_key) = secp.generate_keypair(&mut rand::thread_rng());
@@ -376,7 +378,7 @@ impl<C: Verification> Secp256k1<C> {
376378
/// assert_eq!(secp.verify_ecdsa(&message, &sig, &public_key), Ok(()));
377379
///
378380
/// let message = Message::from_slice(&[0xcd; 32]).expect("32 bytes");
379-
/// assert_eq!(secp.verify_ecdsa(&message, &sig, &public_key), Err(Error::IncorrectSignature));
381+
/// assert_eq!(secp.verify_ecdsa(&message, &sig, &public_key), Err(SysError));
380382
/// # }
381383
/// ```
382384
#[inline]
@@ -385,7 +387,7 @@ impl<C: Verification> Secp256k1<C> {
385387
msg: &Message,
386388
sig: &Signature,
387389
pk: &PublicKey,
388-
) -> Result<(), Error> {
390+
) -> Result<(), SysError> {
389391
unsafe {
390392
if ffi::secp256k1_ecdsa_verify(
391393
self.ctx.as_ptr(),
@@ -394,7 +396,7 @@ impl<C: Verification> Secp256k1<C> {
394396
pk.as_c_ptr(),
395397
) == 0
396398
{
397-
Err(Error::IncorrectSignature)
399+
Err(SysError)
398400
} else {
399401
Ok(())
400402
}
@@ -429,3 +431,75 @@ pub(crate) fn der_length_check(sig: &ffi::Signature, max_len: usize) -> bool {
429431
}
430432
len <= max_len
431433
}
434+
435+
/// Signature is invalid.
436+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
437+
pub enum SignatureError {
438+
/// Invalid signature length.
439+
InvalidLength(usize),
440+
/// FFI call failed.
441+
Sys(SysError),
442+
}
443+
444+
impl fmt::Display for SignatureError {
445+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
446+
use SignatureError::*;
447+
448+
match *self {
449+
InvalidLength(len) => write!(f, "invalid signature length: {}", len),
450+
Sys(ref e) => write_err!(f, "sys error"; e),
451+
}
452+
}
453+
}
454+
455+
#[cfg(feature = "std")]
456+
impl std::error::Error for SignatureError {
457+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
458+
use SignatureError::*;
459+
460+
match *self {
461+
InvalidLength(_) => None,
462+
Sys(ref e) => Some(e),
463+
}
464+
}
465+
}
466+
467+
/// Signature is invalid.
468+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
469+
pub enum SignatureFromStrError {
470+
/// Invalid hex string.
471+
Hex(FromHexError),
472+
/// Invalid signature.
473+
Sig(SignatureError),
474+
}
475+
476+
impl fmt::Display for SignatureFromStrError {
477+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
478+
use SignatureFromStrError::*;
479+
480+
match *self {
481+
Hex(ref e) => write_err!(f, "error decoding hex"; e),
482+
Sig(ref e) => write_err!(f, "invalid signature"; e),
483+
}
484+
}
485+
}
486+
487+
#[cfg(feature = "std")]
488+
impl std::error::Error for SignatureFromStrError {
489+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
490+
use SignatureFromStrError::*;
491+
492+
match *self {
493+
Hex(ref e) => Some(e),
494+
Sig(ref e) => Some(e),
495+
}
496+
}
497+
}
498+
499+
impl From<FromHexError> for SignatureFromStrError {
500+
fn from(e: FromHexError) -> Self { Self::Hex(e) }
501+
}
502+
503+
impl From<SignatureError> for SignatureFromStrError {
504+
fn from(e: SignatureError) -> Self { Self::Sig(e) }
505+
}

0 commit comments

Comments
 (0)