Skip to main content

hopr_crypto_packet/
errors.rs

1use std::fmt::Debug;
2
3use hopr_types::{
4    crypto::errors::CryptoError,
5    internal::{
6        errors::CoreTypesError,
7        prelude::{ChannelId, Ticket, WinningProbability},
8    },
9    primitive::{
10        errors::GeneralError,
11        prelude::{Address, HoprBalance},
12    },
13};
14use thiserror::Error;
15
16#[derive(Error, Debug)]
17pub enum PacketError {
18    #[error("failed to decode packet: {0}")]
19    PacketDecodingError(String),
20
21    #[error("failed to construct packet: {0}")]
22    PacketConstructionError(String),
23
24    #[error("Proof of Relay challenge could not be verified")]
25    PoRVerificationError,
26
27    #[error("logic error during packet processing: {0}")]
28    LogicError(String),
29
30    #[error("underlying transport error while sending packet: {0}")]
31    TransportError(String),
32
33    #[error(transparent)]
34    CryptographicError(#[from] CryptoError),
35
36    #[error(transparent)]
37    CoreTypesError(#[from] CoreTypesError),
38
39    #[error(transparent)]
40    SphinxError(#[from] hopr_crypto_sphinx::errors::SphinxError),
41
42    #[error(transparent)]
43    Other(#[from] GeneralError),
44}
45
46pub type Result<T> = std::result::Result<T, PacketError>;
47
48/// Contains all possible validation errors that can occur during [ticket
49/// validation](crate::validation::validate_unacknowledged_ticket).
50#[derive(Debug, Clone, Copy, Error, strum::AsRefStr)]
51#[strum(serialize_all = "snake_case")]
52pub enum ValidationErrorKind {
53    /// Ticket signer does not match the sender.
54    #[error("ticket signer does not match the sender")]
55    InvalidSigner,
56    /// Ticket amount is lower than the given minimum value.
57    #[error("ticket amount is lower than {0}")]
58    LowValue(HoprBalance),
59    /// Ticket winning probability is lower than the given minimum value.
60    #[error("ticket winning probability is lower than {0}")]
61    LowWinProb(WinningProbability),
62    /// The given channel is closed or pending to close.
63    #[error("channel {0} is closed or pending to close")]
64    ChannelClosed(ChannelId),
65    /// Ticket epoch does not match the given channel epoch.
66    #[error("ticket epoch does not match channel epoch {0}")]
67    EpochMismatch(u32),
68    /// Ticket index is lower than the given channel ticket index.
69    #[error("ticket index is lower than channel index {0}")]
70    IndexTooLow(u64),
71    /// Not enough funds in the given channel to pay for the ticket.
72    #[error("ticket value is greater than remaining unrealized balance {1} in channel {0}")]
73    InsufficientFunds(ChannelId, HoprBalance),
74}
75
76/// Contains errors returned by [validate_unacknowledged_ticket](crate::validation::validate_unacknowledged_ticket).
77#[derive(Debug, Clone, Error)]
78#[error("validation error of {ticket}: {kind}")]
79pub struct TicketValidationError {
80    /// Error description.
81    pub kind: ValidationErrorKind,
82    /// Invalid ticket that failed to validate.
83    pub ticket: Box<Ticket>,
84    /// Issuer of the ticket.
85    ///
86    /// This value is present if at least the ticket signature validation succeeded.
87    pub issuer: Option<Address>,
88}
89
90#[cfg(test)]
91mod tests {
92    use hopr_types::{
93        crypto::prelude::{ChainKeypair, Keypair},
94        internal::prelude::TicketBuilder,
95    };
96
97    use super::*;
98
99    #[test]
100    fn test_validation_error_kind_display() {
101        let kind = ValidationErrorKind::LowWinProb(WinningProbability::ALWAYS);
102        assert_eq!(
103            kind.to_string(),
104            format!(
105                "ticket winning probability is lower than {}",
106                WinningProbability::ALWAYS
107            )
108        );
109        assert_eq!(kind.as_ref(), "low_win_prob");
110    }
111
112    #[test]
113    fn test_ticket_validation_error_display() {
114        let src = ChainKeypair::random();
115        let dst = ChainKeypair::random();
116
117        let ticket = TicketBuilder::zero_hop()
118            .counterparty(&dst)
119            .eth_challenge(Default::default())
120            .build_signed(&src, &Default::default())
121            .unwrap()
122            .leak();
123
124        let error = TicketValidationError {
125            kind: ValidationErrorKind::LowWinProb(WinningProbability::default()),
126            ticket: Box::new(ticket),
127            issuer: None,
128        };
129
130        assert_eq!(
131            error.to_string(),
132            format!(
133                "validation error of {ticket}: ticket winning probability is lower than {}",
134                WinningProbability::ALWAYS
135            )
136        );
137    }
138}