cfx_executor/state/state_object/
commit.rs

1use crate::state::overlay_account::AccountEntry;
2
3use super::State;
4use cfx_internal_common::{
5    debug::ComputeEpochDebugRecord, StateRootWithAuxInfo,
6};
7use cfx_statedb::{access_mode, Result as DbResult};
8use cfx_types::AddressWithSpace;
9use primitives::{Account, EpochId, StorageKey};
10
11pub struct StateCommitResult {
12    pub state_root: StateRootWithAuxInfo,
13    pub accounts_for_txpool: Vec<Account>,
14}
15
16impl State {
17    /// Commit everything to the storage.
18    pub fn commit(
19        mut self, epoch_id: EpochId,
20        mut debug_record: Option<&mut ComputeEpochDebugRecord>,
21    ) -> DbResult<StateCommitResult> {
22        debug!("Commit epoch[{}]", epoch_id);
23
24        let accounts_for_txpool =
25            self.apply_changes_to_statedb(debug_record.as_deref_mut())?;
26        let state_root = self.db.commit(epoch_id, debug_record)?;
27        Ok(StateCommitResult {
28            state_root,
29            accounts_for_txpool,
30        })
31    }
32
33    /// Commit to the statedb and compute state root. Only called in the genesis
34    pub fn compute_state_root_for_genesis(
35        &mut self, mut debug_record: Option<&mut ComputeEpochDebugRecord>,
36    ) -> DbResult<StateRootWithAuxInfo> {
37        self.apply_changes_to_statedb(debug_record.as_deref_mut())?;
38        self.db.compute_state_root(debug_record)
39    }
40
41    /// Apply changes for the accounts and global variables to the statedb.
42    pub fn apply_changes_to_statedb(
43        &mut self, mut debug_record: Option<&mut ComputeEpochDebugRecord>,
44    ) -> DbResult<Vec<Account>> {
45        debug!("state.commit_changes");
46
47        let accounts_for_txpool =
48            self.commit_dirty_accounts(debug_record.as_deref_mut())?;
49        self.global_stat.commit(&mut self.db, debug_record)?;
50        Ok(accounts_for_txpool)
51    }
52
53    fn commit_dirty_accounts(
54        &mut self, mut debug_record: Option<&mut ComputeEpochDebugRecord>,
55    ) -> DbResult<Vec<Account>> {
56        assert!(self.no_checkpoint());
57
58        self.commit_cache(false);
59        let cache_items = self.committed_cache.drain();
60        let mut to_commit_accounts = cache_items
61            .filter_map(|(_, acc)| acc.into_to_commit_account())
62            .collect::<Vec<_>>();
63        to_commit_accounts.sort_by(|a, b| a.address().cmp(b.address()));
64
65        let mut accounts_to_notify = vec![];
66
67        for account in to_commit_accounts.into_iter() {
68            let address = *account.address();
69
70            if account.pending_db_clear() {
71                self.recycle_storage(
72                    vec![address],
73                    debug_record.as_deref_mut(),
74                )?;
75            }
76
77            if !account.removed_without_update() {
78                accounts_to_notify.push(account.as_account());
79                account.commit(
80                    &mut self.db,
81                    &address,
82                    debug_record.as_deref_mut(),
83                )?;
84            }
85        }
86        Ok(accounts_to_notify)
87    }
88
89    fn recycle_storage(
90        &mut self, killed_addresses: Vec<AddressWithSpace>,
91        mut debug_record: Option<&mut ComputeEpochDebugRecord>,
92    ) -> DbResult<()> {
93        // TODO: Think about kill_dust and collateral refund.
94        for address in &killed_addresses {
95            self.db.delete_all::<access_mode::Write>(
96                StorageKey::new_storage_root_key(&address.address)
97                    .with_space(address.space),
98                debug_record.as_deref_mut(),
99            )?;
100            self.db.delete_all::<access_mode::Write>(
101                StorageKey::new_code_root_key(&address.address)
102                    .with_space(address.space),
103                debug_record.as_deref_mut(),
104            )?;
105            self.db.delete(
106                StorageKey::new_account_key(&address.address)
107                    .with_space(address.space),
108                debug_record.as_deref_mut(),
109            )?;
110            self.db.delete(
111                StorageKey::new_deposit_list_key(&address.address)
112                    .with_space(address.space),
113                debug_record.as_deref_mut(),
114            )?;
115            self.db.delete(
116                StorageKey::new_vote_list_key(&address.address)
117                    .with_space(address.space),
118                debug_record.as_deref_mut(),
119            )?;
120        }
121        Ok(())
122    }
123}
124
125impl State {
126    pub fn commit_cache(&mut self, retain_transient_storage: bool) {
127        assert!(self.no_checkpoint());
128        for (addr, mut account) in self.cache.get_mut().drain() {
129            if let AccountEntry::Cached(ref mut acc, dirty) = account.entry {
130                acc.commit_cache(retain_transient_storage, dirty);
131            }
132            self.committed_cache.insert(addr, account.entry);
133        }
134    }
135}
136
137impl State {
138    // Some test code will reuse state incorrectly, so we implement a version
139    // which does not take ownership when committing.
140    #[cfg(test)]
141    pub fn commit_for_test(&mut self, epoch_id: EpochId) -> DbResult<()> {
142        self.apply_changes_to_statedb(None)?;
143        self.db.commit(epoch_id, None)?;
144        Ok(())
145    }
146}