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}