Skip to main content

tenzro_consensus/
error.rs

1//! Error types for the consensus module
2
3use thiserror::Error;
4use tenzro_types::primitives::BlockHeight;
5
6/// Result type for consensus operations
7pub type Result<T> = std::result::Result<T, ConsensusError>;
8
9/// Errors that can occur during consensus operations
10#[derive(Debug, Error)]
11pub enum ConsensusError {
12    /// Invalid block proposal
13    #[error("Invalid block proposal: {0}")]
14    InvalidProposal(String),
15
16    /// Invalid vote
17    #[error("Invalid vote: {0}")]
18    InvalidVote(String),
19
20    /// Insufficient votes for quorum
21    #[error("Insufficient votes for quorum: got {got}, need {need}")]
22    InsufficientVotes { got: u64, need: u64 },
23
24    /// Vote from non-validator
25    #[error("Vote from non-validator: {0}")]
26    NonValidator(String),
27
28    /// Block already exists
29    #[error("Block already exists at height {0}")]
30    DuplicateBlock(BlockHeight),
31
32    /// Block not found
33    #[error("Block not found at height {0}")]
34    BlockNotFound(BlockHeight),
35
36    /// Invalid block height
37    #[error("Invalid block height: expected {expected}, got {actual}")]
38    InvalidHeight {
39        expected: BlockHeight,
40        actual: BlockHeight,
41    },
42
43    /// Invalid block hash
44    #[error("Invalid block hash: expected {expected}, got {actual}")]
45    InvalidHash { expected: String, actual: String },
46
47    /// Invalid signature
48    #[error("Invalid signature: {0}")]
49    InvalidSignature(String),
50
51    /// View timeout
52    #[error("View {0} timed out")]
53    ViewTimeout(u64),
54
55    /// Not the leader for current view
56    #[error("Not the leader for view {0}")]
57    NotLeader(u64),
58
59    /// Already voted in this view
60    #[error("Already voted in view {0}")]
61    AlreadyVoted(u64),
62
63    /// Invalid validator set
64    #[error("Invalid validator set: {0}")]
65    InvalidValidatorSet(String),
66
67    /// Epoch transition error
68    #[error("Epoch transition error: {0}")]
69    EpochTransition(String),
70
71    /// Mempool error
72    #[error("Mempool error: {0}")]
73    Mempool(String),
74
75    /// Invalid TEE attestation
76    #[error("Invalid TEE attestation: {0}")]
77    InvalidAttestation(String),
78
79    /// Configuration error
80    #[error("Configuration error: {0}")]
81    Configuration(String),
82
83    /// Cryptographic error
84    #[error("Cryptographic error: {0}")]
85    Crypto(String),
86
87    /// Internal error
88    #[error("Internal error: {0}")]
89    Internal(String),
90
91    /// Not started
92    #[error("Consensus engine not started")]
93    NotStarted,
94
95    /// Already started
96    #[error("Consensus engine already started")]
97    AlreadyStarted,
98
99    /// Equivocation detected
100    #[error("Equivocation detected: validator {validator} voted for multiple blocks in view {view}")]
101    Equivocation { validator: String, view: u64 },
102
103    /// Per-DID admission lane bucket exhausted (Spec 2).
104    ///
105    /// `lane` carries the lane the controller was assigned to; `retry_after_ms`
106    /// is the controller's best-effort hint for when one bucket token will be
107    /// available; `current_rate` is the lane's per-second refill in tokens/sec.
108    #[error("Rate limit exceeded for lane {lane}: retry after {retry_after_ms}ms (rate {current_rate}/s)")]
109    RateLimited {
110        lane: &'static str,
111        retry_after_ms: u64,
112        burst_remaining: u32,
113        current_rate: f64,
114    },
115
116    /// Per-DID admission lane fee-floor not met (Spec 2).
117    ///
118    /// The lane multiplier is applied to the mempool's static minimum gas
119    /// price (`mempool_min_gas_price`) at admission time. Verified-lane
120    /// controllers pay `1.0×`, Delegated `1.5×`, Open `4.0×`. This makes
121    /// unverified controllers strictly more expensive per-tx so they can't
122    /// trivially crowd out verified traffic during congestion.
123    #[error(
124        "Fee floor not met for {lane} lane: gas_price {gas_price} < required {required} \
125         (base {base} × {multiplier:.2})"
126    )]
127    FeeFloorTooLow {
128        lane: &'static str,
129        gas_price: u64,
130        required: u64,
131        base: u64,
132        multiplier: f64,
133    },
134}
135
136impl From<tenzro_crypto::CryptoError> for ConsensusError {
137    fn from(err: tenzro_crypto::CryptoError) -> Self {
138        ConsensusError::Crypto(err.to_string())
139    }
140}
141
142impl From<tenzro_crypto::bls::BlsError> for ConsensusError {
143    fn from(err: tenzro_crypto::bls::BlsError) -> Self {
144        ConsensusError::Crypto(err.to_string())
145    }
146}