Skip to content

Commit b18475c

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 b18475c

12 files changed

+740
-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

+93-20
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@ use core::{fmt, ptr, str};
1212
#[cfg(feature = "recovery")]
1313
pub use self::recovery::{RecoverableSignature, RecoveryId};
1414
pub use self::serialized_signature::SerializedSignature;
15+
pub use crate::ecdsa::recovery::InvalidRecoveryIdError;
16+
use crate::error::{write_err, SysError};
1517
use crate::ffi::CPtr;
1618
#[cfg(feature = "global-context")]
1719
use crate::SECP256K1;
1820
use crate::{
19-
ffi, from_hex, Error, Message, PublicKey, Secp256k1, SecretKey, Signing, Verification,
21+
ffi, from_hex, FromHexError, Message, PublicKey, Secp256k1, SecretKey, Signing, Verification,
2022
};
2123

2224
/// An ECDSA signature
@@ -36,22 +38,21 @@ impl fmt::Display for Signature {
3638
}
3739

3840
impl str::FromStr for Signature {
39-
type Err = Error;
41+
type Err = SignatureFromStrError;
4042
fn from_str(s: &str) -> Result<Self, Self::Err> {
4143
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-
}
44+
let len = from_hex(s, &mut res)?;
45+
let sig = Signature::from_der(&res[0..len])?;
46+
Ok(sig)
4647
}
4748
}
4849

4950
impl Signature {
5051
#[inline]
5152
/// Converts a DER-encoded byte slice to a signature
52-
pub fn from_der(data: &[u8]) -> Result<Signature, Error> {
53+
pub fn from_der(data: &[u8]) -> Result<Signature, SignatureError> {
5354
if data.is_empty() {
54-
return Err(Error::InvalidSignature);
55+
return Err(SignatureError::InvalidLength(0));
5556
}
5657

5758
unsafe {
@@ -65,15 +66,15 @@ impl Signature {
6566
{
6667
Ok(Signature(ret))
6768
} else {
68-
Err(Error::InvalidSignature)
69+
Err(SignatureError::Sys(SysError))
6970
}
7071
}
7172
}
7273

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

7980
unsafe {
@@ -86,7 +87,7 @@ impl Signature {
8687
{
8788
Ok(Signature(ret))
8889
} else {
89-
Err(Error::InvalidSignature)
90+
Err(SignatureError::Sys(SysError))
9091
}
9192
}
9293
}
@@ -95,9 +96,9 @@ impl Signature {
9596
/// only useful for validating signatures in the Bitcoin blockchain from before
9697
/// 2016. It should never be used in new applications. This library does not
9798
/// support serializing to this "format"
98-
pub fn from_der_lax(data: &[u8]) -> Result<Signature, Error> {
99+
pub fn from_der_lax(data: &[u8]) -> Result<Signature, SignatureError> {
99100
if data.is_empty() {
100-
return Err(Error::InvalidSignature);
101+
return Err(SignatureError::InvalidLength(0));
101102
}
102103

103104
unsafe {
@@ -111,7 +112,7 @@ impl Signature {
111112
{
112113
Ok(Signature(ret))
113114
} else {
114-
Err(Error::InvalidSignature)
115+
Err(SignatureError::Sys(SysError))
115116
}
116117
}
117118
}
@@ -194,7 +195,7 @@ impl Signature {
194195
/// The signature must be normalized or verification will fail (see [`Signature::normalize_s`]).
195196
#[inline]
196197
#[cfg(feature = "global-context")]
197-
pub fn verify(&self, msg: &Message, pk: &PublicKey) -> Result<(), Error> {
198+
pub fn verify(&self, msg: &Message, pk: &PublicKey) -> Result<(), SysError> {
198199
SECP256K1.verify_ecdsa(msg, self, pk)
199200
}
200201
}
@@ -366,7 +367,7 @@ impl<C: Verification> Secp256k1<C> {
366367
///
367368
/// ```rust
368369
/// # #[cfg(feature = "rand-std")] {
369-
/// # use secp256k1::{rand, Secp256k1, Message, Error};
370+
/// # use secp256k1::{ecdsa, rand, Secp256k1, Message};
370371
/// #
371372
/// # let secp = Secp256k1::new();
372373
/// # let (secret_key, public_key) = secp.generate_keypair(&mut rand::thread_rng());
@@ -376,7 +377,7 @@ impl<C: Verification> Secp256k1<C> {
376377
/// assert_eq!(secp.verify_ecdsa(&message, &sig, &public_key), Ok(()));
377378
///
378379
/// let message = Message::from_slice(&[0xcd; 32]).expect("32 bytes");
379-
/// assert_eq!(secp.verify_ecdsa(&message, &sig, &public_key), Err(Error::IncorrectSignature));
380+
/// assert_eq!(secp.verify_ecdsa(&message, &sig, &public_key), Err(ecdsa::SignatureError));
380381
/// # }
381382
/// ```
382383
#[inline]
@@ -385,7 +386,7 @@ impl<C: Verification> Secp256k1<C> {
385386
msg: &Message,
386387
sig: &Signature,
387388
pk: &PublicKey,
388-
) -> Result<(), Error> {
389+
) -> Result<(), SysError> {
389390
unsafe {
390391
if ffi::secp256k1_ecdsa_verify(
391392
self.ctx.as_ptr(),
@@ -394,7 +395,7 @@ impl<C: Verification> Secp256k1<C> {
394395
pk.as_c_ptr(),
395396
) == 0
396397
{
397-
Err(Error::IncorrectSignature)
398+
Err(SysError)
398399
} else {
399400
Ok(())
400401
}
@@ -429,3 +430,75 @@ pub(crate) fn der_length_check(sig: &ffi::Signature, max_len: usize) -> bool {
429430
}
430431
len <= max_len
431432
}
433+
434+
/// Signature is invalid.
435+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
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(Clone, Copy, Debug, 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)