Skip to content

Commit 148bc48

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 148bc48

13 files changed

+838
-273
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

+93-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_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,75 @@ 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(Clone, Copy, Debug, PartialEq, Eq)]
435+
pub enum SignatureError {
436+
/// Invalid signature length.
437+
InvalidLength(usize),
438+
/// FFI call failed.
439+
Sys(SysError),
440+
}
441+
442+
impl fmt::Display for SignatureError {
443+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
444+
use SignatureError::*;
445+
446+
match *self {
447+
InvalidLength(len) => write!(f, "invalid signature length: {}", len),
448+
Sys(ref e) => write_err!(f, "sys error"; e),
449+
}
450+
}
451+
}
452+
453+
#[cfg(feature = "std")]
454+
impl std::error::Error for SignatureError {
455+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
456+
use SignatureError::*;
457+
458+
match *self {
459+
InvalidLength(_) => None,
460+
Sys(ref e) => Some(e),
461+
}
462+
}
463+
}
464+
465+
/// Signature is invalid.
466+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
467+
pub enum SignatureFromStrError {
468+
/// Invalid hex string.
469+
Hex(FromHexError),
470+
/// Invalid signature.
471+
Sig(SignatureError),
472+
}
473+
474+
impl fmt::Display for SignatureFromStrError {
475+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
476+
use SignatureFromStrError::*;
477+
478+
match *self {
479+
Hex(ref e) => write_err!(f, "error decoding hex"; e),
480+
Sig(ref e) => write_err!(f, "invalid signature"; e),
481+
}
482+
}
483+
}
484+
485+
#[cfg(feature = "std")]
486+
impl std::error::Error for SignatureFromStrError {
487+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
488+
use SignatureFromStrError::*;
489+
490+
match *self {
491+
Hex(ref e) => Some(e),
492+
Sig(ref e) => Some(e),
493+
}
494+
}
495+
}
496+
497+
impl From<FromHexError> for SignatureFromStrError {
498+
fn from(e: FromHexError) -> Self { Self::Hex(e) }
499+
}
500+
501+
impl From<SignatureError> for SignatureFromStrError {
502+
fn from(e: SignatureError) -> Self { Self::Sig(e) }
503+
}

0 commit comments

Comments
 (0)