Skip to content

Commit 0a8f8c8

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 ebfb3e2 commit 0a8f8c8

14 files changed

+928
-281
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 {

0 commit comments

Comments
 (0)