Skip to content

Commit 384ad31

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 384ad31

12 files changed

+571
-260
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

+17-6
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use core::ptr::NonNull;
88
pub use self::alloc_only::*;
99
use crate::ffi::types::{c_uint, c_void, AlignedType};
1010
use crate::ffi::{self, CPtr};
11-
use crate::{Error, Secp256k1};
11+
use crate::Secp256k1;
1212

1313
#[cfg(all(feature = "global-context", feature = "std"))]
1414
/// Module implementing a singleton pattern for a global `Secp256k1` context.
@@ -320,14 +320,25 @@ unsafe impl<'buf> PreallocatedContext<'buf> for AllPreallocated<'buf> {}
320320
unsafe impl<'buf> PreallocatedContext<'buf> for SignOnlyPreallocated<'buf> {}
321321
unsafe impl<'buf> PreallocatedContext<'buf> for VerifyOnlyPreallocated<'buf> {}
322322

323+
/// Not enough preallocated memory for the requested buffer size.
324+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
325+
pub struct NotEnoughMemoryError;
326+
327+
crate::error::impl_simple_struct_error!(
328+
NotEnoughMemoryError,
329+
"not enough preallocated memory for the requested buffer size"
330+
);
331+
323332
impl<'buf, C: Context + PreallocatedContext<'buf>> Secp256k1<C> {
324333
/// 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> {
334+
pub fn preallocated_gen_new(
335+
buf: &'buf mut [AlignedType],
336+
) -> Result<Secp256k1<C>, NotEnoughMemoryError> {
326337
#[cfg(target_arch = "wasm32")]
327338
ffi::types::sanity_checks_for_wasm();
328339

329340
if buf.len() < Self::preallocate_size_gen() {
330-
return Err(Error::NotEnoughMemory);
341+
return Err(NotEnoughMemoryError);
331342
}
332343
// Safe because buf is not null since it is not empty.
333344
let buf = unsafe { NonNull::new_unchecked(buf.as_mut_c_ptr() as *mut c_void) };
@@ -343,7 +354,7 @@ impl<'buf> Secp256k1<AllPreallocated<'buf>> {
343354
/// Creates a new Secp256k1 context with all capabilities.
344355
pub fn preallocated_new(
345356
buf: &'buf mut [AlignedType],
346-
) -> Result<Secp256k1<AllPreallocated<'buf>>, Error> {
357+
) -> Result<Secp256k1<AllPreallocated<'buf>>, NotEnoughMemoryError> {
347358
Secp256k1::preallocated_gen_new(buf)
348359
}
349360
/// Uses the ffi `secp256k1_context_preallocated_size` to check the memory size needed for a context.
@@ -378,7 +389,7 @@ impl<'buf> Secp256k1<SignOnlyPreallocated<'buf>> {
378389
/// Creates a new Secp256k1 context that can only be used for signing.
379390
pub fn preallocated_signing_only(
380391
buf: &'buf mut [AlignedType],
381-
) -> Result<Secp256k1<SignOnlyPreallocated<'buf>>, Error> {
392+
) -> Result<Secp256k1<SignOnlyPreallocated<'buf>>, NotEnoughMemoryError> {
382393
Secp256k1::preallocated_gen_new(buf)
383394
}
384395

@@ -402,7 +413,7 @@ impl<'buf> Secp256k1<VerifyOnlyPreallocated<'buf>> {
402413
/// Creates a new Secp256k1 context that can only be used for verification
403414
pub fn preallocated_verification_only(
404415
buf: &'buf mut [AlignedType],
405-
) -> Result<Secp256k1<VerifyOnlyPreallocated<'buf>>, Error> {
416+
) -> Result<Secp256k1<VerifyOnlyPreallocated<'buf>>, NotEnoughMemoryError> {
406417
Secp256k1::preallocated_gen_new(buf)
407418
}
408419

src/ecdh.rs

