1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//! Toplevel errors raised by the light client.

use std::fmt::Debug;

use anomaly::{BoxError, Context};
use crossbeam_channel as crossbeam;
use serde::{Deserialize, Serialize};
use thiserror::Error;

use crate::{
    components::io::IoError,
    light_client::Options,
    predicates::errors::VerificationError,
    types::{Height, LightBlock, PeerId, Status},
};

/// An error raised by this library
pub type Error = anomaly::Error<ErrorKind>;

/// The various error kinds raised by this library
#[derive(Debug, Clone, Error, PartialEq, Serialize, Deserialize)]
pub enum ErrorKind {
    /// I/O error
    #[error("I/O error: {0}")]
    Io(#[from] IoError),

    /// Store error
    #[error("store error")]
    Store,

    /// No primary
    #[error("no primary")]
    NoPrimary,

    /// No witnesses
    #[error("no witnesses")]
    NoWitnesses,

    /// No witness left
    #[error("no witness left")]
    NoWitnessLeft,

    /// A fork has been detected between some peers
    #[error("fork detected peers={0:?}")]
    ForkDetected(Vec<PeerId>),

    /// No initial trusted state
    #[error("no initial trusted state")]
    NoInitialTrustedState,

    /// No trusted state
    #[error("no trusted state")]
    NoTrustedState(Status),

    /// Target height for the light client lower than latest trusted state height
    #[error("target height ({target_height}) is lower than trusted state ({trusted_height})")]
    TargetLowerThanTrustedState {
        /// Target height
        target_height: Height,
        /// Latest trusted state height
        trusted_height: Height,
    },

    /// The trusted state is outside of the trusting period
    #[error("trusted state outside of trusting period")]
    TrustedStateOutsideTrustingPeriod {
        /// Trusted state
        trusted_state: Box<LightBlock>,
        /// Light client options
        options: Options,
    },

    /// Bisection failed when reached trusted state
    #[error("bisection for target at height {0} failed when reached trusted state at height {1}")]
    BisectionFailed(Height, Height),

    /// Verification failed for a light block
    #[error("invalid light block: {0}")]
    InvalidLightBlock(#[source] VerificationError),

    /// Internal channel disconnected
    #[error("internal channel disconnected")]
    ChannelDisconnected,
}

impl ErrorKind {
    /// Add additional context (i.e. include a source error and capture a backtrace).
    /// You can convert the resulting `Context` into an `Error` by calling `.into()`.
    pub fn context(self, source: impl Into<BoxError>) -> Context<Self> {
        Context::new(self, Some(source.into()))
    }
}

/// Extension methods for `ErrorKind`
pub trait ErrorExt {
    /// Whether this error means that the light block
    /// cannot be trusted w.r.t. the latest trusted state.
    fn not_enough_trust(&self) -> bool;

    /// Whether this error means that the light block has expired,
    /// ie. it's outside of the trusting period.
    fn has_expired(&self) -> bool;

    /// Whether this error means that a timeout occured when
    /// querying a node.
    fn is_timeout(&self) -> bool;
}

impl ErrorExt for ErrorKind {
    fn not_enough_trust(&self) -> bool {
        if let Self::InvalidLightBlock(e) = self {
            e.not_enough_trust()
        } else {
            false
        }
    }

    fn has_expired(&self) -> bool {
        if let Self::InvalidLightBlock(e) = self {
            e.has_expired()
        } else {
            false
        }
    }

    /// Whether this error means that a timeout occured when querying a node.
    fn is_timeout(&self) -> bool {
        if let Self::Io(e) = self {
            e.is_timeout()
        } else {
            false
        }
    }
}

impl<T: Debug + Send + Sync + 'static> From<crossbeam::SendError<T>> for ErrorKind {
    fn from(_err: crossbeam::SendError<T>) -> Self {
        Self::ChannelDisconnected
    }
}

impl From<crossbeam::RecvError> for ErrorKind {
    fn from(_err: crossbeam::RecvError) -> Self {
        Self::ChannelDisconnected
    }
}