consensus_core/
error.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use 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/// Errors that can occur when processing blocks, reading from storage, or
17/// encountering shutdown.
18#[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(
93        "Invalid ancestor authority index: block {block_authority}, ancestor {ancestor_index} > {max}"
94    )]
95    InvalidAncestorAuthorityIndex {
96        block_authority: AuthorityIndex,
97        ancestor_index: AuthorityIndex,
98        max: usize,
99    },
100
101    #[error("Failed to deserialize signature: {0}")]
102    MalformedSignature(FastCryptoError),
103
104    #[error("Failed to verify the block's signature: {0}")]
105    SignatureVerificationFailure(FastCryptoError),
106
107    #[error("Synchronizer for fetching blocks directly from ([{0}],{1}) is saturated")]
108    SynchronizerSaturated(AuthorityIndex, String),
109
110    #[error("Block {block_ref:?} rejected: {reason}")]
111    BlockRejected { block_ref: BlockRef, reason: String },
112
113    #[error(
114        "Ancestor is in wrong position: block {block_authority}, ancestor {ancestor_authority}, position {position}"
115    )]
116    InvalidAncestorPosition {
117        block_authority: AuthorityIndex,
118        ancestor_authority: AuthorityIndex,
119        position: usize,
120    },
121
122    #[error("Ancestor's round ({ancestor}) should be lower than the block's round ({block})")]
123    InvalidAncestorRound { ancestor: Round, block: Round },
124
125    #[error("Ancestor {0} not found among genesis blocks!")]
126    InvalidGenesisAncestor(BlockRef),
127
128    #[error("Too many ancestors in the block: {0} > {1}")]
129    TooManyAncestors(usize, usize),
130
131    #[error("Ancestors from the same authority {0}")]
132    DuplicatedAncestorsAuthority(AuthorityIndex),
133
134    #[error("Insufficient stake from parents: {parent_stakes} < {quorum}")]
135    InsufficientParentStakes { parent_stakes: Stake, quorum: Stake },
136
137    #[error("Invalid transaction: {0}")]
138    InvalidTransaction(String),
139
140    #[error("Ancestors max timestamp {max_timestamp_ms} > block timestamp {block_timestamp_ms}")]
141    InvalidBlockTimestamp {
142        max_timestamp_ms: u64,
143        block_timestamp_ms: u64,
144    },
145
146    #[error("Received no commit from peer {peer}")]
147    NoCommitReceived { peer: AuthorityIndex },
148
149    #[error(
150        "Received unexpected start commit from peer {peer}: requested {start}, received {commit:?}"
151    )]
152    UnexpectedStartCommit {
153        peer: AuthorityIndex,
154        start: CommitIndex,
155        commit: Box<Commit>,
156    },
157
158    #[error(
159        "Received unexpected commit sequence from peer {peer}: {prev_commit:?}, {curr_commit:?}"
160    )]
161    UnexpectedCommitSequence {
162        peer: AuthorityIndex,
163        prev_commit: Box<Commit>,
164        curr_commit: Box<Commit>,
165    },
166
167    #[error("Not enough votes ({stake}) on end commit from peer {peer}: {commit:?}")]
168    NotEnoughCommitVotes {
169        stake: Stake,
170        peer: AuthorityIndex,
171        commit: Box<Commit>,
172    },
173
174    #[error("Received unexpected block from peer {peer}: {requested:?} vs {received:?}")]
175    UnexpectedBlockForCommit {
176        peer: AuthorityIndex,
177        requested: BlockRef,
178        received: BlockRef,
179    },
180
181    #[error(
182        "Unexpected certified commit index and last committed index. Expected next commit index to be {expected_commit_index}, but found {commit_index}"
183    )]
184    UnexpectedCertifiedCommitIndex {
185        expected_commit_index: CommitIndex,
186        commit_index: CommitIndex,
187    },
188
189    #[error("RocksDB failure: {0}")]
190    RocksDBFailure(#[from] TypedStoreError),
191
192    #[error("Network config error: {0:?}")]
193    NetworkConfig(String),
194
195    #[error("Failed to connect as client: {0:?}")]
196    NetworkClientConnection(String),
197
198    #[error("Failed to send request: {0:?}")]
199    NetworkRequest(String),
200
201    #[error("Request timeout: {0:?}")]
202    NetworkRequestTimeout(String),
203
204    #[error("Consensus has shut down!")]
205    Shutdown,
206}
207
208impl ConsensusError {
209    /// Returns the error name - only the enun name without any parameters - as
210    /// a static string.
211    pub fn name(&self) -> &'static str {
212        self.into()
213    }
214}
215
216pub type ConsensusResult<T> = Result<T, ConsensusError>;
217
218#[macro_export]
219macro_rules! bail {
220    ($e:expr) => {
221        return Err($e);
222    };
223}
224
225#[macro_export(local_inner_macros)]
226macro_rules! ensure {
227    ($cond:expr, $e:expr) => {
228        if !($cond) {
229            bail!($e);
230        }
231    };
232}
233
234#[cfg(test)]
235mod test {
236    use super::*;
237    /// This test ensures that consensus errors when converted to a static
238    /// string are the same as the enum name without any parameterers
239    /// included to the result string.
240    #[test]
241    fn test_error_name() {
242        {
243            let error = ConsensusError::InvalidAncestorRound {
244                ancestor: 10,
245                block: 11,
246            };
247            let error: &'static str = error.into();
248            assert_eq!(error, "InvalidAncestorRound");
249        }
250        {
251            let error = ConsensusError::InvalidAuthorityIndex {
252                index: AuthorityIndex::new_for_test(3),
253                max: 10,
254            };
255            assert_eq!(error.name(), "InvalidAuthorityIndex");
256        }
257        {
258            let error = ConsensusError::InsufficientParentStakes {
259                parent_stakes: 5,
260                quorum: 20,
261            };
262            assert_eq!(error.name(), "InsufficientParentStakes");
263        }
264    }
265}