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}