consensus_core/
error.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use 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/// Errors that can occur when processing blocks, reading from storage, or encountering shutdown.
17#[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    /// Returns the error name - only the enun name without any parameters - as a static string.
204    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    /// This test ensures that consensus errors when converted to a static string are the same as the enum name without
232    /// any parameterers included to the result string.
233    #[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}