cfx_internal_common/
state_availability_boundary.rs

1// Copyright 2020 Conflux Foundation. All rights reserved.
2// Conflux is free software and distributed under GNU General Public License.
3// See http://www.gnu.org/licenses/
4#[derive(Clone, Debug, MallocSizeOf)]
5pub struct StateAvailabilityBoundary {
6    /// This is the hash of blocks in pivot chain based on current graph.
7    #[debug(ignore)]
8    pub pivot_chain: Vec<H256>,
9
10    pub synced_state_height: u64,
11    /// All states of `full_state_space` are available for reading after this
12    /// height. `None` means no full state are kept.
13    pub full_state_start_height: Option<u64>,
14    /// `None` means both spaces are kept.
15    /// This field is not used if `full_state_start_height` is `None`.
16    pub full_state_space: Option<Space>,
17
18    /// This is the lower boundary height of available state where we can
19    /// execute new epochs based on it. Note that `synced_state_height` is
20    /// within this bound for execution, but its state cannot be accessed
21    /// through `get_state_no_commit`.
22    pub lower_bound: u64,
23    /// This is the upper boundary height of available state.
24    pub upper_bound: u64,
25    // Optimistic execution is the feature to execute ahead of the deferred
26    // execution boundary. The goal is to pipeline the transaction
27    // execution and the block packaging and verification.
28    // optimistic_executed_height is the number of step to go ahead
29    pub optimistic_executed_height: Option<u64>,
30}
31
32impl StateAvailabilityBoundary {
33    pub fn new(
34        epoch_hash: H256, epoch_height: u64,
35        full_state_start_height: Option<u64>, full_state_space: Option<Space>,
36    ) -> Self {
37        Self {
38            pivot_chain: vec![epoch_hash],
39            synced_state_height: 0,
40            full_state_start_height,
41            full_state_space,
42            lower_bound: epoch_height,
43            upper_bound: epoch_height,
44            optimistic_executed_height: None,
45        }
46    }
47
48    /// Check if the state can be accessed for reading.
49    pub fn check_availability(&self, height: u64, block_hash: &H256) -> bool {
50        (height == 0 || height != self.synced_state_height)
51            && self.lower_bound <= height
52            && height <= self.upper_bound
53            && {
54                let r = self.pivot_chain[(height - self.lower_bound) as usize]
55                    == *block_hash;
56                if !r {
57                    debug!(
58                        "pivot_chain={:?} should be {:?} asked is {:?}",
59                        self.pivot_chain,
60                        self.pivot_chain[(height - self.lower_bound) as usize],
61                        block_hash
62                    );
63                }
64                r
65            }
66    }
67
68    pub fn check_read_availability(
69        &self, height: u64, block_hash: &H256, space: Option<Space>,
70    ) -> bool {
71        self.check_availability(height, block_hash)
72            || (height < self.lower_bound
73                && self.full_state_available(height, space))
74    }
75
76    fn full_state_available(&self, height: u64, space: Option<Space>) -> bool {
77        match self.full_state_start_height {
78            // No full state
79            None => false,
80            // Support full state, so check the height and space.
81            Some(start_height) => {
82                height >= start_height && self.contains_space(&space)
83            }
84        }
85    }
86
87    pub fn contains_space(&self, space: &Option<Space>) -> bool {
88        match (space, &self.full_state_space) {
89            (_, None) => {
90                // We keep the state in all spaces.
91                true
92            }
93            (None, Some(_)) => {
94                // We keep a part of states but all states are needed.
95                false
96            }
97            (Some(need_space), Some(kept_space)) => need_space == kept_space,
98        }
99    }
100
101    /// Try to update `upper_bound` according to a new executed block.
102    pub fn adjust_upper_bound(&mut self, executed_block: &BlockHeader) {
103        let next_index = (self.upper_bound - self.lower_bound + 1) as usize;
104        if next_index < self.pivot_chain.len()
105            && executed_block.height() == self.upper_bound + 1
106            && executed_block.hash() == self.pivot_chain[next_index]
107        {
108            self.upper_bound += 1;
109        }
110    }
111
112    /// This function will record the most recent synced_state_height for
113    /// special case handling.
114    pub fn set_synced_state_height(&mut self, synced_state_height: u64) {
115        self.synced_state_height = synced_state_height;
116    }
117
118    /// This function will set a new lower boundary height of available state.
119    /// Caller should make sure the new lower boundary height should be greater
120    /// than or equal to current lower boundary height.
121    /// Caller should also make sure the new lower boundary height should be
122    /// less than or equal to current upper boundary height.
123    pub fn adjust_lower_bound(&mut self, new_lower_bound: u64) {
124        // If we are going to call this function, `upper_bound` will not be 0
125        // unless it is a full node and is in header phase. And we should do
126        // nothing in this case.
127        if self.upper_bound == 0 {
128            return;
129        }
130        assert!(self.lower_bound <= new_lower_bound);
131        assert!(
132            new_lower_bound <= self.upper_bound,
133            "however {} > {}, self {:?}",
134            new_lower_bound,
135            self.upper_bound,
136            self,
137        );
138        if self.synced_state_height != 0
139            && new_lower_bound > self.synced_state_height + REWARD_EPOCH_COUNT
140        {
141            self.synced_state_height = 0;
142        }
143        self.pivot_chain = self
144            .pivot_chain
145            .split_off((new_lower_bound - self.lower_bound) as usize);
146        self.lower_bound = new_lower_bound;
147    }
148}
149
150use cfx_parameters::consensus_internal::REWARD_EPOCH_COUNT;
151use cfx_types::{Space, H256};
152use derive_more::Debug;
153use malloc_size_of_derive::MallocSizeOf;
154use primitives::BlockHeader;