+11-5
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ use core::{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,12 @@ 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+
crate::error::impl_simple_struct_error!(SharedSecretError, "shared secret is invalid");
191+
186192
#[cfg(test)]
187193
#[allow(unused_imports)]
188194
mod tests {

src/ecdsa/mod.rs

+29-19
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,7 @@ pub use self::serialized_signature::SerializedSignature;
1515
use crate::ffi::CPtr;
1616
#[cfg(feature = "global-context")]
1717
use crate::SECP256K1;
18-
use crate::{
19-
ffi, from_hex, Error, Message, PublicKey, Secp256k1, SecretKey, Signing, Verification,
20-
};
18+
use crate::{ffi, from_hex, Message, PublicKey, Secp256k1, SecretKey, Signing, Verification};
2119

2220
/// An ECDSA signature
2321
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
@@ -36,22 +34,22 @@ impl fmt::Display for Signature {
3634
}
3735

3836
impl str::FromStr for Signature {
39-
type Err = Error;
37+
type Err = SignatureError;
4038
fn from_str(s: &str) -> Result<Self, Self::Err> {
4139
let mut res = [0u8; 72];
4240
match from_hex(s, &mut res) {
4341
Ok(x) => Signature::from_der(&res[0..x]),
44-
_ => Err(Error::InvalidSignature),
42+
_ => Err(SignatureError),
4543
}
4644
}
4745
}
4846

4947
impl Signature {
5048
#[inline]
5149
/// Converts a DER-encoded byte slice to a signature
52-
pub fn from_der(data: &[u8]) -> Result<Signature, Error> {
50+
pub fn from_der(data: &[u8]) -> Result<Signature, SignatureError> {
5351
if data.is_empty() {
54-
return Err(Error::InvalidSignature);
52+
return Err(SignatureError);
5553
}
5654

5755
unsafe {
@@ -65,15 +63,15 @@ impl Signature {
6563
{
6664
Ok(Signature(ret))
6765
} else {
68-
Err(Error::InvalidSignature)
66+
Err(SignatureError)
6967
}
7068
}
7169
}
7270

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

7977
unsafe {
@@ -86,7 +84,7 @@ impl Signature {
8684
{
8785
Ok(Signature(ret))
8886
} else {
89-
Err(Error::InvalidSignature)
87+
Err(SignatureError)
9088
}
9189
}
9290
}
@@ -95,9 +93,9 @@ impl Signature {
9593
/// only useful for validating signatures in the Bitcoin blockchain from before
9694
/// 2016. It should never be used in new applications. This library does not
9795
/// support serializing to this "format"
98-
pub fn from_der_lax(data: &[u8]) -> Result<Signature, Error> {
96+
pub fn from_der_lax(data: &[u8]) -> Result<Signature, SignatureError> {
9997
if data.is_empty() {
100-
return Err(Error::InvalidSignature);
98+
return Err(SignatureError);
10199
}
102100

103101
unsafe {
@@ -111,7 +109,7 @@ impl Signature {
111109
{
112110
Ok(Signature(ret))
113111
} else {
114-
Err(Error::InvalidSignature)
112+
Err(SignatureError)
115113
}
116114
}
117115
}
@@ -194,7 +192,7 @@ impl Signature {
194192
/// The signature must be normalized or verification will fail (see [`Signature::normalize_s`]).
195193
#[inline]
196194
#[cfg(feature = "global-context")]
197-
pub fn verify(&self, msg: &Message, pk: &PublicKey) -> Result<(), Error> {
195+
pub fn verify(&self, msg: &Message, pk: &PublicKey) -> Result<(), SignatureError> {
198196
SECP256K1.verify_ecdsa(msg, self, pk)
199197
}
200198
}
@@ -366,7 +364,7 @@ impl<C: Verification> Secp256k1<C> {
366364
///
367365
/// ```rust
368366
/// # #[cfg(feature = "rand-std")] {
369-
/// # use secp256k1::{rand, Secp256k1, Message, Error};
367+
/// # use secp256k1::{ecdsa, rand, Secp256k1, Message};
370368
/// #
371369
/// # let secp = Secp256k1::new();
372370
/// # let (secret_key, public_key) = secp.generate_keypair(&mut rand::thread_rng());
@@ -376,7 +374,7 @@ impl<C: Verification> Secp256k1<C> {
376374
/// assert_eq!(secp.verify_ecdsa(&message, &sig, &public_key), Ok(()));
377375
///
378376
/// let message = Message::from_slice(&[0xcd; 32]).expect("32 bytes");
379-
/// assert_eq!(secp.verify_ecdsa(&message, &sig, &public_key), Err(Error::IncorrectSignature));
377+
/// assert_eq!(secp.verify_ecdsa(&message, &sig, &public_key), Err(ecdsa::SignatureError));
380378
/// # }
381379
/// ```
382380
#[inline]
@@ -385,7 +383,7 @@ impl<C: Verification> Secp256k1<C> {
385383
msg: &Message,
386384
sig: &Signature,
387385
pk: &PublicKey,
388-
) -> Result<(), Error> {
386+
) -> Result<(), SignatureError> {
389387
unsafe {
390388
if ffi::secp256k1_ecdsa_verify(
391389
self.ctx.as_ptr(),
@@ -394,7 +392,7 @@ impl<C: Verification> Secp256k1<C> {
394392
pk.as_c_ptr(),
395393
) == 0
396394
{
397-
Err(Error::IncorrectSignature)
395+
Err(SignatureError)
398396
} else {
399397
Ok(())
400398
}
@@ -429,3 +427,15 @@ pub(crate) fn der_length_check(sig: &ffi::Signature, max_len: usize) -> bool {
429427
}
430428
len <= max_len
431429
}
430+
431+
/// Signature is invalid.
432+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
433+
pub struct SignatureError;
434+
435+
crate::error::impl_simple_struct_error!(SignatureError, "signature is invalid");
436+
437+
/// Recovery ID is invalid.
438+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
439+
pub struct RecoveryIdError;
440+
441+
crate::error::impl_simple_struct_error!(RecoveryIdError, "recover ID is invalid");

src/ecdsa/recovery.rs

+19-15
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
use core::ptr;
88

99
use self::super_ffi::CPtr;
10-
use super::ffi as super_ffi;
10+
use super::{ffi as super_ffi, RecoveryIdError, SignatureError};
1111
use crate::ecdsa::Signature;
1212
use crate::ffi::recovery as ffi;
13-
use crate::{key, Error, Message, Secp256k1, Signing, Verification};
13+
use crate::{key, Message, Secp256k1, Signing, Verification};
1414

1515
/// A tag used for recovering the public key from a compact signature.
1616
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
@@ -23,10 +23,10 @@ pub struct RecoverableSignature(ffi::RecoverableSignature);
2323
impl RecoveryId {
2424
#[inline]
2525
/// Allows library users to create valid recovery IDs from i32.
26-
pub fn from_i32(id: i32) -> Result<RecoveryId, Error> {
26+
pub fn from_i32(id: i32) -> Result<RecoveryId, RecoveryIdError> {
2727
match id {
2828
0..=3 => Ok(RecoveryId(id)),
29-
_ => Err(Error::InvalidRecoveryId),
29+
_ => Err(RecoveryIdError),
3030
}
3131
}
3232

@@ -39,16 +39,19 @@ impl RecoverableSignature {
3939
#[inline]
4040
/// Converts a compact-encoded byte slice to a signature. This
4141
/// representation is nonstandard and defined by the libsecp256k1 library.
42-
pub fn from_compact(data: &[u8], recid: RecoveryId) -> Result<RecoverableSignature, Error> {
42+
pub fn from_compact(
43+
data: &[u8],
44+
recid: RecoveryId,
45+
) -> Result<RecoverableSignature, SignatureError> {
4346
if data.is_empty() {
44-
return Err(Error::InvalidSignature);
47+
return Err(SignatureError);
4548
}
4649

4750
let mut ret = ffi::RecoverableSignature::new();
4851

4952
unsafe {
5053
if data.len() != 64 {
51-
Err(Error::InvalidSignature)
54+
Err(SignatureError)
5255
} else if ffi::secp256k1_ecdsa_recoverable_signature_parse_compact(
5356
super_ffi::secp256k1_context_no_precomp,
5457
&mut ret,
@@ -58,7 +61,7 @@ impl RecoverableSignature {
5861
{
5962
Ok(RecoverableSignature(ret))
6063
} else {
61-
Err(Error::InvalidSignature)
64+
Err(SignatureError)
6265
}
6366
}
6467
}
@@ -113,7 +116,7 @@ impl RecoverableSignature {
113116
/// verify-capable context.
114117
#[inline]
115118
#[cfg(feature = "global-context")]
116-
pub fn recover(&self, msg: &Message) -> Result<key::PublicKey, Error> {
119+
pub fn recover(&self, msg: &Message) -> Result<key::PublicKey, SignatureError> {
117120
crate::SECP256K1.recover_ecdsa(msg, self)
118121
}
119122
}
@@ -191,7 +194,7 @@ impl<C: Verification> Secp256k1<C> {
191194
&self,
192195
msg: &Message,
193196
sig: &RecoverableSignature,
194-
) -> Result<key::PublicKey, Error> {
197+
) -> Result<key::PublicKey, SignatureError> {
195198
unsafe {
196199
let mut pk = super_ffi::PublicKey::new();
197200
if ffi::secp256k1_ecdsa_recover(
@@ -201,7 +204,7 @@ impl<C: Verification> Secp256k1<C> {
201204
msg.as_c_ptr(),
202205
) != 1
203206
{
204-
return Err(Error::InvalidSignature);
207+
return Err(SignatureError);
205208
}
206209
Ok(key::PublicKey::from(pk))
207210
}
@@ -214,9 +217,9 @@ mod tests {
214217
#[cfg(target_arch = "wasm32")]
215218
use wasm_bindgen_test::wasm_bindgen_test as test;
216219

217-
use super::{RecoverableSignature, RecoveryId};
220+
use super::*;
218221
use crate::constants::ONE;
219-
use crate::{Error, Message, Secp256k1, SecretKey};
222+
use crate::{Message, Secp256k1, SecretKey};
220223

221224
#[test]
222225
#[cfg(feature = "rand-std")]
@@ -316,7 +319,8 @@ mod tests {
316319

317320
let msg = crate::random_32_bytes(&mut rand::thread_rng());
318321
let msg = Message::from_slice(&msg).unwrap();
319-
assert_eq!(s.verify_ecdsa(&msg, &sig, &pk), Err(Error::IncorrectSignature));
322+
// TODO: Is this ok, or do we want IncorrectSignatureError as well?
323+
assert_eq!(s.verify_ecdsa(&msg, &sig, &pk), Err(SignatureError));
320324

321325
let recovered_key = s.recover_ecdsa(&msg, &sigr).unwrap();
322326
assert!(recovered_key != pk);
@@ -366,7 +370,7 @@ mod tests {
366370

367371
// Zero is not a valid sig
368372
let sig = RecoverableSignature::from_compact(&[0; 64], RecoveryId(0)).unwrap();
369-
assert_eq!(s.recover_ecdsa(&msg, &sig), Err(Error::InvalidSignature));
373+
assert_eq!(s.recover_ecdsa(&msg, &sig), Err(SignatureError));
370374
// ...but 111..111 is
371375
let sig = RecoverableSignature::from_compact(&[1; 64], RecoveryId(0)).unwrap();
372376
assert!(s.recover_ecdsa(&msg, &sig).is_ok());

0 commit comments

Comments
 (0)