cometbft_light_client/
errors.rs

1//! Toplevel errors raised by the light client.
2
3use std::{fmt::Debug, time::Duration};
4
5use crossbeam_channel as crossbeam;
6use flex_error::{define_error, DisplayError, TraceError};
7
8// Re-export for backward compatibility
9pub use crate::verifier::errors::ErrorExt;
10use crate::{
11    components::io::IoError,
12    verifier::{
13        errors::VerificationErrorDetail,
14        operations::voting_power::VotingPowerTally,
15        options::Options,
16        types::{Hash, Height, LightBlock, PeerId, Status},
17    },
18};
19
20#[cfg(feature = "sled")]
21type SledError = TraceError<sled::Error>;
22
23#[cfg(not(feature = "sled"))]
24type SledError = flex_error::NoSource;
25
26define_error! {
27    #[derive(Debug)]
28    Error {
29        Io
30            [ IoError ]
31            | _ | { "io error" },
32
33        NoPrimary
34            | _ | { "no primary" },
35
36        NoWitnesses
37            | _ | { "no witnesses" },
38
39        NoWitnessesLeft
40            | _ | { "no witnesses left" },
41
42        ForkDetected
43            { peers: Vec<PeerId> }
44            | e | {
45                format_args!("fork detected peers={0:?}",
46                    e.peers)
47            },
48
49        NoInitialTrustedState
50            | _ | { "no initial trusted state" },
51
52        NoTrustedState
53            { status: Status }
54            | e | {
55                format_args!("no trusted state with status {:?}",
56                    e.status)
57            },
58
59        TargetLowerThanTrustedState
60            {
61                target_height: Height,
62                trusted_height: Height,
63            }
64            | e | {
65                format_args!("target height ({0}) is lower than trusted state ({1})",
66                    e.target_height, e.trusted_height)
67            },
68
69        HeightTooHigh
70            {
71                height: Height,
72                latest_height: Height,
73            }
74            |e| {
75                format_args!("height ({0}) is higher than latest height ({1})",
76                    e.height, e.latest_height)
77            },
78
79        TrustedStateOutsideTrustingPeriod
80            {
81                trusted_state: Box<LightBlock>,
82                options: Options,
83            }
84            | _ | {
85                format_args!("trusted state outside of trusting period")
86            },
87
88        BisectionFailed
89            {
90                target_height: Height,
91                trusted_height: Height
92            }
93            | e | {
94                format_args!("bisection for target at height {0} failed when reached trusted state at height {1}",
95                    e.target_height, e.trusted_height)
96            },
97
98        InvalidLightBlock
99            [ DisplayError<VerificationErrorDetail> ]
100            | _ | { "invalid light block" },
101
102        InvalidAdjacentHeaders
103            {
104                hash1: Hash,
105                hash2: Hash,
106            }
107            | e | {
108                format_args!("hash mismatch between two adjacent headers: {0} != {1}",
109                    e.hash1, e.hash2)
110            },
111
112        MissingLastBlockId
113            { height: Height }
114            | e | {
115                format_args!("missing last_block_id for header at height {0}",
116                    e.height)
117            },
118
119        ChannelDisconnected
120            | _ | { "internal channel disconnected" },
121
122        Sled
123            [ SledError ]
124            | _ | { "sled error" },
125
126        SerdeCbor
127            [ TraceError<serde_cbor::Error> ]
128            | _ | { "serde cbor error" },
129
130    }
131}
132
133impl ErrorExt for ErrorDetail {
134    fn not_enough_trust(&self) -> Option<VotingPowerTally> {
135        if let Self::InvalidLightBlock(e) = self {
136            e.source.not_enough_trust()
137        } else {
138            None
139        }
140    }
141
142    fn has_expired(&self) -> bool {
143        if let Self::InvalidLightBlock(e) = self {
144            e.source.has_expired()
145        } else {
146            false
147        }
148    }
149
150    /// Whether this error means that a timeout occurred when querying a node.
151    fn is_timeout(&self) -> Option<Duration> {
152        if let Self::Io(e) = self {
153            e.source.is_timeout()
154        } else {
155            None
156        }
157    }
158
159    fn is_io(&self) -> bool {
160        matches!(self, Self::Io(_))
161    }
162
163    fn is_height_too_high(&self) -> bool {
164        matches!(self, Self::HeightTooHigh { .. })
165    }
166}
167
168impl Error {
169    pub fn send<T>(_e: crossbeam::SendError<T>) -> Error {
170        Error::channel_disconnected()
171    }
172
173    pub fn recv(_e: crossbeam::RecvError) -> Error {
174        Error::channel_disconnected()
175    }
176}