Skip to content

Commit 708da6e

Browse files
committed
Introduce specific errors for individual use cases
This patch attempts to introduce a new convention for error handling. The aim 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 ae76211 commit 708da6e

13 files changed

+864
-275
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 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

+24-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,31 @@ 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(Debug, Clone, PartialEq, Eq)]
326+
#[non_exhaustive]
327+
#[allow(missing_copy_implementations)] // Don't implement Copy when we use non_exhaustive.
328+
pub struct NotEnoughMemoryError;
329+
330+
impl fmt::Display for NotEnoughMemoryError {
331+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
332+
f.write_str("not enough preallocated memory for the requested buffer size")
333+
}
334+
}
335+
336+
#[cfg(feature = "std")]
337+
impl std::error::Error for NotEnoughMemoryError {}
338+
323339
impl<'buf, C: Context + PreallocatedContext<'buf>> Secp256k1<C> {
324340
/// 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> {
341+
pub fn preallocated_gen_new(
342+
buf: &'buf mut [AlignedType],
343+
) -> Result<Secp256k1<C>, NotEnoughMemoryError> {
326344
#[cfg(target_arch = "wasm32")]
327345
ffi::types::sanity_checks_for_wasm();
328346

329347
if buf.len() < Self::preallocate_size_gen() {
330-
return Err(Error::NotEnoughMemory);
348+
return Err(NotEnoughMemoryError);
331349
}
332350
// Safe because buf is not null since it is not empty.
333351
let buf = unsafe { NonNull::new_unchecked(buf.as_mut_c_ptr() as *mut c_void) };
@@ -343,7 +361,7 @@ impl<'buf> Secp256k1<AllPreallocated<'buf>> {
343361
/// Creates a new Secp256k1 context with all capabilities.
344362
pub fn preallocated_new(
345363
buf: &'buf mut [AlignedType],
346-
) -> Result<Secp256k1<AllPreallocated<'buf>>, Error> {
364+
) -> Result<Secp256k1<AllPreallocated<'buf>>, NotEnoughMemoryError> {
347365
Secp256k1::preallocated_gen_new(buf)
348366
}
349367
/// Uses the ffi `secp256k1_context_preallocated_size` to check the memory size needed for a context.
@@ -378,7 +396,7 @@ impl<'buf> Secp256k1<SignOnlyPreallocated<'buf>> {
378396
/// Creates a new Secp256k1 context that can only be used for signing.
379397
pub fn preallocated_signing_only(
380398
buf: &'buf mut [AlignedType],
381-
) -> Result<Secp256k1<SignOnlyPreallocated<'buf>>, Error> {
399+
) -> Result<Secp256k1<SignOnlyPreallocated<'buf>>, NotEnoughMemoryError> {
382400
Secp256k1::preallocated_gen_new(buf)
383401
}
384402

@@ -402,7 +420,7 @@ impl<'buf> Secp256k1<VerifyOnlyPreallocated<'buf>> {
402420
/// Creates a new Secp256k1 context that can only be used for verification
403421
pub fn preallocated_verification_only(
404422
buf: &'buf mut [AlignedType],
405-
) -> Result<Secp256k1<VerifyOnlyPreallocated<'buf>>, Error> {
423+
) -> Result<Secp256k1<VerifyOnlyPreallocated<'buf>>, NotEnoughMemoryError> {
406424
Secp256k1::preallocated_gen_new(buf)
407425
}
408426

src/ecdh.rs

+19-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,19 @@ impl<'de> ::serde::Deserialize<'de> for SharedSecret {
183183
}
184184
}
185185

