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
142
//! A caching and checkpoint layer built upon semantically meaningful database
//! interfaces, providing interfaces and logics for managing accounts and global
//! statistics to the execution engine.

/// Contract Manager: Responsible for creating and deleting contract objects.
mod contract_manager;

/// Implements access functions for the basic fields (e.g., balance, nonce) of
/// `State`.
mod basic_fields;

/// Cache Layer: Implements a read-through write-back cache logic and provides
/// interfaces for reading and writing account data. It also handles the logic
/// for loading extension fields of an account.
mod cache_layer;

/// Checkpoints: Defines the account entry type within checkpoint layers and
/// implements checkpoint maintenance logic.
mod checkpoints;

/// Implements functions for the storage collateral of `State`.
mod collateral;

/// Implements functions for committing `State` changes to db.
mod commit;

/// Implements access functions global statistic variables of `State`.
mod global_statistics;

mod warm;

/// Implements functions for the PoS rewarding of `State`.
mod pos;

mod save;

/// Implements functions for the sponsorship mechanism of `State`.
mod sponsor;

/// Implements functions for the staking mechanism of `State`.
mod staking;

/// Implements access functions for the account storage entries of `State`.
mod storage_entry;

mod reward;

mod state_override;

#[cfg(test)]
mod tests;

pub use self::{
    collateral::{initialize_cip107, settle_collateral_for_all},
    commit::StateCommitResult,
    pos::{distribute_pos_interest, update_pos_status},
    reward::initialize_cip137,
    sponsor::COMMISSION_PRIVILEGE_SPECIAL_KEY,
    staking::initialize_or_update_dao_voted_params,
};
#[cfg(test)]
pub use tests::{get_state_by_epoch_id, get_state_for_genesis_write};

use self::checkpoints::CheckpointLayer;
use super::{
    checkpoints::LazyDiscardedVec,
    global_stat::GlobalStat,
    overlay_account::{
        AccountEntry, AccountEntryWithWarm, OverlayAccount, RequireFields,
    },
};
use crate::substate::Substate;
use cfx_statedb::{Result as DbResult, StateDbExt, StateDbGeneric as StateDb};
use cfx_types::{AddressWithSpace, H256};
use parking_lot::RwLock;
use std::collections::{BTreeSet, HashMap, HashSet};

/// A caching and checkpoint layer built upon semantically meaningful database
/// interfaces, providing interfaces and logics for managing accounts and global
/// statistics to the execution engine.
pub struct State {
    /// The backend database
    pub(super) db: StateDb,

    /// Caches for the account entries
    ///
    /// WARNING: Don't delete cache entries outside of `State::commit`, unless
    /// you are familiar with checkpoint maintenance.
    pub cache: RwLock<HashMap<AddressWithSpace, AccountEntryWithWarm>>,

    pub committed_cache: HashMap<AddressWithSpace, AccountEntry>,
    tx_access_list: Option<HashMap<AddressWithSpace, HashSet<H256>>>,

    /// In-memory global statistic variables.
    // TODO: try not to make it special?
    global_stat: GlobalStat,

    /// Checkpoint layers for the account entries
    // TODO: it seems `RwLock` is not necessary here. But we need to change the
    // signature of `write_account` from `&self` to `&mut self` first
    checkpoints: RwLock<LazyDiscardedVec<CheckpointLayer>>,
}

impl State {
    pub fn new(db: StateDb) -> DbResult<Self> {
        let initialized = db.is_initialized()?;

        let world_stat = if initialized {
            GlobalStat::loaded(&db)?
        } else {
            GlobalStat::assert_non_inited(&db)?;
            GlobalStat::new()
        };

        Ok(State {
            db,
            cache: Default::default(),
            committed_cache: Default::default(),
            checkpoints: Default::default(),
            tx_access_list: None,
            global_stat: world_stat,
        })
    }

    pub fn prefetch_accounts(
        &mut self, addresses: BTreeSet<AddressWithSpace>,
        pool: &rayon::ThreadPool,
    ) -> DbResult<()> {
        use rayon::prelude::*;

        pool.install(|| {
            addresses
                .into_par_iter()
                .map(|addr| self.prefetch(&addr, RequireFields::Code))
        })
        .collect::<DbResult<()>>()?;

        assert!(self.committed_cache.is_empty());
        self.commit_cache(false);
        Ok(())
    }
}