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