kaspa_consensus/pipeline/header_processor/
post_pow_validation.rs

1use 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            // Lazy load the kosherizing blocks since this case is extremely rare
95            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}