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 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 != ¶meters.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 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}