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