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}