error.rs (3658B)
1 use std::{error::Error, fmt}; 2 3 /// Validation failures whose identity drives behavior outside the domain layer. 4 /// 5 /// Keep ordinary validation messages as `anyhow` errors until a caller needs to 6 /// branch on them; variants here are a control-flow contract, not an exhaustive 7 /// catalog of every validation failure. 8 #[derive(Debug, Clone, Copy, PartialEq, Eq)] 9 pub(crate) enum ValidationError { 10 BlockConflictsWithLocalChain { height: u64 }, 11 UnexpectedBlockHeight { expected: u64, actual: u64 }, 12 BlockDoesNotExtendLocalTip, 13 BlockBeforeFinalizerRankSlot { rank: u32, min_timestamp: u64 }, 14 BlockTimestampTooFarInFuture, 15 BurnBundleParentMismatch, 16 BurnAnchorOutsidePendingWindow, 17 MineAnchorNotOnChain, 18 MineAnchorLimitReached, 19 } 20 21 impl ValidationError { 22 pub(crate) fn requests_fork_recovery(self) -> bool { 23 matches!( 24 self, 25 Self::BlockConflictsWithLocalChain { .. } 26 | Self::UnexpectedBlockHeight { .. } 27 | Self::BlockDoesNotExtendLocalTip 28 ) 29 } 30 31 pub(crate) fn is_fork_relative(self) -> bool { 32 matches!( 33 self, 34 Self::BlockConflictsWithLocalChain { .. } 35 | Self::UnexpectedBlockHeight { .. } 36 | Self::BlockDoesNotExtendLocalTip 37 | Self::BurnBundleParentMismatch 38 | Self::BurnAnchorOutsidePendingWindow 39 | Self::MineAnchorNotOnChain 40 ) 41 } 42 43 pub(crate) fn is_temporal(self) -> bool { 44 matches!( 45 self, 46 Self::BlockBeforeFinalizerRankSlot { .. } | Self::BlockTimestampTooFarInFuture 47 ) 48 } 49 } 50 51 impl fmt::Display for ValidationError { 52 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { 53 match self { 54 Self::BlockConflictsWithLocalChain { height } => { 55 write!( 56 formatter, 57 "block at height {height} conflicts with local chain" 58 ) 59 } 60 Self::UnexpectedBlockHeight { expected, actual } => { 61 write!(formatter, "expected block height {expected}, got {actual}") 62 } 63 Self::BlockDoesNotExtendLocalTip => { 64 formatter.write_str("block does not extend local tip") 65 } 66 Self::BlockBeforeFinalizerRankSlot { 67 rank, 68 min_timestamp, 69 } => write!( 70 formatter, 71 "block timestamp is before finalizer rank {rank} time slot {min_timestamp}" 72 ), 73 Self::BlockTimestampTooFarInFuture => { 74 formatter.write_str("block timestamp is too far in the future") 75 } 76 Self::BurnBundleParentMismatch => { 77 formatter.write_str("burn bundle parent hash is invalid") 78 } 79 Self::BurnAnchorOutsidePendingWindow => formatter.write_str( 80 "burn transaction anchor is not valid for either of the next two block heights", 81 ), 82 Self::MineAnchorNotOnChain => { 83 formatter.write_str("mine transaction anchor is not on this chain") 84 } 85 Self::MineAnchorLimitReached => { 86 formatter.write_str("mine transaction anchor limit reached") 87 } 88 } 89 } 90 } 91 92 impl Error for ValidationError {} 93 94 pub(crate) fn error_has_validation( 95 error: &anyhow::Error, 96 predicate: impl Fn(ValidationError) -> bool, 97 ) -> bool { 98 error.chain().any(|cause| { 99 cause 100 .downcast_ref::<ValidationError>() 101 .copied() 102 .is_some_and(&predicate) 103 }) 104 }