1use bitcoin::{util::uint::Uint256, BlockHash, BlockHeader, Network};
2
3use crate::{
4 constants::{
5 checkpoints, last_checkpoint, latest_checkpoint_height, max_target, no_pow_retargeting,
6 pow_limit_bits, BLOCKS_IN_ONE_YEAR, DIFFICULTY_ADJUSTMENT_INTERVAL, TEN_MINUTES,
7 },
8 BlockHeight,
9};
10
11#[derive(Debug)]
13pub enum ValidateHeaderError {
14 HeaderIsOld,
17 DoesNotMatchCheckpoint,
19 InvalidPoWForHeaderTarget,
22 InvalidPoWForComputedTarget,
25 TargetDifficultyAboveMax,
28 HeightTooLow,
30 PrevHeaderNotFound,
33}
34
35pub trait HeaderStore {
36 fn get_header(&self, hash: &BlockHash) -> Option<(BlockHeader, BlockHeight)>;
38 fn get_height(&self) -> BlockHeight;
40 fn get_initial_hash(&self) -> BlockHash;
42}
43
44pub fn validate_header(
47 network: &Network,
48 store: &impl HeaderStore,
49 header: &BlockHeader,
50) -> Result<(), ValidateHeaderError> {
51 let chain_height = store.get_height();
52 let (prev_header, prev_height) = match store.get_header(&header.prev_blockhash) {
53 Some(result) => result,
54 None => {
55 return Err(ValidateHeaderError::PrevHeaderNotFound);
56 }
57 };
58
59 if !is_header_within_one_year_of_tip(prev_height, chain_height) {
60 return Err(ValidateHeaderError::HeightTooLow);
61 }
62
63 if !is_timestamp_valid(store, header) {
64 return Err(ValidateHeaderError::HeaderIsOld);
65 }
66
67 if !is_checkpoint_valid(network, prev_height, header, chain_height) {
68 return Err(ValidateHeaderError::DoesNotMatchCheckpoint);
69 }
70
71 let header_target = header.target();
72 if header_target > max_target(network) {
73 return Err(ValidateHeaderError::TargetDifficultyAboveMax);
74 }
75
76 if header.validate_pow(&header_target).is_err() {
77 return Err(ValidateHeaderError::InvalidPoWForHeaderTarget);
78 }
79
80 let target = get_next_target(network, store, &prev_header, prev_height, header);
81 if let Err(err) = header.validate_pow(&target) {
82 match err {
83 bitcoin::Error::BlockBadProofOfWork => println!("bad proof of work"),
84 bitcoin::Error::BlockBadTarget => println!("bad target"),
85 _ => {}
86 };
87 return Err(ValidateHeaderError::InvalidPoWForComputedTarget);
88 }
89
90 Ok(())
91}
92
93pub fn is_beyond_last_checkpoint(network: &Network, height: BlockHeight) -> bool {
97 match last_checkpoint(network) {
98 Some(last) => last <= height,
99 None => true,
100 }
101}
102
103fn is_checkpoint_valid(
108 network: &Network,
109 prev_height: BlockHeight,
110 header: &BlockHeader,
111 chain_height: BlockHeight,
112) -> bool {
113 let checkpoints = checkpoints(network);
114 let next_height = prev_height.saturating_add(1);
115 if let Some(next_hash) = checkpoints.get(&next_height) {
116 return *next_hash == header.block_hash();
117 }
118
119 let checkpoint_height = latest_checkpoint_height(network, chain_height);
120 next_height > checkpoint_height
121}
122
123fn is_header_within_one_year_of_tip(prev_height: BlockHeight, chain_height: BlockHeight) -> bool {
125 let header_height = prev_height
128 .checked_add(1)
129 .expect("next height causes an overflow");
130
131 let height_one_year_ago = chain_height.saturating_sub(BLOCKS_IN_ONE_YEAR);
132 header_height >= height_one_year_ago
133}
134
135fn is_timestamp_valid(store: &impl HeaderStore, header: &BlockHeader) -> bool {
139 let mut times = vec![];
140 let mut current_header = *header;
141 let initial_hash = store.get_initial_hash();
142 for _ in 0..11 {
143 if let Some((prev_header, _)) = store.get_header(¤t_header.prev_blockhash) {
144 times.push(prev_header.time);
145 if current_header.prev_blockhash == initial_hash {
146 break;
147 }
148 current_header = prev_header;
149 }
150 }
151
152 times.sort_unstable();
153 let median = times[times.len() / 2];
154 header.time > median
155}
156
157fn get_next_target(
164 network: &Network,
165 store: &impl HeaderStore,
166 prev_header: &BlockHeader,
167 prev_height: BlockHeight,
168 header: &BlockHeader,
169) -> Uint256 {
170 match network {
171 Network::Testnet | Network::Regtest => {
172 if (prev_height + 1) % DIFFICULTY_ADJUSTMENT_INTERVAL != 0 {
173 if header.time > prev_header.time + TEN_MINUTES * 2 {
179 max_target(network)
182 } else {
183 BlockHeader::u256_from_compact_target(find_next_difficulty_in_chain(
186 network,
187 store,
188 prev_header,
189 prev_height,
190 ))
191 }
192 } else {
193 BlockHeader::u256_from_compact_target(compute_next_difficulty(
194 network,
195 store,
196 prev_header,
197 prev_height,
198 ))
199 }
200 }
201 Network::Bitcoin | Network::Signet => BlockHeader::u256_from_compact_target(
202 compute_next_difficulty(network, store, prev_header, prev_height),
203 ),
204 }
205}
206
207fn find_next_difficulty_in_chain(
215 network: &Network,
216 store: &impl HeaderStore,
217 prev_header: &BlockHeader,
218 prev_height: BlockHeight,
219) -> u32 {
220 let pow_limit_bits = pow_limit_bits(network);
222 match network {
223 Network::Testnet | Network::Regtest => {
224 let mut current_header = *prev_header;
225 let mut current_height = prev_height;
226 let mut current_hash = prev_header.block_hash();
227 let initial_header_hash = store.get_initial_hash();
228
229 while current_hash != initial_header_hash {
232 if current_header.bits != pow_limit_bits
233 || current_height % DIFFICULTY_ADJUSTMENT_INTERVAL == 0
234 {
235 return current_header.bits;
236 }
237
238 let header_info = store
240 .get_header(¤t_header.prev_blockhash)
241 .expect("previous header should be in the header store");
242 current_header = header_info.0;
243 current_height = header_info.1;
244 current_hash = current_header.prev_blockhash;
245 }
246 pow_limit_bits
247 }
248 Network::Bitcoin | Network::Signet => pow_limit_bits,
249 }
250}
251
252fn compute_next_difficulty(
255 network: &Network,
256 store: &impl HeaderStore,
257 prev_header: &BlockHeader,
258 prev_height: BlockHeight,
259) -> u32 {
260 if (prev_height + 1) % DIFFICULTY_ADJUSTMENT_INTERVAL != 0 || no_pow_retargeting(network) {
266 return prev_header.bits;
267 }
268
269 let mut current_header = *prev_header;
271 for _i in 0..(DIFFICULTY_ADJUSTMENT_INTERVAL - 1) {
272 if let Some((header, _)) = store.get_header(¤t_header.prev_blockhash) {
273 current_header = header;
274 }
275 }
276 let last_adjustment_header = current_header;
278 let last_adjustment_time = last_adjustment_header.time;
279
280 let actual_interval = prev_header.time - last_adjustment_time;
287 let mut adjusted_interval = actual_interval as u32;
288
289 let target_adjustment_interval_time: u32 = DIFFICULTY_ADJUSTMENT_INTERVAL * TEN_MINUTES; adjusted_interval = u32::max(adjusted_interval, target_adjustment_interval_time / 4);
295 adjusted_interval = u32::min(adjusted_interval, target_adjustment_interval_time * 4);
296
297 let mut target = prev_header.target();
301 target = target.mul_u32(adjusted_interval);
302 target = target / Uint256::from_u64(target_adjustment_interval_time as u64).unwrap();
303
304 target = Uint256::min(target, max_target(network));
307
308 BlockHeader::compact_target_from_u256(&target)
310}
311
312#[cfg(test)]
313mod test {
314
315 use std::{collections::HashMap, path::PathBuf, str::FromStr};
316
317 use bitcoin::{consensus::deserialize, hashes::hex::FromHex, TxMerkleNode};
318 use csv::Reader;
319
320 use super::*;
321 use crate::constants::test::{
322 MAINNET_HEADER_11109, MAINNET_HEADER_11110, MAINNET_HEADER_11111, MAINNET_HEADER_586656,
323 MAINNET_HEADER_705600, MAINNET_HEADER_705601, MAINNET_HEADER_705602,
324 TESTNET_HEADER_2132555, TESTNET_HEADER_2132556,
325 };
326
327 #[derive(Clone)]
328 struct StoredHeader {
329 header: BlockHeader,
330 height: BlockHeight,
331 }
332
333 struct SimpleHeaderStore {
334 headers: HashMap<BlockHash, StoredHeader>,
335 height: BlockHeight,
336 initial_hash: BlockHash,
337 }
338
339 impl SimpleHeaderStore {
340 fn new(initial_header: BlockHeader, height: BlockHeight) -> Self {
341 let initial_hash = initial_header.block_hash();
342 let mut headers = HashMap::new();
343 headers.insert(
344 initial_hash,
345 StoredHeader {
346 header: initial_header,
347 height,
348 },
349 );
350
351 Self {
352 headers,
353 height,
354 initial_hash,
355 }
356 }
357
358 fn add(&mut self, header: BlockHeader) {
359 let prev = self
360 .headers
361 .get(&header.prev_blockhash)
362 .expect("prev hash missing");
363 let stored_header = StoredHeader {
364 header,
365 height: prev.height + 1,
366 };
367
368 self.height = stored_header.height;
369 self.headers.insert(header.block_hash(), stored_header);
370 }
371 }
372
373 impl HeaderStore for SimpleHeaderStore {
374 fn get_header(&self, hash: &BlockHash) -> Option<(BlockHeader, BlockHeight)> {
375 self.headers
376 .get(hash)
377 .map(|stored| (stored.header, stored.height))
378 }
379
380 fn get_height(&self) -> BlockHeight {
381 self.height
382 }
383
384 fn get_initial_hash(&self) -> BlockHash {
385 self.initial_hash
386 }
387 }
388
389 fn deserialize_header(encoded_bytes: &str) -> BlockHeader {
390 let bytes = Vec::from_hex(encoded_bytes).expect("failed to decoded bytes");
391 deserialize(bytes.as_slice()).expect("failed to deserialize")
392 }
393
394 fn get_bitcoin_headers() -> Vec<BlockHeader> {
399 let rdr = Reader::from_path(
400 PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap())
401 .join("tests/data/headers.csv"),
402 );
403 assert!(rdr.is_ok(), "Unable to find blockchain_headers.csv file");
404 let mut rdr = rdr.unwrap();
405 let mut headers = vec![];
406 for result in rdr.records() {
407 let record = result.unwrap();
408 let header = BlockHeader {
409 version: i32::from_str_radix(record.get(0).unwrap(), 16).unwrap(),
410 prev_blockhash: BlockHash::from_str(record.get(1).unwrap()).unwrap(),
411 merkle_root: TxMerkleNode::from_str(record.get(2).unwrap()).unwrap(),
412 time: u32::from_str_radix(record.get(3).unwrap(), 16).unwrap(),
413 bits: u32::from_str_radix(record.get(4).unwrap(), 16).unwrap(),
414 nonce: u32::from_str_radix(record.get(5).unwrap(), 16).unwrap(),
415 };
416 headers.push(header);
417 }
418 headers
419 }
420
421 #[test]
422 fn test_simple_mainnet() {
423 let header_705600 = deserialize_header(MAINNET_HEADER_705600);
424 let header_705601 = deserialize_header(MAINNET_HEADER_705601);
425 let store = SimpleHeaderStore::new(header_705600, 705_600);
426 let result = validate_header(&Network::Bitcoin, &store, &header_705601);
427 assert!(result.is_ok());
428 }
429
430 #[test]
431 fn test_simple_testnet() {
432 let header_2132555 = deserialize_header(TESTNET_HEADER_2132555);
433 let header_2132556 = deserialize_header(TESTNET_HEADER_2132556);
434 let store = SimpleHeaderStore::new(header_2132555, 2_132_555);
435 let result = validate_header(&Network::Testnet, &store, &header_2132556);
436 assert!(result.is_ok());
437 }
438
439 #[test]
440 fn test_is_header_valid() {
441 let header_586656 = deserialize_header(MAINNET_HEADER_586656);
442 let mut store = SimpleHeaderStore::new(header_586656, 586_656);
443 let headers = get_bitcoin_headers();
444 for (i, header) in headers.iter().enumerate() {
445 let result = validate_header(&Network::Bitcoin, &store, header);
446 assert!(
447 result.is_ok(),
448 "Failed to validate header on line {}: {:?}",
449 i,
450 result
451 );
452 store.add(*header);
453 }
454 }
455
456 #[test]
457 fn test_is_timestamp_valid() {
458 let header_705600 = deserialize_header(MAINNET_HEADER_705600);
459 let header_705601 = deserialize_header(MAINNET_HEADER_705601);
460 let header_705602 = deserialize_header(MAINNET_HEADER_705602);
461 let mut store = SimpleHeaderStore::new(header_705600, 705_600);
462 store.add(header_705601);
463 store.add(header_705602);
464
465 let mut header = BlockHeader {
466 version: 0x20800004,
467 prev_blockhash: BlockHash::from_hex(
468 "00000000000000000001eea12c0de75000c2546da22f7bf42d805c1d2769b6ef",
469 )
470 .unwrap(),
471 merkle_root: TxMerkleNode::from_hex(
472 "c120ff2ae1363593a0b92e0d281ec341a0cc989b4ee836dc3405c9f4215242a6",
473 )
474 .unwrap(),
475 time: 1634590600,
476 bits: 0x170e0408,
477 nonce: 0xb48e8b0a,
478 };
479 assert!(is_timestamp_valid(&store, &header));
480
481 header.time = 1634588800;
483 assert!(!is_timestamp_valid(&store, &header));
484
485 let result = validate_header(&Network::Bitcoin, &store, &header);
486 assert!(matches!(result, Err(ValidateHeaderError::HeaderIsOld)));
487 }
488
489 #[test]
490 fn test_is_header_valid_missing_prev_header() {
491 let header_705600 = deserialize_header(MAINNET_HEADER_705600);
492 let header_705602 = deserialize_header(MAINNET_HEADER_705602);
493 let store = SimpleHeaderStore::new(header_705600, 705_600);
494 let result = validate_header(&Network::Bitcoin, &store, &header_705602);
495 assert!(matches!(
496 result,
497 Err(ValidateHeaderError::PrevHeaderNotFound)
498 ));
499 }
500
501 #[test]
502 fn test_is_header_valid_checkpoint_valid_at_height() {
503 let network = Network::Bitcoin;
504 let header_11110 = deserialize_header(MAINNET_HEADER_11110);
505 let mut header_11111 = deserialize_header(MAINNET_HEADER_11111);
506 let store = SimpleHeaderStore::new(header_11110, 11110);
507 let (_, prev_height) = store.get_header(&header_11111.prev_blockhash).unwrap();
508
509 assert!(is_checkpoint_valid(
510 &network,
511 prev_height,
512 &header_11111,
513 store.get_height()
514 ));
515
516 header_11111.time -= 1;
519
520 let result = validate_header(&network, &store, &header_11111);
521 assert!(matches!(
522 result,
523 Err(ValidateHeaderError::DoesNotMatchCheckpoint)
524 ));
525 }
526
527 #[test]
528 fn test_is_header_valid_checkpoint_valid_detect_fork_around_11111() {
529 let network = Network::Bitcoin;
530 let header_11109 = deserialize_header(MAINNET_HEADER_11109);
531 let header_11110 = deserialize_header(MAINNET_HEADER_11110);
532 let header_11111 = deserialize_header(MAINNET_HEADER_11111);
533 let mut bad_header_11110 = header_11110;
535 bad_header_11110.time -= 1;
536
537 let mut store = SimpleHeaderStore::new(header_11109, 11109);
538 store.add(header_11110);
539
540 let (_, prev_height) = store.get_header(&header_11111.prev_blockhash).unwrap();
541
542 assert!(is_checkpoint_valid(
543 &network,
544 prev_height,
545 &header_11111,
546 store.get_height()
547 ));
548
549 store.add(header_11111);
550
551 let (_, prev_height) = store.get_header(&header_11111.prev_blockhash).unwrap();
553 assert!(!is_checkpoint_valid(
554 &network,
555 prev_height,
556 &bad_header_11110,
557 store.get_height()
558 ));
559 }
560
561 #[test]
562 fn test_is_header_valid_checkpoint_valid_detect_fork_around_705600() {
563 let network = Network::Bitcoin;
564 let header_705600 = deserialize_header(MAINNET_HEADER_705600);
565 let header_705601 = deserialize_header(MAINNET_HEADER_705601);
566 let store = SimpleHeaderStore::new(header_705600, 705_600);
567 let (_, prev_height) = store.get_header(&header_705601.prev_blockhash).unwrap();
568
569 assert!(is_checkpoint_valid(
570 &network,
571 prev_height,
572 &header_705601,
573 store.get_height()
574 ));
575 }
576
577 #[test]
578 fn test_is_header_valid_invalid_header_target() {
579 let header_705600 = deserialize_header(MAINNET_HEADER_705600);
580 let mut header = deserialize_header(MAINNET_HEADER_705601);
581 header.bits = pow_limit_bits(&Network::Bitcoin);
582 let store = SimpleHeaderStore::new(header_705600, 705_600);
583 let result = validate_header(&Network::Bitcoin, &store, &header);
584 assert!(matches!(
585 result,
586 Err(ValidateHeaderError::InvalidPoWForHeaderTarget)
587 ));
588 }
589
590 #[test]
591 fn test_is_header_valid_invalid_computed_target() {
592 let header_705600 = deserialize_header(MAINNET_HEADER_705600);
593 let header = deserialize_header(MAINNET_HEADER_705601);
594 let store = SimpleHeaderStore::new(header_705600, 705_600);
595 let result = validate_header(&Network::Regtest, &store, &header);
596 assert!(matches!(
597 result,
598 Err(ValidateHeaderError::InvalidPoWForComputedTarget)
599 ));
600 }
601
602 #[test]
603 fn test_is_header_valid_target_difficulty_above_max() {
604 let header_705600 = deserialize_header(MAINNET_HEADER_705600);
605 let mut header = deserialize_header(MAINNET_HEADER_705601);
606 header.bits = pow_limit_bits(&Network::Regtest);
607 let store = SimpleHeaderStore::new(header_705600, 705_600);
608 let result = validate_header(&Network::Bitcoin, &store, &header);
609 assert!(matches!(
610 result,
611 Err(ValidateHeaderError::TargetDifficultyAboveMax)
612 ));
613 }
614
615 #[test]
616 fn test_is_header_within_one_year_of_tip_next_height_is_above_the_minimum() {
617 assert!(
618 is_header_within_one_year_of_tip(700_000, 650_000),
619 "next height is above the one year minimum"
620 );
621 assert!(
622 is_header_within_one_year_of_tip(700_000, 750_000),
623 "next height is within the one year range"
624 );
625 assert!(
626 !is_header_within_one_year_of_tip(700_000, 800_000),
627 "next height is below the one year minimum"
628 );
629 }
630
631 #[test]
632 #[should_panic(expected = "next height causes an overflow")]
633 fn test_is_header_within_one_year_of_tip_should_panic_as_next_height_is_too_high() {
634 is_header_within_one_year_of_tip(BlockHeight::MAX, 0);
635 }
636
637 #[test]
638 fn test_is_header_within_one_year_of_tip_chain_height_is_less_than_one_year() {
639 assert!(
640 is_header_within_one_year_of_tip(1, 0),
641 "chain height is less than one year"
642 );
643 assert!(
644 is_header_within_one_year_of_tip(1, BLOCKS_IN_ONE_YEAR + 2),
645 "chain height difference is exactly one year"
646 );
647 assert!(
648 !is_header_within_one_year_of_tip(1, BLOCKS_IN_ONE_YEAR + 3),
649 "chain height difference is one year + 1 block"
650 );
651 }
652}