186+
/// Share secret is invalid.
187+
#[derive(Debug, Clone, PartialEq, Eq)]
188+
#[non_exhaustive]
189+
#[allow(missing_copy_implementations)] // Don't implement Copy when we use non_exhaustive.
190+
pub struct SharedSecretError;
191+
192+
impl fmt::Display for SharedSecretError {
193+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str("shared secret is invalid") }
194+
}
195+
196+
#[cfg(feature = "std")]
197+
impl std::error::Error for SharedSecretError {}
198+
186199
#[cfg(test)]
187200
#[allow(unused_imports)]
188201
mod tests {

src/ecdsa/mod.rs

+94-21
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,14 @@ pub mod serialized_signature;
1010
use core::{fmt, ptr, str};
1111

1212
#[cfg(feature = "recovery")]
13-
pub use self::recovery::{RecoverableSignature, RecoveryId};
13+
pub use self::recovery::{InvalidRecoveryIdError, RecoverableSignature, RecoveryId};
1414
pub use self::serialized_signature::SerializedSignature;
15+
use crate::error::{write_err, SysError};
1516
use crate::ffi::CPtr;
1617
#[cfg(feature = "global-context")]
1718
use crate::SECP256K1;
1819
use crate::{
19-
ffi, from_hex, Error, Message, PublicKey, Secp256k1, SecretKey, Signing, Verification,
20+
ffi, from_hex, FromHexError, Message, PublicKey, Secp256k1, SecretKey, Signing, Verification,
2021
};
2122

2223
/// An ECDSA signature
@@ -36,22 +37,21 @@ impl fmt::Display for Signature {
3637
}
3738

3839
impl str::FromStr for Signature {
39-
type Err = Error;
40+
type Err = SignatureFromStrError;
4041
fn from_str(s: &str) -> Result<Self, Self::Err> {
4142
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-
}
43+
let len = from_hex(s, &mut res)?;
44+
let sig = Signature::from_der(&res[0..len])?;
45+
Ok(sig)
4646
}
4747
}
4848

