ckb_sync/
status.rs

1use ckb_constant::sync::{BAD_MESSAGE_BAN_TIME, SYNC_USELESS_BAN_TIME};
2use ckb_types::core::error::ARGV_TOO_LONG_TEXT;
3use std::fmt::{self, Display, Formatter};
4use std::time::Duration;
5
6/// Similar to `?`, `attempt!` is used for propagating `Status`.
7///
8/// `attempt!` return early if it is not `Status::ok()`.
9///
10/// ```rust
11/// use ckb_sync::{Status, StatusCode, attempt};
12///
13/// fn return_early(status: Status) -> Status {
14///     attempt!(status);
15///     StatusCode::OK.with_context("bar")
16/// }
17///
18/// assert_eq!(return_early(StatusCode::OK.into()).to_string(), "OK(100): bar");
19/// assert_eq!(return_early(StatusCode::Ignored.into()).to_string(), "Ignored(101)");
20/// ```
21#[macro_export]
22macro_rules! attempt {
23    ($code:expr) => {{
24        let ret = $code;
25        if !ret.is_ok() {
26            return ret;
27        }
28        ret
29    }};
30}
31
32/// StatusCodes indicate whether a specific operation has been successfully completed.
33/// The StatusCode element is a 3-digit integer.
34///
35/// The first digest of the StatusCode defines the class of result:
36///   - 1xx: Informational - Request received, continuing process
37///   - 4xx: Malformed Error - The request contains malformed messages
38///   - 5xx: Warning - The node warns about recoverable conditions
39#[repr(u16)]
40#[derive(Clone, Copy, Debug, PartialEq, Eq)]
41pub enum StatusCode {
42    ///////////////////////////////////
43    //      Informational 1xx        //
44    ///////////////////////////////////
45    /// OK
46    OK = 100,
47    /// Ignored
48    Ignored = 101,
49    /// The node had already received and recorded this block as pending block
50    CompactBlockIsAlreadyPending = 102,
51    /// The node is requesting from other peers for this block, but no response yet
52    CompactBlockIsAlreadyInFlight = 103,
53    /// The node had already stored this block into database
54    CompactBlockAlreadyStored = 104,
55    /// The CompactBlock is older than what the node expects
56    CompactBlockIsStaled = 105,
57    /// The node cannot process the arrived CompactBlock successfully for lack
58    /// of information of its parent
59    CompactBlockRequiresParent = 106,
60    /// The node cannot process the arrived CompactBlock successfully for lack
61    /// of parts of its transactions
62    CompactBlockRequiresFreshTransactions = 107,
63    /// CompactBlock short-ids collision
64    CompactBlockMeetsShortIdsCollision = 108,
65    /// In-flight blocks limit exceeded
66    BlocksInFlightReachLimit = 109,
67    /// Generic rate limit error
68    TooManyRequests = 110,
69
70    ///////////////////////////////////
71    //      Malformed Errors 4xx     //
72    ///////////////////////////////////
73    /// Malformed protocol message
74    ProtocolMessageIsMalformed = 400,
75    /// Block verified failed or the block is already marked as invalid
76    BlockIsInvalid = 401,
77    /// Header verified failed or the header is already marked as invalid
78    CompactBlockHasInvalidHeader = 402,
79    /// Duplicated short-ids within a same CompactBlock
80    CompactBlockHasDuplicatedShortIds = 403,
81    /// Missing cellbase as the first transaction within a CompactBlock
82    CompactBlockHasNotPrefilledCellbase = 404,
83    /// Duplicated prefilled transactions within a same CompactBlock
84    CompactBlockHasDuplicatedPrefilledTransactions = 405,
85    /// The prefilled transactions are out-of-order
86    CompactBlockHasOutOfOrderPrefilledTransactions = 406,
87    /// Some of the prefilled transactions are out-of-index
88    CompactBlockHasOutOfIndexPrefilledTransactions = 407,
89    /// Invalid uncle block
90    CompactBlockHasInvalidUncle = 408,
91    /// Unmatched Transaction Root
92    CompactBlockHasUnmatchedTransactionRootWithReconstructedBlock = 409,
93    /// The length of BlockTransactions is unmatched with in pending_compact_blocks
94    BlockTransactionsLengthIsUnmatchedWithPendingCompactBlock = 410,
95    /// The short-ids of BlockTransactions is unmatched with in pending_compact_blocks
96    BlockTransactionsShortIdsAreUnmatchedWithPendingCompactBlock = 411,
97    /// The length of BlockUncles is unmatched with in pending_compact_blocks
98    BlockUnclesLengthIsUnmatchedWithPendingCompactBlock = 412,
99    /// The hash of uncles is unmatched
100    BlockUnclesAreUnmatchedWithPendingCompactBlock = 413,
101    /// Cannot locate the common blocks based on the GetHeaders
102    GetHeadersMissCommonAncestors = 414,
103    /// Headers verified failed
104    HeadersIsInvalid = 415,
105    /// Too many unknown transactions
106    TooManyUnknownTransactions = 416,
107    /// Request Genesis
108    RequestGenesis = 417,
109    /// Request Duplicate data
110    RequestDuplicate = 418,
111
112    ///////////////////////////////////
113    //      Warning 5xx              //
114    ///////////////////////////////////
115    /// Errors returned from the tx-pool
116    TxPool = 501,
117    /// Errors returned from the network layer
118    Network = 502,
119}
120
121impl StatusCode {
122    /// Code with context
123    pub fn with_context<S: ToString>(self, context: S) -> Status {
124        Status::new(self, Some(context))
125    }
126
127    /// StatusCode's name like `Ok(100)`
128    pub fn name(self) -> String {
129        format!("{:?}({})", self, self as u16)
130    }
131}
132
133/// Process message status
134#[derive(Clone, Debug, Eq)]
135pub struct Status {
136    code: StatusCode,
137    context: Option<String>,
138}
139
140impl Status {
141    /// New with code
142    pub fn new<S: ToString>(code: StatusCode, context: Option<S>) -> Self {
143        Self {
144            code,
145            context: context.map(|s| s.to_string()),
146        }
147    }
148
149    /// Ok status
150    pub fn ok() -> Self {
151        Self::new::<&str>(StatusCode::OK, None)
152    }
153
154    /// Ignored status
155    pub fn ignored() -> Self {
156        Self::new::<&str>(StatusCode::Ignored, None)
157    }
158
159    /// Whether ok
160    pub fn is_ok(&self) -> bool {
161        self.code == StatusCode::OK
162    }
163
164    /// Whether should ban session
165    pub fn should_ban(&self) -> Option<Duration> {
166        if !(400..500).contains(&(self.code as u16)) {
167            return None;
168        }
169        if let Some(context) = &self.context {
170            // TODO: it might be worthwhile to formalize all error texts
171            // that won't be banned.
172            if context.contains(ARGV_TOO_LONG_TEXT) {
173                return None;
174            }
175        }
176        match self.code {
177            StatusCode::GetHeadersMissCommonAncestors => Some(SYNC_USELESS_BAN_TIME),
178            _ => Some(BAD_MESSAGE_BAN_TIME),
179        }
180    }
181
182    /// Whether should output a warning log
183    pub fn should_warn(&self) -> bool {
184        self.code as u16 >= 500
185    }
186
187    /// Status code
188    pub fn code(&self) -> StatusCode {
189        self.code
190    }
191}
192
193impl PartialEq for Status {
194    fn eq(&self, other: &Self) -> bool {
195        self.code == other.code
196    }
197}
198
199impl Display for Status {
200    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
201        match self.context {
202            Some(ref context) => write!(f, "{:?}({}): {}", self.code, self.code as u16, context),
203            None => write!(f, "{:?}({})", self.code, self.code as u16),
204        }
205    }
206}
207
208impl From<StatusCode> for Status {
209    fn from(code: StatusCode) -> Self {
210        Self::new::<&str>(code, None)
211    }
212}