Skip to main content

urma_runtime/
error.rs

1use std::fmt::{Display, Formatter};
2use urma_chain::validation::BlockValidationError;
3
4#[derive(Debug)]
5pub enum Error {
6    Identity(urma_identity::error::IdentityError),
7    Wallet(urma_wallet::wallet::WalletError),
8    Protocol(urma_core::error::Error),
9    Amount(bitcoin::amount::ParseAmountError),
10    Script(bitcoin::script::Error),
11    Invalid(String),
12    Unsupported(String),
13    Capacity(String),
14    Missing(String),
15    Context { message: String, cause: Box<Error> },
16    Io(std::io::Error),
17    Json(serde_json::Error),
18    Hex(hex::FromHexError),
19    Url(url::ParseError),
20    Integer(std::num::TryFromIntError),
21    IntegerText(std::num::ParseIntError),
22    Slice(std::array::TryFromSliceError),
23    Utf8(std::str::Utf8Error),
24    Text(std::string::FromUtf8Error),
25    Allocation(std::collections::TryReserveError),
26    Random(rand::Error),
27    Secp256k1(bitcoin::secp256k1::Error),
28    Transaction(bitcoin::consensus::encode::Error),
29    Push(bitcoin::script::PushBytesError),
30    TaprootBuilder(bitcoin::taproot::TaprootBuilderError),
31    Taproot(bitcoin::taproot::TaprootError),
32    TaprootSighash(bitcoin::sighash::TaprootError),
33    SegwitSighash(bitcoin::sighash::P2wpkhError),
34    AddressScript(bitcoin::address::FromScriptError),
35    AddressParse(bitcoin::address::ParseError),
36    AddressNetwork(bitcoin::address::NetworkValidationError),
37    PublicKey(bitcoin::key::FromSliceError),
38    UncompressedKey(bitcoin::key::UncompressedPublicKeyError),
39    Ecdsa(bitcoin::ecdsa::Error),
40    Hash(bitcoin::hex::HexToArrayError),
41    ProofOfWork(bitcoin::block::ValidationError),
42    Rpc(bitcoincore_rpc::Error),
43    Http(minreq::Error),
44    Persist(tempfile::PersistError),
45    Ip(std::net::AddrParseError),
46}
47impl Display for Error {
48    fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
49        match self {
50            Self::Identity(cause) => Display::fmt(cause, formatter),
51            Self::Wallet(cause) => Display::fmt(cause, formatter),
52            Self::Protocol(cause) => Display::fmt(cause, formatter),
53            Self::Amount(cause) => Display::fmt(cause, formatter),
54            Self::Script(cause) => Display::fmt(cause, formatter),
55            Self::Invalid(message)
56            | Self::Missing(message)
57            | Self::Unsupported(message)
58            | Self::Capacity(message) => formatter.write_str(message),
59            Self::Context { message, cause } => write!(formatter, "{message}: {cause}"),
60            Self::Io(cause) => Display::fmt(cause, formatter),
61            Self::Json(cause) => Display::fmt(cause, formatter),
62            Self::Hex(cause) => Display::fmt(cause, formatter),
63            Self::Url(cause) => Display::fmt(cause, formatter),
64            Self::Integer(cause) => Display::fmt(cause, formatter),
65            Self::IntegerText(cause) => Display::fmt(cause, formatter),
66            Self::Slice(cause) => Display::fmt(cause, formatter),
67            Self::Utf8(cause) => Display::fmt(cause, formatter),
68            Self::Text(cause) => Display::fmt(cause, formatter),
69            Self::Allocation(cause) => Display::fmt(cause, formatter),
70            Self::Random(cause) => Display::fmt(cause, formatter),
71            Self::Secp256k1(cause) => Display::fmt(cause, formatter),
72            Self::Transaction(cause) => Display::fmt(cause, formatter),
73            Self::Push(cause) => Display::fmt(cause, formatter),
74            Self::TaprootBuilder(cause) => Display::fmt(cause, formatter),
75            Self::Taproot(cause) => Display::fmt(cause, formatter),
76            Self::TaprootSighash(cause) => Display::fmt(cause, formatter),
77            Self::SegwitSighash(cause) => Display::fmt(cause, formatter),
78            Self::AddressScript(cause) => Display::fmt(cause, formatter),
79            Self::AddressParse(cause) => Display::fmt(cause, formatter),
80            Self::AddressNetwork(cause) => Display::fmt(cause, formatter),
81            Self::PublicKey(cause) => Display::fmt(cause, formatter),
82            Self::UncompressedKey(cause) => Display::fmt(cause, formatter),
83            Self::Ecdsa(cause) => Display::fmt(cause, formatter),
84            Self::Hash(cause) => Display::fmt(cause, formatter),
85            Self::ProofOfWork(cause) => Display::fmt(cause, formatter),
86            Self::Rpc(cause) => Display::fmt(cause, formatter),
87            Self::Http(cause) => Display::fmt(cause, formatter),
88            Self::Persist(cause) => Display::fmt(cause, formatter),
89            Self::Ip(cause) => Display::fmt(cause, formatter),
90        }
91    }
92}
93impl std::error::Error for Error {
94    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
95        match self {
96            Self::Identity(cause) => Some(cause),
97            Self::Wallet(cause) => Some(cause),
98            Self::Protocol(cause) => Some(cause),
99            Self::Amount(cause) => Some(cause),
100            Self::Script(cause) => Some(cause),
101            Self::Io(cause) => Some(cause),
102            Self::Json(cause) => Some(cause),
103            Self::Hex(cause) => Some(cause),
104            Self::Url(cause) => Some(cause),
105            Self::Integer(cause) => Some(cause),
106            Self::IntegerText(cause) => Some(cause),
107            Self::Slice(cause) => Some(cause),
108            Self::Utf8(cause) => Some(cause),
109            Self::Text(cause) => Some(cause),
110            Self::Allocation(cause) => Some(cause),
111            Self::Random(cause) => Some(cause),
112            Self::Secp256k1(cause) => Some(cause),
113            Self::Transaction(cause) => Some(cause),
114            Self::Push(cause) => Some(cause),
115            Self::TaprootBuilder(cause) => Some(cause),
116            Self::Taproot(cause) => Some(cause),
117            Self::TaprootSighash(cause) => Some(cause),
118            Self::SegwitSighash(cause) => Some(cause),
119            Self::AddressScript(cause) => Some(cause),
120            Self::AddressParse(cause) => Some(cause),
121            Self::AddressNetwork(cause) => Some(cause),
122            Self::PublicKey(cause) => Some(cause),
123            Self::UncompressedKey(cause) => Some(cause),
124            Self::Ecdsa(cause) => Some(cause),
125            Self::Hash(cause) => Some(cause),
126            Self::ProofOfWork(cause) => Some(cause),
127            Self::Rpc(cause) => Some(cause),
128            Self::Http(cause) => Some(cause),
129            Self::Persist(cause) => Some(cause),
130            Self::Ip(cause) => Some(cause),
131            Self::Context { cause, .. } => Some(cause.as_ref()),
132            _ => None,
133        }
134    }
135}
136impl From<std::io::Error> for Error {
137    fn from(error: std::io::Error) -> Self {
138        Self::Io(error)
139    }
140}
141impl From<serde_json::Error> for Error {
142    fn from(error: serde_json::Error) -> Self {
143        Self::Json(error)
144    }
145}
146impl From<hex::FromHexError> for Error {
147    fn from(error: hex::FromHexError) -> Self {
148        Self::Hex(error)
149    }
150}
151impl From<url::ParseError> for Error {
152    fn from(error: url::ParseError) -> Self {
153        Self::Url(error)
154    }
155}
156impl From<std::num::TryFromIntError> for Error {
157    fn from(error: std::num::TryFromIntError) -> Self {
158        Self::Integer(error)
159    }
160}
161impl From<std::num::ParseIntError> for Error {
162    fn from(error: std::num::ParseIntError) -> Self {
163        Self::IntegerText(error)
164    }
165}
166impl From<std::array::TryFromSliceError> for Error {
167    fn from(error: std::array::TryFromSliceError) -> Self {
168        Self::Slice(error)
169    }
170}
171impl From<std::str::Utf8Error> for Error {
172    fn from(error: std::str::Utf8Error) -> Self {
173        Self::Utf8(error)
174    }
175}
176impl From<std::string::FromUtf8Error> for Error {
177    fn from(error: std::string::FromUtf8Error) -> Self {
178        Self::Text(error)
179    }
180}
181impl From<std::collections::TryReserveError> for Error {
182    fn from(error: std::collections::TryReserveError) -> Self {
183        Self::Allocation(error)
184    }
185}
186impl From<rand::Error> for Error {
187    fn from(error: rand::Error) -> Self {
188        Self::Random(error)
189    }
190}
191impl From<bitcoin::secp256k1::Error> for Error {
192    fn from(error: bitcoin::secp256k1::Error) -> Self {
193        Self::Secp256k1(error)
194    }
195}
196impl From<bitcoin::consensus::encode::Error> for Error {
197    fn from(error: bitcoin::consensus::encode::Error) -> Self {
198        Self::Transaction(error)
199    }
200}
201impl From<bitcoin::script::PushBytesError> for Error {
202    fn from(error: bitcoin::script::PushBytesError) -> Self {
203        Self::Push(error)
204    }
205}
206impl From<bitcoin::taproot::TaprootBuilderError> for Error {
207    fn from(error: bitcoin::taproot::TaprootBuilderError) -> Self {
208        Self::TaprootBuilder(error)
209    }
210}
211impl From<bitcoin::taproot::TaprootError> for Error {
212    fn from(error: bitcoin::taproot::TaprootError) -> Self {
213        Self::Taproot(error)
214    }
215}
216impl From<bitcoin::sighash::TaprootError> for Error {
217    fn from(error: bitcoin::sighash::TaprootError) -> Self {
218        Self::TaprootSighash(error)
219    }
220}
221impl From<bitcoin::sighash::P2wpkhError> for Error {
222    fn from(error: bitcoin::sighash::P2wpkhError) -> Self {
223        Self::SegwitSighash(error)
224    }
225}
226impl From<bitcoin::address::FromScriptError> for Error {
227    fn from(error: bitcoin::address::FromScriptError) -> Self {
228        Self::AddressScript(error)
229    }
230}
231impl From<bitcoin::address::ParseError> for Error {
232    fn from(error: bitcoin::address::ParseError) -> Self {
233        Self::AddressParse(error)
234    }
235}
236impl From<bitcoin::address::NetworkValidationError> for Error {
237    fn from(error: bitcoin::address::NetworkValidationError) -> Self {
238        Self::AddressNetwork(error)
239    }
240}
241impl From<bitcoin::key::FromSliceError> for Error {
242    fn from(error: bitcoin::key::FromSliceError) -> Self {
243        Self::PublicKey(error)
244    }
245}
246impl From<bitcoin::key::UncompressedPublicKeyError> for Error {
247    fn from(error: bitcoin::key::UncompressedPublicKeyError) -> Self {
248        Self::UncompressedKey(error)
249    }
250}
251impl From<bitcoin::ecdsa::Error> for Error {
252    fn from(error: bitcoin::ecdsa::Error) -> Self {
253        Self::Ecdsa(error)
254    }
255}
256impl From<bitcoin::hex::HexToArrayError> for Error {
257    fn from(error: bitcoin::hex::HexToArrayError) -> Self {
258        Self::Hash(error)
259    }
260}
261impl From<bitcoin::block::ValidationError> for Error {
262    fn from(error: bitcoin::block::ValidationError) -> Self {
263        Self::ProofOfWork(error)
264    }
265}
266impl From<bitcoincore_rpc::Error> for Error {
267    fn from(error: bitcoincore_rpc::Error) -> Self {
268        Self::Rpc(error)
269    }
270}
271impl From<minreq::Error> for Error {
272    fn from(error: minreq::Error) -> Self {
273        Self::Http(error)
274    }
275}
276impl From<tempfile::PersistError> for Error {
277    fn from(error: tempfile::PersistError) -> Self {
278        Self::Persist(error)
279    }
280}
281impl From<std::net::AddrParseError> for Error {
282    fn from(error: std::net::AddrParseError) -> Self {
283        Self::Ip(error)
284    }
285}
286
287pub trait Context<T> {
288    fn context(self, message: impl Into<String>) -> Result<T, Error>;
289    fn with_context(self, message: impl FnOnce() -> String) -> Result<T, Error>;
290}
291impl<T> Context<T> for Option<T> {
292    fn context(self, message: impl Into<String>) -> Result<T, Error> {
293        match self {
294            Some(value) => Ok(value),
295            None => Err(Error::Missing(message.into())),
296        }
297    }
298    fn with_context(self, message: impl FnOnce() -> String) -> Result<T, Error> {
299        match self {
300            Some(value) => Ok(value),
301            None => Err(Error::Missing(message())),
302        }
303    }
304}
305impl<T, E: Into<Error>> Context<T> for Result<T, E> {
306    fn context(self, message: impl Into<String>) -> Result<T, Error> {
307        self.map_err(|cause| Error::Context {
308            message: message.into(),
309            cause: Box::new(cause.into()),
310        })
311    }
312    fn with_context(self, message: impl FnOnce() -> String) -> Result<T, Error> {
313        self.map_err(|cause| Error::Context {
314            message: message(),
315            cause: Box::new(cause.into()),
316        })
317    }
318}
319#[macro_export]
320macro_rules! ensure {
321    ($condition:expr, $($message:tt)*) => {
322        if !$condition { return Err($crate::error::Error::Invalid(format!($($message)*))); }
323    };
324}
325#[macro_export]
326macro_rules! bail {
327    ($($message:tt)*) => { return Err($crate::error::Error::Invalid(format!($($message)*))) };
328}
329pub use {bail, ensure};
330impl From<bitcoin::script::Error> for Error {
331    fn from(error: bitcoin::script::Error) -> Self {
332        Self::Script(error)
333    }
334}
335impl From<bitcoin::amount::ParseAmountError> for Error {
336    fn from(error: bitcoin::amount::ParseAmountError) -> Self {
337        Self::Amount(error)
338    }
339}
340
341impl From<urma_core::error::Error> for Error {
342    fn from(cause: urma_core::error::Error) -> Self {
343        Self::Protocol(cause)
344    }
345}
346
347impl From<urma_wallet::wallet::WalletError> for Error {
348    fn from(cause: urma_wallet::wallet::WalletError) -> Self {
349        Self::Wallet(cause)
350    }
351}
352
353impl From<urma_identity::error::IdentityError> for Error {
354    fn from(cause: urma_identity::error::IdentityError) -> Self {
355        Self::Identity(cause)
356    }
357}
358
359impl From<urma_io::Error> for Error {
360    fn from(cause: urma_io::Error) -> Self {
361        match cause {
362            urma_io::Error::Io(cause) => Self::Io(cause),
363            urma_io::Error::PrivatePermissions => {
364                Self::Invalid("file must have private permissions".into())
365            }
366            urma_io::Error::Integer(cause) => Self::Integer(cause),
367            urma_io::Error::LimitOverflow => Self::Missing("file read limit overflow".into()),
368            urma_io::Error::TooLarge { limit } => {
369                Self::Capacity(format!("file exceeds {limit} byte client capacity"))
370            }
371            urma_io::Error::Persist(cause) => Self::Persist(cause),
372            urma_io::Error::Context { message, cause } => Self::Context {
373                message,
374                cause: Box::new((*cause).into()),
375            },
376        }
377    }
378}
379
380impl From<BlockValidationError> for Error {
381    fn from(error: BlockValidationError) -> Self {
382        match error {
383            BlockValidationError::ProofOfWork(cause) => Self::Context {
384                message: "block proof of work".into(),
385                cause: Box::new(Self::ProofOfWork(cause)),
386            },
387            cause => Self::Invalid(cause.to_string()),
388        }
389    }
390}
391
392impl From<urma_chain::transaction::TransactionDecodeError> for Error {
393    fn from(cause: urma_chain::transaction::TransactionDecodeError) -> Self {
394        use urma_chain::transaction::TransactionDecodeError;
395        match cause {
396            TransactionDecodeError::LimitExceeded => Self::Invalid(cause.to_string()),
397            TransactionDecodeError::Hex(cause) => Self::Hex(cause),
398            TransactionDecodeError::Consensus(cause) => Self::Context {
399                message: "decode transaction".into(),
400                cause: Box::new(Self::Transaction(cause)),
401            },
402        }
403    }
404}