4949
impl Signature {
5050
#[inline]
5151
/// Converts a DER-encoded byte slice to a signature
52-
pub fn from_der(data: &[u8]) -> Result<Signature, Error> {
52+
pub fn from_der(data: &[u8]) -> Result<Signature, SignatureError> {
5353
if data.is_empty() {
54-
return Err(Error::InvalidSignature);
54+
return Err(SignatureError::InvalidLength(0));
5555
}
5656

5757
unsafe {
@@ -65,15 +65,15 @@ impl Signature {
6565
{
6666
Ok(Signature(ret))
6767
} else {
68-
Err(Error::InvalidSignature)
68+
Err(SignatureError::Sys(SysError))
6969
}
7070
}
7171
}
7272

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

7979
unsafe {
@@ -86,7 +86,7 @@ impl Signature {
8686
{
8787
Ok(Signature(ret))
8888
} else {
89-
Err(Error::InvalidSignature)
89+
Err(SignatureError::Sys(SysError))
9090
}
9191
}
9292
}
@@ -95,9 +95,9 @@ impl Signature {
9595
/// only useful for validating signatures in the Bitcoin blockchain from before
9696
/// 2016. It should never be used in new applications. This library does not
9797
/// support serializing to this "format"
98-
pub fn from_der_lax(data: &[u8]) -> Result<Signature, Error> {
98+
pub fn from_der_lax(data: &[u8]) -> Result<Signature, SignatureError> {
9999
if data.is_empty() {
100-
return Err(Error::InvalidSignature);
100+
return Err(SignatureError::InvalidLength(0));
101101
}
102102

103103
unsafe {
@@ -111,7 +111,7 @@ impl Signature {
111111
{
112112
Ok(Signature(ret))
113113
} else {
114-
Err(Error::InvalidSignature)
114+
Err(SignatureError::Sys(SysError))
115115
}
116116
}
117117
}
@@ -194,7 +194,7 @@ impl Signature {
194194
/// The signature must be normalized or verification will fail (see [`Signature::normalize_s`]).
195195
#[inline]
196196
#[cfg(feature = "global-context")]
197-
pub fn verify(&self, msg: &Message, pk: &PublicKey) -> Result<(), Error> {
197+
pub fn verify(&self, msg: &Message, pk: &PublicKey) -> Result<(), SysError> {
198198
SECP256K1.verify_ecdsa(msg, self, pk)
199199
}
200200
}
@@ -366,7 +366,7 @@ impl<C: Verification> Secp256k1<C> {
366366
///
367367
/// ```rust
368368
/// # #[cfg(feature = "rand-std")] {
369-
/// # use secp256k1::{rand, Secp256k1, Message, Error};
369+
/// # use secp256k1::{ecdsa, rand, Secp256k1, Message, SysError};
370370
/// #
371371
/// # let secp = Secp256k1::new();
372372
/// # let (secret_key, public_key) = secp.generate_keypair(&mut rand::thread_rng());
@@ -376,7 +376,7 @@ impl<C: Verification> Secp256k1<C> {
376376
/// assert_eq!(secp.verify_ecdsa(&message, &sig, &public_key), Ok(()));
377377
///
378378
/// let message = Message::from_digest_slice(&[0xcd; 32]).expect("32 bytes");
379-
/// assert_eq!(secp.verify_ecdsa(&message, &sig, &public_key), Err(Error::IncorrectSignature));
379+
/// assert_eq!(secp.verify_ecdsa(&message, &sig, &public_key), Err(SysError));
380380
/// # }
381381
/// ```
382382
#[inline]
@@ -385,7 +385,7 @@ impl<C: Verification> Secp256k1<C> {
385385
msg: &Message,
386386
sig: &Signature,
387387
pk: &PublicKey,
388-
) -> Result<(), Error> {
388+
) -> Result<(), SysError> {
389389
unsafe {
390390
if ffi::secp256k1_ecdsa_verify(
391391
self.ctx.as_ptr(),
@@ -394,7 +394,7 @@ impl<C: Verification> Secp256k1<C> {
394394
pk.as_c_ptr(),
395395
) == 0
396396
{
397-
Err(Error::IncorrectSignature)
397+
Err(SysError)
398398
} else {
399399
Ok(())
400400
}
@@ -429,3 +429,76 @@ pub(crate) fn der_length_check(sig: &ffi::Signature, max_len: usize) -> bool {
429429
}
430430
len <= max_len
431431
}
432+
433+
/// Signature is invalid.
434+
#[derive(Debug, Clone, PartialEq, Eq)]
435+
#[non_exhaustive]
436+
pub enum SignatureError {
437+
/// Invalid signature length.
438+
InvalidLength(usize),
439+
/// FFI call failed.
440+
Sys(SysError),
441+
}
442+
443+
impl fmt::Display for SignatureError {
444+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
445+
use SignatureError::*;
446+
447+
match *self {
448+
InvalidLength(len) => write!(f, "invalid signature length: {}", len),
449+
Sys(ref e) => write_err!(f, "sys error"; e),
450+
}
451+
}
452+
}
453+
454+
#[cfg(feature = "std")]
455+
impl std::error::Error for SignatureError {
456+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
457+
use SignatureError::*;
458+
459+
match *self {
460+
InvalidLength(_) => None,
461+
Sys(ref e) => Some(e),
462+
}
463+
}
464+
}
465+
466+
/// Signature is invalid.
467+
#[derive(Debug, Clone, PartialEq, Eq)]
468+
pub enum SignatureFromStrError {
469+
/// Invalid hex string.
470+
Hex(FromHexError),
471+
/// Invalid signature.
472+
Sig(SignatureError),
473+
}
474+
475+
impl fmt::Display for SignatureFromStrError {
476+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
477+
use SignatureFromStrError::*;
478+
479+
match *self {
480+
Hex(ref e) => write_err!(f, "error decoding hex"; e),
481+
Sig(ref e) => write_err!(f, "invalid signature"; e),
482+
}
483+
}
484+
}
485+
486+
#[cfg(feature = "std")]
487+
impl std::error::Error for SignatureFromStrError {
488+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
489+
use SignatureFromStrError::*;
490+
491+
match *self {
492+
Hex(ref e) => Some(e),
493+
Sig(ref e) => Some(e),
494+
}
495+
}
496+
}
497+
498+
impl From<FromHexError> for SignatureFromStrError {
499+
fn from(e: FromHexError) -> Self { Self::Hex(e) }
500+
}
501+
502+
impl From<SignatureError> for SignatureFromStrError {
503+
fn from(e: SignatureError) -> Self { Self::Sig(e) }
504+
}

0 commit comments

Comments
 (0)