Skip to main content

zebra_chain/serialization/
error.rs

1//! Errors for Zcash consensus-critical serialization.
2
3use std::{array::TryFromSliceError, io, num::TryFromIntError, str::Utf8Error, sync::Arc};
4
5use bounded_vec::BoundedVecOutOfBounds;
6use hex::FromHexError;
7use thiserror::Error;
8
9/// A serialization error.
10// TODO: refine error types -- better to use boxed errors?
11#[derive(Clone, Error, Debug)]
12pub enum SerializationError {
13    /// An io error that prevented deserialization
14    #[error("io error: {0}")]
15    Io(#[from] Arc<io::Error>),
16
17    /// The data to be deserialized was malformed.
18    // TODO: refine errors
19    #[error("parse error: {0}")]
20    Parse(&'static str),
21
22    /// A string was not UTF-8.
23    ///
24    /// Note: Rust `String` and `str` are always UTF-8.
25    #[error("string was not UTF-8: {0}")]
26    Utf8Error(#[from] Utf8Error),
27
28    /// A slice was an unexpected length during deserialization.
29    #[error("slice was the wrong length: {0}")]
30    TryFromSliceError(#[from] TryFromSliceError),
31
32    /// The length of a vec is too large to convert to a usize (and thus, too large to allocate on this platform)
33    #[error("CompactSize too large: {0}")]
34    TryFromIntError(#[from] TryFromIntError),
35
36    /// A string was not valid hexadecimal.
37    #[error("string was not hex: {0}")]
38    FromHexError(#[from] FromHexError),
39
40    /// An error caused when validating a zatoshi `Amount`
41    #[error("input couldn't be parsed as a zatoshi `Amount`: {source}")]
42    Amount {
43        /// The source error indicating how the num failed to validate
44        #[from]
45        source: crate::amount::Error,
46    },
47
48    /// Invalid transaction with a non-zero balance and no Sapling shielded spends or outputs.
49    ///
50    /// Transaction does not conform to the Sapling [consensus
51    /// rule](https://zips.z.cash/protocol/protocol.pdf#txnencodingandconsensus).
52    #[error("transaction balance is non-zero but doesn't have Sapling shielded spends or outputs")]
53    BadTransactionBalance,
54}
55
56impl From<SerializationError> for io::Error {
57    fn from(e: SerializationError) -> Self {
58        match e {
59            SerializationError::Io(e) => {
60                Arc::try_unwrap(e).unwrap_or_else(|e| io::Error::new(e.kind(), e.to_string()))
61            }
62            SerializationError::Parse(msg) => io::Error::new(io::ErrorKind::InvalidData, msg),
63            SerializationError::Utf8Error(e) => io::Error::new(io::ErrorKind::InvalidData, e),
64            SerializationError::TryFromSliceError(e) => {
65                io::Error::new(io::ErrorKind::InvalidData, e)
66            }
67            SerializationError::TryFromIntError(e) => io::Error::new(io::ErrorKind::InvalidData, e),
68            SerializationError::FromHexError(e) => io::Error::new(io::ErrorKind::InvalidData, e),
69            SerializationError::Amount { source } => {
70                io::Error::new(io::ErrorKind::InvalidData, source)
71            }
72            SerializationError::BadTransactionBalance => io::Error::new(
73                io::ErrorKind::InvalidData,
74                "bad transaction balance: non-zero with no Sapling shielded spends or outputs",
75            ),
76        }
77    }
78}
79
80impl From<crate::Error> for SerializationError {
81    fn from(e: crate::Error) -> Self {
82        match e {
83            crate::Error::InvalidConsensusBranchId => Self::Parse("invalid consensus branch id"),
84            crate::Error::Io(e) => Self::Io(e),
85            crate::Error::MissingNetworkUpgrade => Self::Parse("missing network upgrade"),
86            crate::Error::Amount(_) => Self::BadTransactionBalance,
87            crate::Error::Conversion(_) => {
88                Self::Parse("Zebra's type could not be converted to its librustzcash equivalent")
89            }
90        }
91    }
92}
93
94/// Allow converting `io::Error` to `SerializationError`; we need this since we
95/// use `Arc<io::Error>` in `SerializationError::Io`.
96impl From<io::Error> for SerializationError {
97    fn from(value: io::Error) -> Self {
98        Arc::new(value).into()
99    }
100}
101
102impl From<BoundedVecOutOfBounds> for SerializationError {
103    fn from(_: BoundedVecOutOfBounds) -> Self {
104        SerializationError::Parse("vector length out of bounds")
105    }
106}