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