Skip to main content

dig_epoch/
error.rs

1//! # `error` — crate-wide error types
2//!
3//! **Introduced by:** `STR-002` — Module hierarchy (SPEC §13).
4//!
5//! **Future owners:** Phase 2 of `IMPLEMENTATION_ORDER.md`:
6//!
7//! - [`ERR-001`](../../docs/requirements/domains/error_types/specs/ERR-001.md)
8//!   — `EpochError` enum with all variants
9//! - [`ERR-002`](../../docs/requirements/domains/error_types/specs/ERR-002.md)
10//!   — `CheckpointCompetitionError` enum
11//! - [`ERR-003`](../../docs/requirements/domains/error_types/specs/ERR-003.md)
12//!   — `From` conversions and `Display` messages
13//!
14//! **Spec reference:**
15//! [`SPEC.md` §13](../../docs/resources/SPEC.md) — canonical module list;
16//! [`SPEC.md` §11](../../docs/resources/SPEC.md) — error taxonomy.
17//!
18//! ## Content rule
19//!
20//! Both error enums derive their surface from the `thiserror` crate
21//! (pinned in `Cargo.toml` by STR-001). Per SPEC §11, no error variant
22//! may leak internal details of the `parking_lot::RwLock` (lock poisoning
23//! does not apply — `parking_lot` does not poison), nor may any variant
24//! wrap a panic-producing path.
25//!
26//! ## Status at STR-002
27//!
28//! Empty aside from the [`STR_002_MODULE_PRESENT`] sentinel.
29
30/// Sentinel marker proving the module exists and is reachable at
31/// `dig_epoch::error::STR_002_MODULE_PRESENT`.
32///
33/// Exercised by the STR-002 integration test — see
34/// [`tests/crate_structure/str_002_test.rs`](../../tests/crate_structure/str_002_test.rs)
35/// (row 10, `test_error_module`).
36#[doc(hidden)]
37pub const STR_002_MODULE_PRESENT: () = ();
38
39use crate::types::epoch_phase::EpochPhase;
40
41// -----------------------------------------------------------------------------
42// ERR-002 — CheckpointCompetitionError
43// -----------------------------------------------------------------------------
44
45/// Errors within the checkpoint competition lifecycle.
46///
47/// Spec ref: SPEC §10.2 / ERR-002.
48#[derive(Debug, Clone, thiserror::Error)]
49pub enum CheckpointCompetitionError {
50    /// Checkpoint data failed validation.
51    #[error("Invalid checkpoint data: {0}")]
52    InvalidData(String),
53
54    /// No competition exists for the requested epoch.
55    #[error("Checkpoint competition not found for epoch {0}")]
56    NotFound(u64),
57
58    /// Submitted checkpoint's score does not exceed the current leader.
59    #[error("Score not higher: current {current}, submitted {submitted}")]
60    ScoreNotHigher {
61        /// Current leading score.
62        current: u64,
63        /// Score of the new submission.
64        submitted: u64,
65    },
66
67    /// Submission's epoch field doesn't match the competition's epoch.
68    #[error("Epoch mismatch: expected {expected}, got {got}")]
69    EpochMismatch {
70        /// Epoch the competition is running for.
71        expected: u64,
72        /// Epoch field in the submission.
73        got: u64,
74    },
75
76    /// Competition has already been finalized.
77    #[error("Competition already finalized")]
78    AlreadyFinalized,
79
80    /// Competition hasn't been started yet.
81    #[error("Competition not started")]
82    NotStarted,
83}
84
85// -----------------------------------------------------------------------------
86// ERR-001 — EpochError
87// -----------------------------------------------------------------------------
88
89/// Primary error type for the dig-epoch crate.
90///
91/// Spec ref: SPEC §10.1 / ERR-001.
92#[derive(Debug, Clone, thiserror::Error)]
93pub enum EpochError {
94    /// Attempted to advance an epoch that hasn't reached Complete phase.
95    #[error("Cannot advance: epoch {0} is not complete")]
96    EpochNotComplete(u64),
97
98    /// Attempted to advance an epoch with no finalized checkpoint.
99    #[error("Cannot advance: epoch {0} has no finalized checkpoint")]
100    NoFinalizedCheckpoint(u64),
101
102    /// Checkpoint-class block contains non-zero SpendBundles, cost, or fees.
103    #[error("Checkpoint block at height {0} is not empty: {1} bundles, {2} cost, {3} fees")]
104    CheckpointBlockNotEmpty(u64, u32, u64, u64),
105
106    /// Operation requires a specific phase but the epoch is in a different one.
107    #[error("Phase mismatch: expected {expected}, got {got}")]
108    PhaseMismatch {
109        /// Required phase.
110        expected: EpochPhase,
111        /// Actual current phase.
112        got: EpochPhase,
113    },
114
115    /// Submission or query references the wrong epoch.
116    #[error("Epoch mismatch: expected {expected}, got {got}")]
117    EpochMismatch {
118        /// Expected epoch number.
119        expected: u64,
120        /// Actual epoch number received.
121        got: u64,
122    },
123
124    /// L2 height is below genesis (height 0 or underflow).
125    #[error("Invalid height {0}: below genesis")]
126    InvalidHeight(u64),
127
128    /// DFSP operation attempted at a height before activation.
129    #[error("DFSP not active at height {0}")]
130    DfspNotActive(u64),
131
132    /// DFSP epoch-boundary processing error.
133    #[error("DFSP epoch-boundary error: {0}")]
134    DfspBoundary(String),
135
136    /// Checkpoint competition error (delegated via `#[from]`).
137    #[error("Competition error: {0}")]
138    Competition(#[from] CheckpointCompetitionError),
139
140    /// Deserialization failure (bincode, malformed input, etc.).
141    #[error("Invalid data: {0}")]
142    InvalidData(String),
143}