Skip to main content

silent_payments_core/
error.rs

1//! Per-domain error enums for Silent Payments operations.
2//!
3//! Three error types cover different failure domains:
4//! - [`AddressError`] -- SP address parsing and encoding failures
5//! - [`InputError`] -- transaction input classification failures
6//! - [`CryptoError`] -- cryptographic operation failures
7//!
8//! All enums implement [`std::error::Error`] via `thiserror` and provide
9//! wallet-developer-friendly error messages (not cryptographer jargon).
10
11use thiserror::Error;
12
13/// Errors from parsing or encoding BIP 352 Silent Payment addresses.
14#[derive(Debug, Error)]
15pub enum AddressError {
16    /// Bech32m decoding failure (checksum, character set, or length).
17    #[error("invalid bech32m encoding: {0}")]
18    InvalidBech32(String),
19
20    /// Address version is unsupported.
21    /// Version 31 is always rejected (backward-incompatible).
22    /// Versions 1-30 are forward-compatible (first 66 bytes are read).
23    #[error(
24        "unsupported SP address version {version}: \
25         versions 1-30 are forward-compatible, version 31 is rejected"
26    )]
27    UnsupportedVersion { version: u8 },
28
29    /// Payload length does not match the expected length for the given version.
30    #[error(
31        "wrong payload length for version {version}: \
32         expected {expected} bytes, got {actual}"
33    )]
34    WrongPayloadLength {
35        version: u8,
36        expected: usize,
37        actual: usize,
38    },
39
40    /// Human-readable prefix is not recognized (not sp/tsp/sprt).
41    #[error("unknown human-readable prefix: {hrp}")]
42    UnknownHrp { hrp: String },
43
44    /// A public key in the address payload is invalid.
45    #[error("invalid public key in address payload: {reason}")]
46    InvalidPublicKey { reason: String },
47}
48
49/// Errors from classifying transaction inputs and extracting public keys.
50#[derive(Debug, Clone, Error)]
51pub enum InputError {
52    /// Input type is not eligible for Silent Payments (SEC-03: explicit error, never filter).
53    #[error("ineligible input type: {script_type} ({reason})")]
54    IneligibleInput { script_type: String, reason: String },
55
56    /// Failed to extract a public key from an otherwise eligible input.
57    #[error("failed to extract public key from {input_type} input: {reason}")]
58    PubKeyExtraction { input_type: String, reason: String },
59
60    /// Transaction has zero eligible inputs for Silent Payments.
61    #[error("no eligible inputs found in transaction")]
62    NoEligibleInputs,
63}
64
65/// Errors from cryptographic operations (EC math, key validation, ECDH).
66#[derive(Debug, Error)]
67pub enum CryptoError {
68    /// A secp256k1 library operation failed.
69    /// Wraps the error as a string to avoid exposing the transitive type.
70    #[error("secp256k1 operation failed: {0}")]
71    Secp256k1(String),
72
73    /// ECDH or tweak computation produced a zero result
74    /// (hash output = 0 or >= curve order).
75    #[error(
76        "shared secret or tweak computation produced zero result \
77         (hash output = 0 or >= curve order)"
78    )]
79    ZeroTweakResult,
80
81    /// A point operation resulted in the point at infinity (identity element).
82    #[error("point operation resulted in the point at infinity")]
83    PointAtInfinity,
84
85    /// A public key is invalid.
86    #[error("invalid public key: {reason}")]
87    InvalidPublicKey { reason: String },
88
89    /// A secret key is invalid (zero or >= curve order).
90    #[error("invalid secret key: key is zero or >= curve order")]
91    InvalidSecretKey,
92}
93
94impl From<bitcoin::secp256k1::Error> for CryptoError {
95    fn from(err: bitcoin::secp256k1::Error) -> Self {
96        CryptoError::Secp256k1(err.to_string())
97    }
98}