zebra_rpc/server/
error.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
//! RPC error codes & their handling.
use jsonrpsee_types::{ErrorCode, ErrorObject, ErrorObjectOwned};

/// Bitcoin RPC error codes
///
/// Drawn from <https://github.com/zcash/zcash/blob/99ad6fdc3a549ab510422820eea5e5ce9f60a5fd/src/rpc/protocol.h#L32-L80>.
///
/// ## Notes
///
/// - All explicit discriminants fit within `i64`.
#[derive(Default, Debug)]
pub enum LegacyCode {
    // General application defined errors
    /// `std::exception` thrown in command handling
    #[default]
    Misc = -1,
    /// Server is in safe mode, and command is not allowed in safe mode
    ForbiddenBySafeMode = -2,
    /// Unexpected type was passed as parameter
    Type = -3,
    /// Invalid address or key
    InvalidAddressOrKey = -5,
    /// Ran out of memory during operation
    OutOfMemory = -7,
    /// Invalid, missing or duplicate parameter
    InvalidParameter = -8,
    /// Database error
    Database = -20,
    /// Error parsing or validating structure in raw format
    Deserialization = -22,
    /// General error during transaction or block submission
    Verify = -25,
    /// Transaction or block was rejected by network rules
    VerifyRejected = -26,
    /// Transaction already in chain
    VerifyAlreadyInChain = -27,
    /// Client still warming up
    InWarmup = -28,

    // P2P client errors
    /// Bitcoin is not connected
    ClientNotConnected = -9,
    /// Still downloading initial blocks
    ClientInInitialDownload = -10,
    /// Node is already added
    ClientNodeAlreadyAdded = -23,
    /// Node has not been added before
    ClientNodeNotAdded = -24,
    /// Node to disconnect not found in connected nodes
    ClientNodeNotConnected = -29,
    /// Invalid IP/Subnet
    ClientInvalidIpOrSubnet = -30,
}

impl From<LegacyCode> for ErrorCode {
    fn from(code: LegacyCode) -> Self {
        Self::ServerError(code as i32)
    }
}

impl From<LegacyCode> for i32 {
    fn from(code: LegacyCode) -> Self {
        code as i32
    }
}

/// A trait for mapping errors to [`jsonrpsee_types::ErrorObjectOwned`].
pub(crate) trait MapError<T>: Sized {
    /// Maps errors to [`jsonrpsee_types::ErrorObjectOwned`] with a specific error code.
    fn map_error(self, code: impl Into<ErrorCode>) -> std::result::Result<T, ErrorObjectOwned>;

    /// Maps errors to [`jsonrpsee_types::ErrorObjectOwned`] with a prefixed message and a specific error code.
    #[cfg(feature = "getblocktemplate-rpcs")]
    fn map_error_with_prefix(
        self,
        code: impl Into<ErrorCode>,
        msg_prefix: impl ToString,
    ) -> Result<T, ErrorObjectOwned>;

    /// Maps errors to [`jsonrpsee_types::ErrorObjectOwned`] with a [`LegacyCode::Misc`] error code.
    fn map_misc_error(self) -> std::result::Result<T, ErrorObjectOwned> {
        self.map_error(LegacyCode::Misc)
    }
}

/// A trait for conditionally converting a value into a `Result<T, jsonrpc_core::Error>`.
pub(crate) trait OkOrError<T>: Sized {
    /// Converts the implementing type to `Result<T, jsonrpc_core::Error>`, using an error code and
    /// message if conversion is to `Err`.
    fn ok_or_error(
        self,
        code: impl Into<ErrorCode>,
        message: impl ToString,
    ) -> std::result::Result<T, ErrorObjectOwned>;

    /// Converts the implementing type to `Result<T, jsonrpc_core::Error>`, using a [`LegacyCode::Misc`] error code.
    fn ok_or_misc_error(self, message: impl ToString) -> std::result::Result<T, ErrorObjectOwned> {
        self.ok_or_error(LegacyCode::Misc, message)
    }
}

impl<T, E> MapError<T> for Result<T, E>
where
    E: ToString,
{
    fn map_error(self, code: impl Into<ErrorCode>) -> Result<T, ErrorObjectOwned> {
        self.map_err(|error| ErrorObject::owned(code.into().code(), error.to_string(), None::<()>))
    }

    #[cfg(feature = "getblocktemplate-rpcs")]
    fn map_error_with_prefix(
        self,
        code: impl Into<ErrorCode>,
        msg_prefix: impl ToString,
    ) -> Result<T, ErrorObjectOwned> {
        self.map_err(|error| {
            ErrorObject::owned(
                code.into().code(),
                format!("{}: {}", msg_prefix.to_string(), error.to_string()),
                None::<()>,
            )
        })
    }
}

impl<T> OkOrError<T> for Option<T> {
    fn ok_or_error(
        self,
        code: impl Into<ErrorCode>,
        message: impl ToString,
    ) -> Result<T, ErrorObjectOwned> {
        self.ok_or(ErrorObject::owned(
            code.into().code(),
            message.to_string(),
            None::<()>,
        ))
    }
}