cfx_executor/state/state_object/
commit.rsuse crate::state::overlay_account::AccountEntry;
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 {
    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,
        })
    }
    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)
    }
    pub 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());
        self.commit_cache(false);
        let cache_items = self.committed_cache.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<()> {
        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 {
    pub fn commit_cache(&mut self, retain_transient_storage: bool) {
        assert!(self.no_checkpoint());
        for (addr, mut account) in self.cache.get_mut().drain() {
            if let AccountEntry::Cached(ref mut acc, dirty) = account.entry {
                acc.commit_cache(retain_transient_storage, dirty);
            }
            self.committed_cache.insert(addr, account.entry);
        }
    }
}
impl State {
    #[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(())
    }
}