Skip to main content

uqoin_core/
error.rs

1//! The `error` module in the `uqoin-cor`e library defines a structured approach
2//! to error handling within the Uqoin cryptocurrency protocol. It introduces a 
3//! comprehensive enumeration of error kinds that represent various failure 
4//! scenarios encountered during protocol operations, such as coin validation, 
5//! transaction processing, and block verification. By encapsulating these error
6//! conditions, the module facilitates robust error management and propagation
7//! throughout the system.
8
9/// Represents specific categories of errors that can occur within the Uqoin 
10/// protocol:
11/// * CoinInvalid: Indicates that a coin fails validation checks.
12/// * CoinNotUnique: Denotes duplication of coin identifiers.
13/// * CoinTooCheap: Signifies that a coin's value is below the acceptable 
14/// threshold.
15/// * TransactionInvalidSender: The sender information in a transaction is 
16/// invalid or cannot be verified.
17/// * TransactionEmpty: The transaction contains no operations or data.
18/// * TransactionBrokenGroup: The transaction group structure is malformed or 
19/// inconsistent.
20/// * TransactionBrokenExt: Extension data is corrupted or invalid.
21/// * BlockBroken: The block structure is corrupted or fails integrity checks.
22/// * BlockOrderMismatch: The sequence of blocks does not follow the expected 
23/// order.
24/// * BlockValidatorMismatch: The block's validator does not match the expected
25/// validator.
26/// * BlockPreviousHashMismatch: The previous hash reference in the block does
27/// not match the actual previous block's hash.
28/// * BlockOffsetMismatch: The block's offset value is incorrect or
29/// inconsistent.
30/// * BlockInvalidHash: The block's hash does not meet the required criteria.
31/// * BlockInvalidHashComplexity: The block's hash does not satisfy the 
32/// complexity requirements.
33/// * Other: A catch-all for unspecified or miscellaneous errors.
34#[derive(Debug, Clone, PartialEq)]
35pub enum ErrorKind {
36    CoinInvalid,
37    CoinNotUnique,
38    CoinTooCheap,
39    TransactionInvalidSender,
40    TransactionEmpty,
41    TransactionBrokenGroup,
42    TransactionBrokenExt,
43    BlockBroken,
44    BlockOrderMismatch,
45    BlockValidatorMismatch,
46    BlockPreviousHashMismatch,
47    BlockOffsetMismatch,
48    BlockInvalidHash,
49    BlockInvalidHashComplexity,
50    BlockchainTruncate,
51    Other,
52}
53
54
55/// Shortcut for converting boolean check into error.
56/// A utility macro to streamline error checking:
57/// ```ignore
58/// validate!(condition, ErrorKindVariant)
59/// ```
60/// If condition evaluates to `true`, it returns `Ok(())`; otherwise, it returns
61/// an `Err` with the specified `ErrorKind`.
62#[macro_export]
63macro_rules! validate {
64    ($check:expr, $kind:ident) => (
65        if $check {
66            Ok::<(), crate::error::Error>(())
67        } else {
68            Err(crate::error::ErrorKind::$kind.into())
69        }
70    )
71}
72
73
74/// Uqoin error structure. It supports converting into `std::io::Error`.
75/// Encapsulates an error kind along with a descriptive message:
76/// * kind: An instance of ErrorKind representing the type of error.
77/// * message: A human-readable description of the error.
78/// Implements the `std::error::Error` and `std::fmt::Display` traits for 
79/// integration with Rust's error handling ecosystem.
80#[derive(Debug, Clone, PartialEq)]
81pub struct Error {
82    kind: ErrorKind,
83    message: String,
84}
85
86
87impl Error {
88    /// Create a new Uqoin error instance.
89    pub fn new(kind: ErrorKind, message: String) -> Self {
90        Self { kind, message }
91    }
92
93    /// Get kind of the error.
94    pub fn kind(&self) -> ErrorKind {
95        self.kind.clone()
96    }
97}
98
99
100impl std::error::Error for Error {}
101
102
103impl std::fmt::Display for Error {
104    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
105        write!(f, "{}", self.message)
106    }
107}
108
109
110impl From<ErrorKind> for Error {
111    fn from(uqoin_error_kind: ErrorKind) -> Error {
112        let message = format!("{:?}", uqoin_error_kind);
113        Error::new(uqoin_error_kind, message)
114    }
115}
116
117
118impl From<Error> for std::io::Error {
119    fn from(uqoin_error: Error) -> std::io::Error {
120        std::io::Error::new(std::io::ErrorKind::Other, uqoin_error.to_string())
121    }
122}
123
124
125impl From<ErrorKind> for std::io::Error {
126    fn from(uqoin_error_kind: ErrorKind) -> std::io::Error {
127        let uqoin_error = Error::from(uqoin_error_kind);
128        uqoin_error.into()
129    }
130}
131
132
133#[cfg(test)]
134mod tests {
135    use super::*;
136
137    #[test]
138    fn test_new() {
139        let err = Error::new(
140            ErrorKind::CoinInvalid,
141            "E764663DA70C4805F07F733C2A782116C7492C70EE67DD39C5DDA817816B8AB2"
142                .to_string()
143        );
144
145        assert_eq!(err.kind(), ErrorKind::CoinInvalid);
146        assert_eq!(
147            err.to_string(),
148            "E764663DA70C4805F07F733C2A782116C7492C70EE67DD39C5DDA817816B8AB2"
149        );
150    }
151
152    #[test]
153    fn test_err_to_std() {
154        let err = Error::new(
155            ErrorKind::CoinInvalid,
156            "E764663DA70C4805F07F733C2A782116C7492C70EE67DD39C5DDA817816B8AB2"
157                .to_string()
158        );
159
160        let err_std: std::io::Error = err.into();
161
162        assert_eq!(err_std.kind(), std::io::ErrorKind::Other);
163        assert_eq!(
164            err_std.to_string(), 
165            "E764663DA70C4805F07F733C2A782116C7492C70EE67DD39C5DDA817816B8AB2"
166        );
167    }
168
169    #[test]
170    fn test_kind_to_err() {
171        let kind = ErrorKind::CoinInvalid;
172        let err: Error = kind.into();
173        assert_eq!(err.kind(), ErrorKind::CoinInvalid);
174        assert_eq!(err.to_string(), "CoinInvalid");
175    }
176
177    #[test]
178    fn test_kind_to_err_str() {
179        let kind = ErrorKind::CoinInvalid;
180        let err_std: std::io::Error = kind.into();
181        assert_eq!(err_std.kind(), std::io::ErrorKind::Other);
182        assert_eq!(err_std.to_string(), "CoinInvalid");
183    }
184
185    #[test]
186    fn test_validate_macro() {
187        let result = validate!(true, CoinInvalid);
188        assert!(result.is_ok());
189
190        let result = validate!(false, CoinTooCheap);
191        assert!(result.is_err());
192        if let Err(e) = result {
193            assert_eq!(e.kind(), ErrorKind::CoinTooCheap);
194        }
195    }
196}