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}