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