kaspa_consensus/processes/
block_depth.rs

1use kaspa_consensus_core::blockhash::ORIGIN;
2use kaspa_hashes::Hash;
3use std::sync::Arc;
4
5use crate::model::{
6    services::reachability::{MTReachabilityService, ReachabilityService},
7    stores::{
8        depth::DepthStoreReader,
9        ghostdag::{GhostdagData, GhostdagStoreReader},
10        reachability::ReachabilityStoreReader,
11    },
12};
13
14#[derive(Clone)]
15pub struct BlockDepthManager<S: DepthStoreReader, U: ReachabilityStoreReader, V: GhostdagStoreReader> {
16    merge_depth: u64,
17    finality_depth: u64,
18    genesis_hash: Hash,
19    depth_store: Arc<S>,
20    reachability_service: MTReachabilityService<U>,
21    ghostdag_store: Arc<V>,
22}
23
24impl<S: DepthStoreReader, U: ReachabilityStoreReader, V: GhostdagStoreReader> BlockDepthManager<S, U, V> {
25    pub fn new(
26        merge_depth: u64,
27        finality_depth: u64,
28        genesis_hash: Hash,
29        depth_store: Arc<S>,
30        reachability_service: MTReachabilityService<U>,
31        ghostdag_store: Arc<V>,
32    ) -> Self {
33        Self { merge_depth, finality_depth, genesis_hash, depth_store, reachability_service, ghostdag_store }
34    }
35    pub fn calc_merge_depth_root(&self, ghostdag_data: &GhostdagData, pruning_point: Hash) -> Hash {
36        self.calculate_block_at_depth(ghostdag_data, self.merge_depth, pruning_point)
37    }
38
39    pub fn calc_finality_point(&self, ghostdag_data: &GhostdagData, pruning_point: Hash) -> Hash {
40        self.calculate_block_at_depth(ghostdag_data, self.finality_depth, pruning_point)
41    }
42
43    fn calculate_block_at_depth(&self, ghostdag_data: &GhostdagData, depth: u64, pruning_point: Hash) -> Hash {
44        assert!(depth == self.merge_depth || depth == self.finality_depth);
45
46        if ghostdag_data.blue_score < depth {
47            return self.genesis_hash;
48        }
49
50        let pp_bs = self.ghostdag_store.get_blue_score(pruning_point).unwrap();
51
52        if ghostdag_data.blue_score < pp_bs + depth {
53            return ORIGIN;
54        }
55
56        if !self.reachability_service.is_chain_ancestor_of(pruning_point, ghostdag_data.selected_parent) {
57            return ORIGIN;
58        }
59
60        let mut current = if depth == self.merge_depth {
61            self.depth_store.merge_depth_root(ghostdag_data.selected_parent).unwrap()
62        } else {
63            self.depth_store.finality_point(ghostdag_data.selected_parent).unwrap()
64        };
65
66        // In this case we expect the pruning point or a block above it to be the block at depth.
67        // Note that above we already verified the chain and distance conditions for this.
68        // Additionally observe that if `current` is a valid hash it must not be pruned for the same reason.
69        if current == ORIGIN {
70            current = pruning_point;
71        }
72
73        let required_blue_score = ghostdag_data.blue_score - depth;
74
75        for chain_block in self.reachability_service.forward_chain_iterator(current, ghostdag_data.selected_parent, true) {
76            if self.ghostdag_store.get_blue_score(chain_block).unwrap() >= required_blue_score {
77                break;
78            }
79
80            current = chain_block;
81        }
82
83        current
84    }
85
86    /// Returns the set of blues which are eligible for "kosherizing" merge bound violating blocks.
87    /// By prunality rules, these blocks must have `merge_depth_root` on their selected chain.  
88    pub fn kosherizing_blues<'a>(
89        &'a self,
90        ghostdag_data: &'a GhostdagData,
91        merge_depth_root: Hash,
92    ) -> impl DoubleEndedIterator<Item = Hash> + 'a {
93        ghostdag_data
94            .mergeset_blues
95            .iter()
96            .copied()
97            .filter(move |blue| self.reachability_service.is_chain_ancestor_of(merge_depth_root, *blue))
98    }
99}