1use consensus_config::{AuthorityIndex, Epoch, Stake};
5use consensus_types::block::{BlockRef, Round};
6use fastcrypto::error::FastCryptoError;
7use strum_macros::IntoStaticStr;
8use thiserror::Error;
9use typed_store::TypedStoreError;
10
11use crate::{
12 commit::{Commit, CommitIndex},
13 network::PeerId,
14};
15
16#[derive(Clone, Debug, Error, IntoStaticStr)]
18pub enum ConsensusError {
19 #[error("Error deserializing block: {0}")]
20 MalformedBlock(bcs::Error),
21
22 #[error("Unexpected block form on the wire")]
23 UnexpectedBlockForm,
24
25 #[error("Error decoding block envelope: {0}")]
26 MalformedBlockEnvelope(prost::DecodeError),
27
28 #[error("Error deserializing commit: {0}")]
29 MalformedCommit(bcs::Error),
30
31 #[error("Error serializing: {0}")]
32 SerializationFailure(bcs::Error),
33
34 #[error("Block contains a transaction that is too large: {size} > {limit}")]
35 TransactionTooLarge { size: usize, limit: usize },
36
37 #[error("Block contains too many transactions: {count} > {limit}")]
38 TooManyTransactions { count: usize, limit: usize },
39
40 #[error("Block contains too many transaction bytes: {size} > {limit}")]
41 TooManyTransactionBytes { size: usize, limit: usize },
42
43 #[error("Unexpected block authority {0} from peer {1}")]
44 UnexpectedAuthority(AuthorityIndex, AuthorityIndex),
45
46 #[error("Block has wrong epoch: expected {expected}, actual {actual}")]
47 WrongEpoch { expected: Epoch, actual: Epoch },
48
49 #[error("Genesis blocks should only be generated from Committee!")]
50 UnexpectedGenesisBlock,
51
52 #[error("Block version does not match the protocol config: {version}")]
53 UnexpectedBlockVersion { version: String },
54
55 #[error(
56 "Transaction vote cutoff round must be lower than the block round: cutoff {cutoff}, block {block}"
57 )]
58 InvalidTransactionVotesCutoff { cutoff: Round, block: Round },
59
60 #[error("Invalid transaction votes: {0}")]
61 InvalidTransactionVotes(String),
62
63 #[error("Genesis blocks should not be queried!")]
64 UnexpectedGenesisBlockRequested,
65
66 #[error("Expected {requested} but received {received} blocks returned from peer {peer}")]
67 UnexpectedNumberOfBlocksFetched {
68 peer: PeerId,
69 requested: usize,
70 received: usize,
71 },
72
73 #[error("Unexpected block returned while fetching missing blocks")]
74 UnexpectedFetchedBlock {
75 index: AuthorityIndex,
76 block_ref: BlockRef,
77 },
78
79 #[error(
80 "Unexpected block {block_ref} returned while fetching last own block from peer {index}"
81 )]
82 UnexpectedLastOwnBlock {
83 index: AuthorityIndex,
84 block_ref: BlockRef,
85 },
86
87 #[error(
88 "Too many blocks have been returned from authority {0} when requesting to fetch missing blocks"
89 )]
90 TooManyFetchedBlocksReturned(AuthorityIndex),
91
92 #[error("Too many authorities have been provided from authority {0}")]
93 TooManyAuthoritiesProvided(AuthorityIndex),
94
95 #[error(
96 "Provided size of highest accepted rounds parameter, {0}, is different than committee size, {1}"
97 )]
98 InvalidSizeOfHighestAcceptedRounds(usize, usize),
99
100 #[error("Invalid fetch blocks request: {0}")]
101 InvalidFetchBlocksRequest(String),
102
103 #[error("Invalid authority index at {loc}: {index} > {max}")]
104 InvalidAuthorityIndex {
105 loc: String,
106 index: AuthorityIndex,
107 max: usize,
108 },
109
110 #[error("Failed to deserialize signature: {0}")]
111 MalformedSignature(FastCryptoError),
112
113 #[error("Failed to verify the block's signature: {0}")]
114 SignatureVerificationFailure(FastCryptoError),
115
116 #[error("Synchronizer for fetching blocks directly from {0} is saturated")]
117 SynchronizerSaturated(String),
118
119 #[error("Peer {0} is unavailable")]
120 PeerUnavailable(String),
121
122 #[error("Peer not found for block synchronization: {0}")]
123 PeerNotFound(String),
124
125 #[error("Block {block_ref:?} rejected: {reason}")]
126 BlockRejected { block_ref: BlockRef, reason: String },
127
128 #[error(
129 "Ancestor is in wrong position: block {block_authority}, ancestor {ancestor_authority}, position {position}"
130 )]
131 InvalidAncestorPosition {
132 block_authority: AuthorityIndex,
133 ancestor_authority: AuthorityIndex,
134 position: usize,
135 },
136
137 #[error("Ancestor's round ({ancestor}) should be lower than the block's round ({block})")]
138 InvalidAncestorRound { ancestor: Round, block: Round },
139
140 #[error("Ancestor {0} not found among genesis blocks!")]
141 InvalidGenesisAncestor(BlockRef),
142
143 #[error("Too many ancestors in the block: {0} > {1}")]
144 TooManyAncestors(usize, usize),
145
146 #[error("Ancestors from the same authority {0}")]
147 DuplicatedAncestorsAuthority(AuthorityIndex),
148
149 #[error("Insufficient stake from parents: {parent_stakes} < {quorum}")]
150 InsufficientParentStakes { parent_stakes: Stake, quorum: Stake },
151
152 #[error("Invalid transaction: {0}")]
153 InvalidTransaction(String),
154
155 #[error("Received no commit from peer {peer}")]
156 NoCommitReceived { peer: PeerId },
157
158 #[error(
159 "Received unexpected start commit from peer {peer}: requested {start}, received {commit:?}"
160 )]
161 UnexpectedStartCommit {
162 peer: PeerId,
163 start: CommitIndex,
164 commit: Box<Commit>,
165 },
166
167 #[error(
168 "Received unexpected commit sequence from peer {peer}: {prev_commit:?}, {curr_commit:?}"
169 )]
170 UnexpectedCommitSequence {
171 peer: PeerId,
172 prev_commit: Box<Commit>,
173 curr_commit: Box<Commit>,
174 },
175
176 #[error("Not enough votes ({stake}) on end commit from peer {peer}: {commit:?}")]
177 NotEnoughCommitVotes {
178 stake: Stake,
179 peer: PeerId,
180 commit: Box<Commit>,
181 },
182
183 #[error("Received unexpected block from peer {peer}: {requested:?} vs {received:?}")]
184 UnexpectedBlockForCommit {
185 peer: PeerId,
186 requested: BlockRef,
187 received: BlockRef,
188 },
189
190 #[error(
191 "Unexpected certified commit index and last committed index. Expected next commit index to be {expected_commit_index}, but found {commit_index}"
192 )]
193 UnexpectedCertifiedCommitIndex {
194 expected_commit_index: CommitIndex,
195 commit_index: CommitIndex,
196 },
197
198 #[error("RocksDB failure: {0}")]
199 RocksDBFailure(#[from] TypedStoreError),
200
201 #[error("Unknown network peer: {0}")]
202 UnknownNetworkPeer(String),
203
204 #[error("Peer {0} is disconnected.")]
205 PeerDisconnected(String),
206
207 #[error("Network config error: {0:?}")]
208 NetworkConfig(String),
209
210 #[error("Failed to connect as client: {0:?}")]
211 NetworkClientConnection(String),
212
213 #[error("Failed to send request: {0:?}")]
214 NetworkRequest(String),
215
216 #[error("Request timeout: {0:?}")]
217 NetworkRequestTimeout(String),
218
219 #[error("Consensus has shut down!")]
220 Shutdown,
221}
222
223impl ConsensusError {
224 pub fn name(&self) -> &'static str {
226 self.into()
227 }
228}
229
230pub type ConsensusResult<T> = Result<T, ConsensusError>;
231
232#[macro_export]
233macro_rules! bail {
234 ($e:expr) => {
235 return Err($e);
236 };
237}
238
239#[macro_export(local_inner_macros)]
240macro_rules! ensure {
241 ($cond:expr, $e:expr) => {
242 if !($cond) {
243 bail!($e);
244 }
245 };
246}
247
248#[cfg(test)]
249mod test {
250 use super::*;
251
252 #[test]
255 fn test_error_name() {
256 {
257 let error = ConsensusError::InvalidAncestorRound {
258 ancestor: 10,
259 block: 11,
260 };
261 let error: &'static str = error.into();
262
263 assert_eq!(error, "InvalidAncestorRound");
264 }
265
266 {
267 let error = ConsensusError::InvalidAuthorityIndex {
268 loc: "test".to_string(),
269 index: AuthorityIndex::new_for_test(3),
270 max: 10,
271 };
272 assert_eq!(error.name(), "InvalidAuthorityIndex");
273 }
274
275 {
276 let error = ConsensusError::InsufficientParentStakes {
277 parent_stakes: 5,
278 quorum: 20,
279 };
280 assert_eq!(error.name(), "InsufficientParentStakes");
281 }
282 }
283}