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::commit::{Commit, CommitIndex};
12
13/// Errors that can occur when processing blocks, reading from storage, or encountering shutdown.
14#[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    /// Returns the error name - only the enun name without any parameters - as a static string.
197    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    /// This test ensures that consensus errors when converted to a static string are the same as the enum name without
225    /// any parameterers included to the result string.
226    #[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}