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
use super::State;
use cfx_internal_common::{
    debug::ComputeEpochDebugRecord, StateRootWithAuxInfo,
};
use cfx_statedb::{access_mode, Result as DbResult};
use cfx_types::AddressWithSpace;
use primitives::{Account, EpochId, StorageKey};

pub struct StateCommitResult {
    pub state_root: StateRootWithAuxInfo,
    pub accounts_for_txpool: Vec<Account>,
}

impl State {
    /// Commit everything to the storage.
    pub fn commit(
        mut self, epoch_id: EpochId,
        mut debug_record: Option<&mut ComputeEpochDebugRecord>,
    ) -> DbResult<StateCommitResult> {
        debug!("Commit epoch[{}]", epoch_id);

        let accounts_for_txpool =
            self.apply_changes_to_statedb(debug_record.as_deref_mut())?;
        let state_root = self.db.commit(epoch_id, debug_record)?;
        Ok(StateCommitResult {
            state_root,
            accounts_for_txpool,
        })
    }

    /// Commit to the statedb and compute state root. Only called in the genesis
    pub fn compute_state_root_for_genesis(
        &mut self, mut debug_record: Option<&mut ComputeEpochDebugRecord>,
    ) -> DbResult<StateRootWithAuxInfo> {
        self.apply_changes_to_statedb(debug_record.as_deref_mut())?;
        self.db.compute_state_root(debug_record)
    }

    /// Apply changes for the accounts and global variables to the statedb.
    fn apply_changes_to_statedb(
        &mut self, mut debug_record: Option<&mut ComputeEpochDebugRecord>,
    ) -> DbResult<Vec<Account>> {
        debug!("state.commit_changes");

        let accounts_for_txpool =
            self.commit_dirty_accounts(debug_record.as_deref_mut())?;
        self.global_stat.commit(&mut self.db, debug_record)?;
        Ok(accounts_for_txpool)
    }

    fn commit_dirty_accounts(
        &mut self, mut debug_record: Option<&mut ComputeEpochDebugRecord>,
    ) -> DbResult<Vec<Account>> {
        assert!(self.no_checkpoint());

        let cache_items = self.cache.get_mut().drain();
        let mut to_commit_accounts = cache_items
            .filter_map(|(_, acc)| acc.into_to_commit_account())
            .collect::<Vec<_>>();
        to_commit_accounts.sort_by(|a, b| a.address().cmp(b.address()));

        let mut accounts_to_notify = vec![];

        for account in to_commit_accounts.into_iter() {
            let address = *account.address();

            if account.pending_db_clear() {
                self.recycle_storage(
                    vec![address],
                    debug_record.as_deref_mut(),
                )?;
            }

            if !account.removed_without_update() {
                accounts_to_notify.push(account.as_account());
                account.commit(
                    &mut self.db,
                    &address,
                    debug_record.as_deref_mut(),
                )?;
            }
        }
        Ok(accounts_to_notify)
    }

    fn recycle_storage(
        &mut self, killed_addresses: Vec<AddressWithSpace>,
        mut debug_record: Option<&mut ComputeEpochDebugRecord>,
    ) -> DbResult<()> {
        // TODO: Think about kill_dust and collateral refund.
        for address in &killed_addresses {
            self.db.delete_all::<access_mode::Write>(
                StorageKey::new_storage_root_key(&address.address)
                    .with_space(address.space),
                debug_record.as_deref_mut(),
            )?;
            self.db.delete_all::<access_mode::Write>(
                StorageKey::new_code_root_key(&address.address)
                    .with_space(address.space),
                debug_record.as_deref_mut(),
            )?;
            self.db.delete(
                StorageKey::new_account_key(&address.address)
                    .with_space(address.space),
                debug_record.as_deref_mut(),
            )?;
            self.db.delete(
                StorageKey::new_deposit_list_key(&address.address)
                    .with_space(address.space),
                debug_record.as_deref_mut(),
            )?;
            self.db.delete(
                StorageKey::new_vote_list_key(&address.address)
                    .with_space(address.space),
                debug_record.as_deref_mut(),
            )?;
        }
        Ok(())
    }
}

impl State {
    // Some test code will reuse state incorrectly, so we implement a version
    // which does not take ownership when committing.
    #[cfg(test)]
    pub fn commit_for_test(&mut self, epoch_id: EpochId) -> DbResult<()> {
        self.apply_changes_to_statedb(None)?;
        self.db.commit(epoch_id, None)?;
        Ok(())
    }
}