kaspa_consensus/pipeline/header_processor/
post_pow_validation.rs1use super::{HeaderProcessingContext, HeaderProcessor};
2use crate::errors::{BlockProcessResult, RuleError, TwoDimVecDisplay};
3use crate::model::services::reachability::ReachabilityService;
4use crate::processes::window::WindowManager;
5use kaspa_consensus_core::header::Header;
6use kaspa_hashes::Hash;
7use std::collections::HashSet;
8
9impl HeaderProcessor {
10 pub fn post_pow_validation(&self, ctx: &mut HeaderProcessingContext, header: &Header) -> BlockProcessResult<()> {
11 self.check_blue_score(ctx, header)?;
12 self.check_blue_work(ctx, header)?;
13 self.check_median_timestamp(ctx, header)?;
14 self.check_merge_size_limit(ctx)?;
15 self.check_bounded_merge_depth(ctx)?;
16 self.check_pruning_point(ctx, header)?;
17 self.check_indirect_parents(ctx, header)
18 }
19
20 pub fn check_median_timestamp(&self, ctx: &mut HeaderProcessingContext, header: &Header) -> BlockProcessResult<()> {
21 let (past_median_time, window) = self.window_manager.calc_past_median_time(ctx.ghostdag_data())?;
22 ctx.block_window_for_past_median_time = Some(window);
23
24 if header.timestamp <= past_median_time {
25 return Err(RuleError::TimeTooOld(header.timestamp, past_median_time));
26 }
27
28 Ok(())
29 }
30
31 pub fn check_merge_size_limit(&self, ctx: &mut HeaderProcessingContext) -> BlockProcessResult<()> {
32 let mergeset_size = ctx.ghostdag_data().mergeset_size() as u64;
33 if mergeset_size > self.mergeset_size_limit {
34 return Err(RuleError::MergeSetTooBig(mergeset_size, self.mergeset_size_limit));
35 }
36 Ok(())
37 }
38
39 fn check_blue_score(&self, ctx: &mut HeaderProcessingContext, header: &Header) -> BlockProcessResult<()> {
40 let gd_blue_score = ctx.ghostdag_data().blue_score;
41 if gd_blue_score != header.blue_score {
42 return Err(RuleError::UnexpectedHeaderBlueScore(gd_blue_score, header.blue_score));
43 }
44 Ok(())
45 }
46
47 fn check_blue_work(&self, ctx: &mut HeaderProcessingContext, header: &Header) -> BlockProcessResult<()> {
48 let gd_blue_work = ctx.ghostdag_data().blue_work;
49 if gd_blue_work != header.blue_work {
50 return Err(RuleError::UnexpectedHeaderBlueWork(gd_blue_work, header.blue_work));
51 }
52 Ok(())
53 }
54
55 pub fn check_indirect_parents(&self, ctx: &mut HeaderProcessingContext, header: &Header) -> BlockProcessResult<()> {
56 let expected_block_parents = self.parents_manager.calc_block_parents(ctx.pruning_point(), header.direct_parents());
57 if header.parents_by_level.len() != expected_block_parents.len()
58 || !expected_block_parents.iter().enumerate().all(|(block_level, expected_level_parents)| {
59 let header_level_parents = &header.parents_by_level[block_level];
60 if header_level_parents.len() != expected_level_parents.len() {
61 return false;
62 }
63
64 let expected_set = HashSet::<&Hash>::from_iter(expected_level_parents);
65 header_level_parents.iter().all(|header_parent| expected_set.contains(header_parent))
66 })
67 {
68 return Err(RuleError::UnexpectedIndirectParents(
69 TwoDimVecDisplay(expected_block_parents),
70 TwoDimVecDisplay(header.parents_by_level.clone()),
71 ));
72 };
73 Ok(())
74 }
75
76 pub fn check_pruning_point(&self, ctx: &mut HeaderProcessingContext, header: &Header) -> BlockProcessResult<()> {
77 let expected = self.pruning_point_manager.expected_header_pruning_point(ctx.ghostdag_data().to_compact(), ctx.pruning_info);
78 if expected != header.pruning_point {
79 return Err(RuleError::WrongHeaderPruningPoint(expected, header.pruning_point));
80 }
81 Ok(())
82 }
83
84 pub fn check_bounded_merge_depth(&self, ctx: &mut HeaderProcessingContext) -> BlockProcessResult<()> {
85 let ghostdag_data = ctx.ghostdag_data();
86 let merge_depth_root = self.depth_manager.calc_merge_depth_root(ghostdag_data, ctx.pruning_point());
87 let finality_point = self.depth_manager.calc_finality_point(ghostdag_data, ctx.pruning_point());
88 let mut kosherizing_blues: Option<Vec<Hash>> = None;
89
90 for red in ghostdag_data.mergeset_reds.iter().copied() {
91 if self.reachability_service.is_dag_ancestor_of(merge_depth_root, red) {
92 continue;
93 }
94 if kosherizing_blues.is_none() {
96 kosherizing_blues = Some(self.depth_manager.kosherizing_blues(ghostdag_data, merge_depth_root).collect());
97 }
98 if !self.reachability_service.is_dag_ancestor_of_any(red, &mut kosherizing_blues.as_ref().unwrap().iter().copied()) {
99 return Err(RuleError::ViolatingBoundedMergeDepth);
100 }
101 }
102
103 ctx.merge_depth_root = Some(merge_depth_root);
104 ctx.finality_point = Some(finality_point);
105 Ok(())
106 }
107}