1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// Copyright 2019 Conflux Foundation. All rights reserved.
// Conflux is free software and distributed under GNU General Public License.
// See http://www.gnu.org/licenses/

// StateManager is the single entry-point to access State for any epoch.
// StateManager manages internal mutability and is thread-safe.
pub use super::impls::state_manager::StateManager;

pub type SharedStateManager = Arc<StateManager>;

#[derive(Debug)]
pub struct StateIndex {
    pub snapshot_epoch_id: EpochId,
    pub snapshot_merkle_root: MerkleHash,
    pub intermediate_epoch_id: EpochId,
    pub intermediate_trie_root_merkle: MerkleHash,
    pub maybe_intermediate_mpt_key_padding: Option<DeltaMptKeyPadding>,
    pub epoch_id: EpochId,
    pub delta_mpt_key_padding: DeltaMptKeyPadding,
    pub maybe_delta_trie_height: Option<u32>,
    pub maybe_height: Option<u64>,
}

// The trait is created to separate the implementation to another file, and the
// concrete struct is put into inner mod, because the implementation is
// anticipated to be too complex to present in the same file of the API.
pub trait StateManagerTrait {
    /// At the boundary of snapshot, getting a state for new epoch will switch
    /// to new Delta MPT, but it's unnecessary getting a no-commit state.
    ///
    /// With try_open == true, the call fails immediately when the max number of
    /// snapshot open is reached.
    ///
    /// If `space` is `None`, we need data from all spaces.
    fn get_state_no_commit(
        self: &Arc<Self>, epoch_id: StateIndex, try_open: bool,
        space: Option<Space>,
    ) -> Result<Option<Box<dyn StateTrait>>>;
    fn get_state_for_next_epoch(
        self: &Arc<Self>, parent_epoch_id: StateIndex,
        recover_mpt_during_construct_pivot_state: bool,
    ) -> Result<Option<Box<dyn StateTrait>>>;
    fn get_state_for_genesis_write(self: &Arc<Self>) -> Box<dyn StateTrait>;
}

pub trait ReplicatedStateManagerTrait {
    fn get_replicated_state_for_next_epoch(
        self: &Arc<Self>, parent_epoch_id: StateIndex,
    ) -> Result<Option<Box<dyn StateTrait>>>;
    fn get_replicated_state_for_genesis_write(
        self: &Arc<Self>,
    ) -> Box<dyn StateTrait>;
}

impl StateIndex {
    pub fn height_to_delta_height(
        height: u64, snapshot_epoch_count: u32,
    ) -> u32 {
        if height == 0 {
            0
        } else {
            ((height - 1) % (snapshot_epoch_count as u64)) as u32 + 1
        }
    }

    pub fn new_for_test_only_delta_mpt(epoch_id: &EpochId) -> Self {
        Self {
            snapshot_epoch_id: NULL_EPOCH,
            snapshot_merkle_root: MERKLE_NULL_NODE,
            intermediate_epoch_id: NULL_EPOCH,
            intermediate_trie_root_merkle: MERKLE_NULL_NODE,
            maybe_intermediate_mpt_key_padding: None,
            epoch_id: *epoch_id,
            delta_mpt_key_padding: GENESIS_DELTA_MPT_KEY_PADDING.clone(),
            maybe_delta_trie_height: Some(0),
            maybe_height: Some(0),
        }
    }

    /// Height is used to check for shifting snapshot.
    /// The state root and height information should be provided from consensus.
    pub fn new_for_next_epoch(
        base_epoch_id: &EpochId, state_root: &StateRootWithAuxInfo,
        height: u64, snapshot_epoch_count: u32,
    ) -> Self {
        Self {
            snapshot_epoch_id: state_root.aux_info.snapshot_epoch_id,
            snapshot_merkle_root: state_root.state_root.snapshot_root,
            intermediate_epoch_id: state_root.aux_info.intermediate_epoch_id,
            intermediate_trie_root_merkle: state_root
                .state_root
                .intermediate_delta_root,
            maybe_intermediate_mpt_key_padding: state_root
                .aux_info
                .maybe_intermediate_mpt_key_padding
                .clone(),
            epoch_id: *base_epoch_id,
            delta_mpt_key_padding: state_root
                .aux_info
                .delta_mpt_key_padding
                .clone(),
            maybe_delta_trie_height: Some(Self::height_to_delta_height(
                height,
                snapshot_epoch_count,
            )),
            maybe_height: Some(height),
        }
    }

    pub fn new_for_readonly(
        epoch_id: &EpochId, state_root: &StateRootWithAuxInfo,
    ) -> Self {
        Self {
            snapshot_epoch_id: state_root.aux_info.snapshot_epoch_id,
            snapshot_merkle_root: state_root.state_root.snapshot_root,
            intermediate_epoch_id: state_root.aux_info.intermediate_epoch_id,
            intermediate_trie_root_merkle: state_root
                .state_root
                .intermediate_delta_root,
            maybe_intermediate_mpt_key_padding: state_root
                .aux_info
                .maybe_intermediate_mpt_key_padding
                .clone(),
            epoch_id: *epoch_id,
            delta_mpt_key_padding: state_root
                .aux_info
                .delta_mpt_key_padding
                .clone(),
            maybe_delta_trie_height: None,
            maybe_height: None,
        }
    }
}

use crate::{impls::errors::*, state::StateTrait, StateRootWithAuxInfo};
use cfx_types::Space;
use primitives::{
    DeltaMptKeyPadding, EpochId, MerkleHash, GENESIS_DELTA_MPT_KEY_PADDING,
    MERKLE_NULL_NODE, NULL_EPOCH,
};
use std::sync::Arc;