sidestr_header/error.rs
1//! The one error type, for decoding and proof-of-work checks.
2
3use crate::HeaderFamily;
4use core::fmt;
5
6/// Why a header could not be decoded or does not satisfy a rule.
7///
8/// Each variant names the upstream rule it stands in for, so a rejection can
9/// be traced to the reference: the codec's length check
10/// (`codec/codec.js` `decode`, "trailing bytes after BlockHeader"), the
11/// header-family selection on version bit 31 (`siding/lib/parents.mjs`,
12/// SPEC 3.2), Bitcoin Core's `arith_uint256::SetCompact` overflow and negative
13/// cases, and siding's `pow` rule (SPEC 4 step 1).
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15#[non_exhaustive]
16pub enum Error {
17 /// The byte slice is not the family's wire size (80 or 164).
18 WrongLength {
19 /// The family the bytes were decoded as.
20 family: HeaderFamily,
21 /// That family's wire size.
22 expected: usize,
23 /// The length actually supplied.
24 actual: usize,
25 },
26 /// A stock header's version has bit 31 set. Beside a stock parent the
27 /// producer keeps it clear (`siding/lib/block.mjs` `buildBlock`,
28 /// `test/stock-header-test.mjs` "version keeps bit 31 clear"); on a
29 /// BLAKE2b-family chain the same bit would select the v2 layout, so a
30 /// stock header carrying it is not a header of either family.
31 VersionBit31Set,
32 /// A v2 header's version has bit 31 clear: the bytes are not a v2 header
33 /// (`isHeaderV2` in `codec/pow/knots-header-v2.js`).
34 VersionBit31Clear,
35 /// A v2 header's `flags` has a reserved bit (6 or 7) set
36 /// (`knots:rule-header-flags-reserved`, error code `bad-flags-highbits`).
37 ReservedFlags(u8),
38 /// A hex string was not 64 hex characters.
39 InvalidHex,
40 /// Compact `bits` with the sign bit set and a non-zero mantissa
41 /// (Core's `fNegative`).
42 CompactNegative(u32),
43 /// Compact `bits` whose mantissa would not fit in 256 bits
44 /// (Core's `fOverflow`).
45 CompactOverflow(u32),
46 /// The header's `bits` is not the compact encoding of the chain's
47 /// `powLimit`. Siding sets every block's bits to that value
48 /// (`siding/lib/chain.mjs`: `compactFromTarget(powLimit)`) and the
49 /// kernel's difficulty rule holds it there (`powNoRetargeting`).
50 BitsNotPowLimit {
51 /// The header's `bits`.
52 bits: u32,
53 /// `powLimit` in compact form.
54 expected: u32,
55 },
56 /// The block hash, read as a 256-bit number, exceeds the target `bits`
57 /// encodes (`btc:rule-header-pow`; SPEC 4 step 1).
58 TargetNotMet,
59}
60
61impl fmt::Display for Error {
62 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63 match self {
64 Error::WrongLength {
65 family,
66 expected,
67 actual,
68 } => write!(f, "a {family} header is {expected} bytes, got {actual}"),
69 Error::VersionBit31Set => f.write_str("stock header version has bit 31 set"),
70 Error::VersionBit31Clear => f.write_str("v2 header version has bit 31 clear"),
71 Error::ReservedFlags(flags) => {
72 write!(
73 f,
74 "v2 header flags {flags:#04x} set a reserved bit (6 or 7)"
75 )
76 }
77 Error::InvalidHex => f.write_str("expected 64 hex characters"),
78 Error::CompactNegative(bits) => write!(f, "compact target {bits:#010x} is negative"),
79 Error::CompactOverflow(bits) => {
80 write!(f, "compact target {bits:#010x} overflows 256 bits")
81 }
82 Error::BitsNotPowLimit { bits, expected } => write!(
83 f,
84 "header bits {bits:#010x} are not the chain's powLimit {expected:#010x}"
85 ),
86 Error::TargetNotMet => f.write_str("block hash does not meet the target"),
87 }
88 }
89}
90
91#[cfg(feature = "std")]
92impl std::error::Error for Error {}