Skip to main content

hns_header_consensus/
lib.rs

1#![doc = "Canonical 236-byte Handshake headers and proof-of-work consensus."]
2
3use blake2::digest::{Update, VariableOutput};
4use blake2::{Blake2b512, Blake2bVar, Digest as BlakeDigest};
5use hns_encoding::{Decoder, Encoder};
6use hns_primitives::{
7    BlockHash, BlockTime, Chainwork, CompactTarget, Height, MerkleRoot, PowHash, PowMask,
8    ReservedRoot, ShareHash, TreeRoot, WitnessRoot,
9};
10use sha3::{Digest as ShaDigest, Sha3_256};
11use thiserror::Error;
12
13pub const HEADER_SIZE: usize = 236;
14pub const EXTRA_NONCE_SIZE: usize = 24;
15
16#[derive(Clone, Debug, Eq, PartialEq)]
17pub struct Header {
18    pub nonce: u32,
19    pub time: BlockTime,
20    pub previous_block: BlockHash,
21    pub tree_root: TreeRoot,
22    pub extra_nonce: [u8; EXTRA_NONCE_SIZE],
23    pub reserved_root: ReservedRoot,
24    pub witness_root: WitnessRoot,
25    pub merkle_root: MerkleRoot,
26    pub version: u32,
27    pub bits: CompactTarget,
28    pub mask: PowMask,
29}
30
31impl Default for Header {
32    fn default() -> Self {
33        Self {
34            nonce: 0,
35            time: BlockTime::new(0),
36            previous_block: BlockHash::default(),
37            tree_root: TreeRoot::default(),
38            extra_nonce: [0; EXTRA_NONCE_SIZE],
39            reserved_root: ReservedRoot::default(),
40            witness_root: WitnessRoot::default(),
41            merkle_root: MerkleRoot::default(),
42            version: 0,
43            bits: CompactTarget::new(0),
44            mask: PowMask::default(),
45        }
46    }
47}
48
49impl Header {
50    pub fn encode(&self) -> [u8; HEADER_SIZE] {
51        let mut encoder = Encoder::with_capacity(HEADER_SIZE);
52        encoder.put_u32_le(self.nonce);
53        encoder.put_u64_le(self.time.get());
54        encoder.put_bytes(self.previous_block.as_bytes());
55        encoder.put_bytes(self.tree_root.as_bytes());
56        encoder.put_bytes(&self.extra_nonce);
57        encoder.put_bytes(self.reserved_root.as_bytes());
58        encoder.put_bytes(self.witness_root.as_bytes());
59        encoder.put_bytes(self.merkle_root.as_bytes());
60        encoder.put_u32_le(self.version);
61        encoder.put_u32_le(self.bits.get());
62        encoder.put_bytes(self.mask.as_bytes());
63        encoder
64            .into_bytes()
65            .try_into()
66            .expect("header encoding is always 236 bytes")
67    }
68
69    pub fn decode(input: &[u8]) -> Result<Self, HeaderError> {
70        if input.len() != HEADER_SIZE {
71            return Err(HeaderError::InvalidLength {
72                actual: input.len(),
73            });
74        }
75        let mut decoder = Decoder::new(input);
76        let header = Self {
77            nonce: decoder.read_u32_le()?,
78            time: BlockTime::new(decoder.read_u64_le()?),
79            previous_block: BlockHash::new(decoder.read_array()?),
80            tree_root: TreeRoot::new(decoder.read_array()?),
81            extra_nonce: decoder.read_array()?,
82            reserved_root: ReservedRoot::new(decoder.read_array()?),
83            witness_root: WitnessRoot::new(decoder.read_array()?),
84            merkle_root: MerkleRoot::new(decoder.read_array()?),
85            version: decoder.read_u32_le()?,
86            bits: CompactTarget::new(decoder.read_u32_le()?),
87            mask: PowMask::new(decoder.read_array()?),
88        };
89        decoder.finish()?;
90        Ok(header)
91    }
92
93    pub fn block_hash(&self) -> BlockHash {
94        BlockHash::new(self.pow_hash().into_bytes())
95    }
96
97    pub fn subheader(&self) -> [u8; 128] {
98        let mut encoder = Encoder::with_capacity(128);
99        encoder.put_bytes(&self.extra_nonce);
100        encoder.put_bytes(self.reserved_root.as_bytes());
101        encoder.put_bytes(self.witness_root.as_bytes());
102        encoder.put_bytes(self.merkle_root.as_bytes());
103        encoder.put_u32_le(self.version);
104        encoder.put_u32_le(self.bits.get());
105        encoder
106            .into_bytes()
107            .try_into()
108            .expect("subheader is always 128 bytes")
109    }
110
111    pub fn sub_hash(&self) -> [u8; 32] {
112        blake2b_256(&[&self.subheader()])
113    }
114
115    pub fn mask_hash(&self) -> [u8; 32] {
116        blake2b_256(&[self.previous_block.as_bytes(), self.mask.as_bytes()])
117    }
118
119    pub fn commit_hash(&self) -> [u8; 32] {
120        blake2b_256(&[&self.sub_hash(), &self.mask_hash()])
121    }
122
123    pub fn preheader(&self) -> [u8; 128] {
124        let mut encoder = Encoder::with_capacity(128);
125        encoder.put_u32_le(self.nonce);
126        encoder.put_u64_le(self.time.get());
127        encoder.put_bytes(&self.padding::<20>());
128        encoder.put_bytes(self.previous_block.as_bytes());
129        encoder.put_bytes(self.tree_root.as_bytes());
130        encoder.put_bytes(&self.commit_hash());
131        encoder
132            .into_bytes()
133            .try_into()
134            .expect("preheader is always 128 bytes")
135    }
136
137    pub fn share_hash(&self) -> ShareHash {
138        let preheader = self.preheader();
139        let left = blake2b_512(&preheader);
140        let right = sha3_256(&[&preheader, &self.padding::<8>()]);
141        ShareHash::new(blake2b_256(&[&left, &self.padding::<32>(), &right]))
142    }
143
144    pub fn pow_hash(&self) -> PowHash {
145        let mut hash = self.share_hash().into_bytes();
146        for (byte, mask) in hash.iter_mut().zip(self.mask.as_bytes()) {
147            *byte ^= mask;
148        }
149        PowHash::new(hash)
150    }
151
152    pub fn verify_pow(&self) -> bool {
153        DecodedTarget::from_compact(self.bits).is_met_by(self.pow_hash().as_bytes())
154    }
155
156    fn padding<const LENGTH: usize>(&self) -> [u8; LENGTH] {
157        // This is consensus padding, not zero-fill: HSD derives every byte from
158        // the previous block and tree root before calculating the share hash.
159        let mut output = [0_u8; LENGTH];
160        for (index, byte) in output.iter_mut().enumerate() {
161            *byte =
162                self.previous_block.as_bytes()[index % 32] ^ self.tree_root.as_bytes()[index % 32];
163        }
164        output
165    }
166}
167
168#[derive(Clone, Copy, Debug, Eq, PartialEq)]
169pub struct DecodedTarget {
170    bytes: [u8; 32],
171    negative: bool,
172    overflow: bool,
173}
174
175impl DecodedTarget {
176    pub fn from_compact(bits: CompactTarget) -> Self {
177        let bits = bits.get();
178        if bits == 0 {
179            return Self {
180                bytes: [0; 32],
181                negative: false,
182                overflow: false,
183            };
184        }
185        let exponent = (bits >> 24) as usize;
186        let negative = bits & 0x0080_0000 != 0;
187        let mantissa = bits & 0x007f_ffff;
188        let mut bytes = [0_u8; 32];
189        let mut overflow = false;
190        if exponent <= 3 {
191            let value = mantissa >> (8 * (3 - exponent));
192            bytes[29..32].copy_from_slice(&value.to_be_bytes()[1..4]);
193        } else {
194            let mantissa_bytes = [
195                ((mantissa >> 16) & 0xff) as u8,
196                ((mantissa >> 8) & 0xff) as u8,
197                (mantissa & 0xff) as u8,
198            ];
199            for (offset, byte) in mantissa_bytes.into_iter().enumerate() {
200                let position = 32_isize - exponent as isize + offset as isize;
201                if !(0..32).contains(&position) {
202                    overflow |= byte != 0;
203                } else {
204                    bytes[position as usize] = byte;
205                }
206            }
207        }
208        Self {
209            bytes,
210            negative,
211            overflow,
212        }
213    }
214
215    pub const fn bytes(&self) -> &[u8; 32] {
216        &self.bytes
217    }
218
219    pub fn is_valid(&self) -> bool {
220        !self.negative && !self.overflow && self.bytes.iter().any(|byte| *byte != 0)
221    }
222
223    pub fn is_met_by(&self, hash: &[u8; 32]) -> bool {
224        self.is_valid() && hash <= &self.bytes
225    }
226
227    pub fn proof(&self) -> Option<Chainwork> {
228        if !self.is_valid() {
229            return None;
230        }
231        U256::from_be_bytes(self.bytes)
232            .work_for_target()
233            .map(|work| Chainwork::from_be_bytes(work.to_be_bytes()))
234    }
235
236    pub fn to_compact(self) -> CompactTarget {
237        let Some(first) = self.bytes.iter().position(|byte| *byte != 0) else {
238            return CompactTarget::new(0);
239        };
240        let mut exponent = 32 - first;
241        let mut mantissa = if exponent <= 3 {
242            let mut value = 0_u32;
243            for byte in &self.bytes[first..] {
244                value = (value << 8) | u32::from(*byte);
245            }
246            value << (8 * (3 - exponent))
247        } else {
248            (u32::from(self.bytes[first]) << 16)
249                | (u32::from(self.bytes[first + 1]) << 8)
250                | u32::from(self.bytes[first + 2])
251        };
252        if mantissa & 0x0080_0000 != 0 {
253            mantissa >>= 8;
254            exponent += 1;
255        }
256        CompactTarget::new(((exponent as u32) << 24) | mantissa)
257    }
258}
259
260#[derive(Clone, Copy, Debug, Eq, PartialEq)]
261pub enum Network {
262    Mainnet,
263    Testnet,
264    Regtest,
265    Simnet,
266}
267
268impl Network {
269    pub const fn id(self) -> u8 {
270        match self {
271            Self::Mainnet => 0,
272            Self::Testnet => 1,
273            Self::Regtest => 2,
274            Self::Simnet => 3,
275        }
276    }
277
278    pub const fn parameters(self) -> NetworkParameters {
279        match self {
280            Self::Mainnet => NetworkParameters {
281                network: self,
282                packet_magic: 0x5b6e_f2d3,
283                port: 12_038,
284                brontide_port: 44_806,
285                pow: PowParameters {
286                    limit: hex32(
287                        "0000000000ffff00000000000000000000000000000000000000000000000000",
288                    ),
289                    bits: CompactTarget::new(0x1c00_ffff),
290                    target_window: 144,
291                    target_spacing: 600,
292                    target_timespan: 86_400,
293                    minimum_actual_timespan: 21_600,
294                    maximum_actual_timespan: 345_600,
295                    target_reset: false,
296                    no_retargeting: false,
297                },
298                genesis_hash: BlockHash::new(hex32(
299                    "5b6ef2d3c1f3cdcadfd9a030ba1811efdd17740f14e166489760741d075992e0",
300                )),
301                genesis_time: BlockTime::new(1_580_745_078),
302            },
303            Self::Testnet => NetworkParameters {
304                network: self,
305                packet_magic: 0xb152_0dd2,
306                port: 13_038,
307                brontide_port: 45_806,
308                pow: PowParameters {
309                    limit: hex32(
310                        "00000000ffff0000000000000000000000000000000000000000000000000000",
311                    ),
312                    bits: CompactTarget::new(0x1d00_ffff),
313                    target_window: 144,
314                    target_spacing: 600,
315                    target_timespan: 86_400,
316                    minimum_actual_timespan: 21_600,
317                    maximum_actual_timespan: 345_600,
318                    target_reset: true,
319                    no_retargeting: false,
320                },
321                genesis_hash: BlockHash::new(hex32(
322                    "b1520dd24372f82ec94ebf8cf9d9b037d419c4aa3575d05dec70aedd1b427901",
323                )),
324                genesis_time: BlockTime::new(1_580_745_079),
325            },
326            Self::Regtest => NetworkParameters {
327                network: self,
328                packet_magic: 0xae38_95cf,
329                port: 14_038,
330                brontide_port: 46_806,
331                pow: PowParameters {
332                    limit: hex32(
333                        "7fffff0000000000000000000000000000000000000000000000000000000000",
334                    ),
335                    bits: CompactTarget::new(0x207f_ffff),
336                    target_window: 144,
337                    target_spacing: 600,
338                    target_timespan: 86_400,
339                    minimum_actual_timespan: 21_600,
340                    maximum_actual_timespan: 345_600,
341                    target_reset: true,
342                    no_retargeting: true,
343                },
344                genesis_hash: BlockHash::new(hex32(
345                    "ae3895cf597eff05b19e02a70ceeeecb9dc72dbfe6504a50e9343a72f06a87c5",
346                )),
347                genesis_time: BlockTime::new(1_580_745_080),
348            },
349            Self::Simnet => NetworkParameters {
350                network: self,
351                packet_magic: 0x0e64_8edc,
352                port: 15_038,
353                brontide_port: 47_806,
354                pow: PowParameters {
355                    limit: hex32(
356                        "7fffff0000000000000000000000000000000000000000000000000000000000",
357                    ),
358                    bits: CompactTarget::new(0x207f_ffff),
359                    target_window: 144,
360                    target_spacing: 600,
361                    target_timespan: 86_400,
362                    minimum_actual_timespan: 21_600,
363                    maximum_actual_timespan: 345_600,
364                    target_reset: false,
365                    no_retargeting: false,
366                },
367                genesis_hash: BlockHash::new(hex32(
368                    "0e648edc9cddb179014658061ea3f666a45cf44881877ae506e6babefbef6992",
369                )),
370                genesis_time: BlockTime::new(1_580_745_081),
371            },
372        }
373    }
374}
375
376#[derive(Clone, Copy, Debug, Eq, PartialEq)]
377pub struct PowParameters {
378    pub limit: [u8; 32],
379    pub bits: CompactTarget,
380    pub target_window: u32,
381    pub target_spacing: u32,
382    pub target_timespan: u32,
383    pub minimum_actual_timespan: u32,
384    pub maximum_actual_timespan: u32,
385    pub target_reset: bool,
386    pub no_retargeting: bool,
387}
388
389#[derive(Clone, Copy, Debug, Eq, PartialEq)]
390pub struct NetworkParameters {
391    pub network: Network,
392    pub packet_magic: u32,
393    pub port: u16,
394    pub brontide_port: u16,
395    pub pow: PowParameters,
396    pub genesis_hash: BlockHash,
397    pub genesis_time: BlockTime,
398}
399
400impl NetworkParameters {
401    pub const fn genesis_header(self) -> Header {
402        Header {
403            nonce: 0,
404            time: self.genesis_time,
405            previous_block: BlockHash::new([0; 32]),
406            tree_root: TreeRoot::new([0; 32]),
407            extra_nonce: [0; EXTRA_NONCE_SIZE],
408            reserved_root: ReservedRoot::new([0; 32]),
409            witness_root: WitnessRoot::new(hex32(
410                "1a2c60b9439206938f8d7823782abdb8b211a57431e9c9b6a6365d8d42893351",
411            )),
412            merkle_root: MerkleRoot::new(hex32(
413                "8e4c9756fef2ad10375f360e0560fcc7587eb5223ddf8cd7c7e06e60a1140b15",
414            )),
415            version: 0,
416            bits: self.pow.bits,
417            mask: PowMask::new([0; 32]),
418        }
419    }
420}
421
422#[derive(Clone, Copy, Debug, Eq, PartialEq)]
423pub struct DifficultyPoint {
424    pub height: Height,
425    pub time: BlockTime,
426    pub bits: CompactTarget,
427    pub chainwork: Chainwork,
428}
429
430pub fn expected_next_bits(
431    parameters: PowParameters,
432    next_time: BlockTime,
433    previous: DifficultyPoint,
434    first_suitable: Option<DifficultyPoint>,
435    last_suitable: Option<DifficultyPoint>,
436) -> Result<CompactTarget, HeaderError> {
437    if parameters.no_retargeting {
438        return Ok(parameters.bits);
439    }
440    if parameters.target_reset
441        && next_time.get()
442            > previous
443                .time
444                .get()
445                .saturating_add(u64::from(parameters.target_spacing) * 2)
446    {
447        return Ok(parameters.bits);
448    }
449    if previous.height.get() < parameters.target_window.saturating_add(2) {
450        if previous.bits != parameters.bits {
451            return Err(HeaderError::InvalidDifficulty);
452        }
453        return Ok(parameters.bits);
454    }
455    retarget_bits(
456        parameters,
457        first_suitable.ok_or(HeaderError::MissingDifficultyPoint)?,
458        last_suitable.ok_or(HeaderError::MissingDifficultyPoint)?,
459    )
460}
461
462pub fn retarget_bits(
463    parameters: PowParameters,
464    first: DifficultyPoint,
465    last: DifficultyPoint,
466) -> Result<CompactTarget, HeaderError> {
467    if last.height <= first.height {
468        return Err(HeaderError::InvalidDifficulty);
469    }
470    let work_delta = last
471        .chainwork
472        .checked_sub(first.chainwork)
473        .map_err(|_| HeaderError::InvalidDifficulty)?;
474    let scaled_work = work_delta
475        .checked_mul_u64(u64::from(parameters.target_spacing))
476        .map_err(|_| HeaderError::InvalidDifficulty)?;
477    let actual_timespan = last.time.get().saturating_sub(first.time.get()).clamp(
478        u64::from(parameters.minimum_actual_timespan),
479        u64::from(parameters.maximum_actual_timespan),
480    );
481    let work = scaled_work
482        .checked_div_u64(actual_timespan)
483        .map_err(|_| HeaderError::InvalidDifficulty)?;
484    if work == Chainwork::ZERO {
485        return Ok(parameters.bits);
486    }
487    let target = U256::from_be_bytes(work.to_be_bytes())
488        .target_for_work()
489        .ok_or(HeaderError::InvalidDifficulty)?;
490    if target > U256::from_be_bytes(parameters.limit) {
491        return Ok(parameters.bits);
492    }
493    Ok(DecodedTarget {
494        bytes: target.to_be_bytes(),
495        negative: false,
496        overflow: false,
497    }
498    .to_compact())
499}
500
501#[derive(Clone, Copy, Debug, Eq, PartialEq)]
502pub struct HeaderValidationContext {
503    pub height: Height,
504    pub previous_block: BlockHash,
505    pub median_time: BlockTime,
506    pub now: BlockTime,
507    pub expected_bits: CompactTarget,
508}
509
510pub fn validate_header(
511    parameters: NetworkParameters,
512    header: &Header,
513    context: HeaderValidationContext,
514) -> Result<Chainwork, HeaderError> {
515    let is_genesis = context.height.get() == 0;
516    if is_genesis {
517        if header != &parameters.genesis_header() || header.block_hash() != parameters.genesis_hash
518        {
519            return Err(HeaderError::WrongGenesis);
520        }
521    } else if header.previous_block != context.previous_block {
522        return Err(HeaderError::WrongPreviousBlock);
523    }
524    if header.time <= context.median_time
525        || header.time.get() > context.now.get().saturating_add(7200)
526    {
527        return Err(HeaderError::InvalidTime);
528    }
529    if header.bits != context.expected_bits {
530        return Err(HeaderError::InvalidDifficulty);
531    }
532    let target = DecodedTarget::from_compact(header.bits);
533    if !is_genesis && !target.is_met_by(header.pow_hash().as_bytes()) {
534        return Err(HeaderError::InvalidProofOfWork);
535    }
536    target.proof().ok_or(HeaderError::InvalidProofOfWork)
537}
538
539#[derive(Debug, Error)]
540pub enum HeaderError {
541    #[error(transparent)]
542    Decode(#[from] hns_encoding::DecodeError),
543    #[error("Handshake header must be exactly 236 bytes, got {actual}")]
544    InvalidLength { actual: usize },
545    #[error("header does not match the selected network genesis")]
546    WrongGenesis,
547    #[error("header does not connect to the expected previous block")]
548    WrongPreviousBlock,
549    #[error("invalid header time")]
550    InvalidTime,
551    #[error("invalid header difficulty")]
552    InvalidDifficulty,
553    #[error("missing suitable difficulty point")]
554    MissingDifficultyPoint,
555    #[error("header proof of work does not meet target")]
556    InvalidProofOfWork,
557}
558
559#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
560struct U256([u8; 32]);
561
562impl U256 {
563    const ZERO: Self = Self([0; 32]);
564    const ONE: Self = Self::from_u64(1);
565
566    const fn from_be_bytes(bytes: [u8; 32]) -> Self {
567        Self(bytes)
568    }
569
570    const fn to_be_bytes(self) -> [u8; 32] {
571        self.0
572    }
573
574    const fn from_u64(value: u64) -> Self {
575        let mut bytes = [0_u8; 32];
576        let value = value.to_be_bytes();
577        let mut index = 0;
578        while index < 8 {
579            bytes[24 + index] = value[index];
580            index += 1;
581        }
582        Self(bytes)
583    }
584
585    fn checked_add(self, other: Self) -> Option<Self> {
586        let mut output = [0_u8; 32];
587        let mut carry = 0_u16;
588        for index in (0..32).rev() {
589            let sum = u16::from(self.0[index]) + u16::from(other.0[index]) + carry;
590            output[index] = sum as u8;
591            carry = sum >> 8;
592        }
593        (carry == 0).then_some(Self(output))
594    }
595
596    fn work_for_target(self) -> Option<Self> {
597        if self == Self::ZERO {
598            return None;
599        }
600        let Some(divisor) = self.checked_add(Self::ONE) else {
601            return Some(Self::ONE);
602        };
603        Self::divide_two_to_256(divisor)
604    }
605
606    fn target_for_work(self) -> Option<Self> {
607        if self == Self::ZERO {
608            return None;
609        }
610        if self == Self::ONE {
611            return Some(Self([0xff; 32]));
612        }
613        Self::divide_two_to_256(self)?.checked_sub(Self::ONE)
614    }
615
616    fn checked_sub(self, other: Self) -> Option<Self> {
617        (self >= other).then(|| self.wrapping_sub(other))
618    }
619
620    fn shift_left_one(&mut self) -> bool {
621        let high = self.0[0] & 0x80 != 0;
622        let mut carry = 0_u8;
623        for byte in self.0.iter_mut().rev() {
624            let next_carry = *byte >> 7;
625            *byte = (*byte << 1) | carry;
626            carry = next_carry;
627        }
628        high
629    }
630
631    fn wrapping_sub(self, other: Self) -> Self {
632        let mut output = [0_u8; 32];
633        let mut borrow = 0_i16;
634        for index in (0..32).rev() {
635            let difference = i16::from(self.0[index]) - i16::from(other.0[index]) - borrow;
636            if difference < 0 {
637                output[index] = (difference + 256) as u8;
638                borrow = 1;
639            } else {
640                output[index] = difference as u8;
641                borrow = 0;
642            }
643        }
644        Self(output)
645    }
646
647    fn set_bit(&mut self, bit: usize) {
648        self.0[31 - bit / 8] |= 1 << (bit % 8);
649    }
650
651    fn divide_two_to_256(divisor: Self) -> Option<Self> {
652        if divisor <= Self::ONE {
653            return None;
654        }
655        let mut remainder = Self::ZERO;
656        let mut quotient = Self::ZERO;
657        for bit in (0..=256).rev() {
658            let high = remainder.shift_left_one();
659            if bit == 256 {
660                remainder.0[31] |= 1;
661            }
662            if high || remainder >= divisor {
663                remainder = remainder.wrapping_sub(divisor);
664                if bit < 256 {
665                    quotient.set_bit(bit);
666                }
667            }
668        }
669        Some(quotient)
670    }
671}
672
673fn blake2b_256(parts: &[&[u8]]) -> [u8; 32] {
674    let mut hasher = Blake2bVar::new(32).expect("valid BLAKE2b output length");
675    for part in parts {
676        Update::update(&mut hasher, part);
677    }
678    let mut output = [0_u8; 32];
679    hasher
680        .finalize_variable(&mut output)
681        .expect("valid BLAKE2b output buffer");
682    output
683}
684
685fn blake2b_512(input: &[u8]) -> [u8; 64] {
686    let mut hasher = Blake2b512::new();
687    BlakeDigest::update(&mut hasher, input);
688    hasher.finalize().into()
689}
690
691fn sha3_256(parts: &[&[u8]]) -> [u8; 32] {
692    let mut hasher = Sha3_256::new();
693    for part in parts {
694        ShaDigest::update(&mut hasher, part);
695    }
696    hasher.finalize().into()
697}
698
699const fn hex32(value: &str) -> [u8; 32] {
700    let bytes = value.as_bytes();
701    assert!(bytes.len() == 64);
702    let mut output = [0_u8; 32];
703    let mut index = 0;
704    while index < 32 {
705        output[index] = (hex_nibble(bytes[index * 2]) << 4) | hex_nibble(bytes[index * 2 + 1]);
706        index += 1;
707    }
708    output
709}
710
711const fn hex_nibble(value: u8) -> u8 {
712    match value {
713        b'0'..=b'9' => value - b'0',
714        b'a'..=b'f' => value - b'a' + 10,
715        b'A'..=b'F' => value - b'A' + 10,
716        _ => panic!("invalid hexadecimal constant"),
717    }
718}
719
720#[cfg(test)]
721mod tests {
722    use super::*;
723
724    #[test]
725    fn header_round_trips_in_exact_hsd_order() {
726        let raw = (0..HEADER_SIZE)
727            .map(|index| index as u8)
728            .collect::<Vec<_>>();
729        let header = Header::decode(&raw).expect("valid");
730        assert_eq!(header.nonce, 0x0302_0100);
731        assert_eq!(header.time.get(), 0x0b0a_0908_0706_0504);
732        assert_eq!(header.encode().as_slice(), raw);
733    }
734
735    #[test]
736    fn non_genesis_share_hash_matches_hsd_xor_padding() {
737        let mut raw = (0..HEADER_SIZE)
738            .map(|index| index as u8)
739            .collect::<Vec<_>>();
740        raw[4..12].copy_from_slice(&1_580_745_079_u64.to_le_bytes());
741        let header = Header::decode(&raw).expect("valid");
742
743        assert_ne!(header.previous_block, BlockHash::default());
744        assert_eq!(header.padding::<20>(), [0x20; 20]);
745
746        // Oracle generated with Headers.fromHead(raw) and shareHash() from
747        // HSD 698e252ebc7b5c1dd0a9587e342fdd153d020ae4.
748        assert_eq!(
749            header.share_hash().as_bytes(),
750            &hex32("a4338cfe232773c176979d2440c325035ce0c8711afce02bf7442d7f54b3398e")
751        );
752    }
753
754    #[test]
755    fn network_genesis_hashes_match_hsd() {
756        for network in [
757            Network::Mainnet,
758            Network::Testnet,
759            Network::Regtest,
760            Network::Simnet,
761        ] {
762            let parameters = network.parameters();
763            let genesis = parameters.genesis_header();
764            assert_eq!(genesis.block_hash(), parameters.genesis_hash);
765            assert_eq!(
766                validate_header(
767                    parameters,
768                    &genesis,
769                    HeaderValidationContext {
770                        height: Height::new(0),
771                        previous_block: BlockHash::default(),
772                        median_time: BlockTime::new(0),
773                        now: genesis.time,
774                        expected_bits: genesis.bits,
775                    },
776                )
777                .expect("valid genesis"),
778                DecodedTarget::from_compact(genesis.bits)
779                    .proof()
780                    .expect("work")
781            );
782        }
783    }
784
785    #[test]
786    fn compact_targets_and_chainwork_match_hsd_boundaries() {
787        for bits in [
788            0x0112_0000,
789            0x0201_2300,
790            0x0312_3456,
791            0x0412_3456,
792            0x1d00_ffff,
793            0x207f_ffff,
794        ] {
795            let compact = CompactTarget::new(bits);
796            let target = DecodedTarget::from_compact(compact);
797            assert!(target.is_valid());
798            assert_eq!(target.to_compact(), compact);
799        }
800        assert_eq!(
801            DecodedTarget::from_compact(CompactTarget::new(0x207f_ffff))
802                .proof()
803                .expect("proof")
804                .to_be_bytes(),
805            Chainwork::from_limbs_le([2, 0, 0, 0]).to_be_bytes()
806        );
807    }
808
809    #[test]
810    fn retarget_matches_pinned_hsd_half_timespan_vector() {
811        let pow = Network::Mainnet.parameters().pow;
812        let first = DifficultyPoint {
813            height: Height::new(1000),
814            time: BlockTime::new(1_000_000),
815            bits: pow.bits,
816            chainwork: Chainwork::from_be_bytes(hex32(
817                "0000000000000000000000000000000000000000000000000123456789abcdef",
818            )),
819        };
820        let last = DifficultyPoint {
821            height: Height::new(first.height.get() + pow.target_window),
822            time: BlockTime::new(first.time.get() + u64::from(pow.target_timespan / 2)),
823            bits: pow.bits,
824            chainwork: Chainwork::from_be_bytes(hex32(
825                "0000000000000000000000000000000000000000000000000123d56819ac5def",
826            )),
827        };
828        assert_eq!(
829            retarget_bits(pow, first, last).expect("retarget"),
830            CompactTarget::new(0x1b7f_ff80)
831        );
832    }
833
834    #[test]
835    fn validation_rejects_wrong_network_genesis() {
836        let mainnet = Network::Mainnet.parameters();
837        let testnet = Network::Testnet.parameters();
838        let header = testnet.genesis_header();
839        let context = HeaderValidationContext {
840            height: Height::new(0),
841            previous_block: BlockHash::default(),
842            median_time: BlockTime::new(0),
843            now: BlockTime::new(header.time.get()),
844            expected_bits: header.bits,
845        };
846        assert!(matches!(
847            validate_header(mainnet, &header, context),
848            Err(HeaderError::WrongGenesis)
849        ));
850    }
851}