cfx_executor/state/state_object/
commit.rs1use 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 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 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 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 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 #[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}