cfx_executor/state/state_object/
cache_layer.rs

1//! Cache Layer in State: Implements a read-through write-back cache logic and
2//! provides interfaces for reading and writing account data. It also handles
3//! the logic for loading extension fields of an account.
4
5use super::{AccountEntry, OverlayAccount, RequireFields, State};
6use crate::{state::overlay_account::AccountEntryWithWarm, unwrap_or_return};
7use cfx_statedb::{
8    Error as DbErrorKind, Result as DbResult, StateDb, StateDbExt,
9};
10use cfx_types::{Address, AddressSpaceUtil, AddressWithSpace, U256};
11use parking_lot::{
12    MappedRwLockReadGuard, MappedRwLockWriteGuard, RwLockReadGuard,
13    RwLockWriteGuard,
14};
15use std::collections::{
16    hash_map::Entry::{Occupied, Vacant},
17    HashMap,
18};
19
20pub type AccountReadGuard<'a> = MappedRwLockReadGuard<'a, OverlayAccount>;
21pub type AccountWriteGuard<'a> = MappedRwLockWriteGuard<'a, OverlayAccount>;
22
23impl State {
24    /// A convenience function of `read_account_ext_lock`
25    pub(super) fn read_account_lock(
26        &self, address: &AddressWithSpace,
27    ) -> DbResult<Option<AccountReadGuard<'_>>> {
28        self.read_account_ext_lock(address, RequireFields::None)
29    }
30
31    /// A convenience function of `read_account_ext_lock`
32    pub(super) fn read_native_account_lock(
33        &self, address: &Address,
34    ) -> DbResult<Option<AccountReadGuard<'_>>> {
35        self.read_account_lock(&address.with_native_space())
36    }
37
38    /// Requests an immutable reference of an account through the cache by the
39    /// address and required, returning a reference with a read lock guard.
40    /// It returns `None` if the account doesn't exist.
41    pub(super) fn read_account_ext_lock(
42        &self, address: &AddressWithSpace, require: RequireFields,
43    ) -> DbResult<Option<AccountReadGuard<'_>>> {
44        let mut cache = self.cache.write();
45
46        let account_entry = Self::fetch_account_mut(
47            &mut cache,
48            &self.committed_cache,
49            &self.db,
50            address,
51            require,
52        )?;
53
54        self.copy_cache_entry_to_checkpoint(*address, account_entry);
55
56        Ok(if !account_entry.is_db_absent() {
57            Some(RwLockReadGuard::map(
58                RwLockWriteGuard::downgrade(cache),
59                |cache| cache.get(address).unwrap().account().unwrap(),
60            ))
61        } else {
62            None
63        })
64    }
65
66    /// Prefetch an account with required extension fields. This function does
67    /// not hold a write lock during loading db, enabling parallel prefetch.
68    pub(super) fn prefetch(
69        &self, address: &AddressWithSpace, require: RequireFields,
70    ) -> DbResult<()> {
71        // TODO: this logic seems useless, since the prefetch is always called
72        // on a newly inited state.
73        if let Some(account_entry) = self.cache.read().get(address) {
74            if let Some(account) = account_entry.account() {
75                if !account.should_load_ext_fields(require) {
76                    // Return if the account has been loaded and no more field
77                    // needs to be loaded
78                    return Ok(());
79                }
80            } else {
81                // Return if the account is known be absent in db.
82                return Ok(());
83            }
84        }
85
86        // Performance Consideration: If an account already exists but
87        // additional fields are requested, this implementation reloads
88        // the account, which may result in some performance loss.
89        // However, to ensure code clarity and maintainability, and to safeguard
90        // checkpoint logic integrity, we choose not to optimize this
91        // behavior. Currently, this case does not occur in the existing
92        // codebase.
93
94        // Load the account and insert to cache
95        let mut account_entry =
96            AccountEntry::new_loaded(self.db.get_account(address)?);
97        Self::load_account_ext_fields(require, &mut account_entry, &self.db)?;
98
99        // The prefetch phase's warm bit is not important because it will soon
100        // be written from the cache to the committed cache, which does not
101        // include the warm bit.
102        self.cache
103            .write()
104            .insert(*address, account_entry.with_warm(false));
105        Ok(())
106    }
107}
108
109impl State {
110    /// A convenience function of `write_account_ext_lock`
111    pub fn write_account_lock(
112        &self, address: &AddressWithSpace,
113    ) -> DbResult<AccountWriteGuard<'_>> {
114        self.write_account_ext_lock(address, RequireFields::None)
115    }
116
117    /// A convenience function of `write_account_ext_lock`
118    pub(super) fn write_native_account_lock(
119        &self, address: &Address,
120    ) -> DbResult<AccountWriteGuard<'_>> {
121        self.write_account_lock(&address.with_native_space())
122    }
123
124    /// Requests a mutable reference of an account through the cache by the
125    /// address and required, returning a reference with a write lock guard.
126    /// It asserts a fail if the account doesn't exist.
127    pub(super) fn write_account_ext_lock(
128        &self, address: &AddressWithSpace, require: RequireFields,
129    ) -> DbResult<AccountWriteGuard<'_>> {
130        fn no_account_is_an_error(
131            address: &AddressWithSpace,
132        ) -> DbResult<OverlayAccount> {
133            bail!(DbErrorKind::IncompleteDatabase(address.address));
134        }
135        self.write_account_inner(address, require, no_account_is_an_error)
136    }
137
138    /// Requests a mutable reference of an account through the cache by the
139    /// address, returning a reference with a write lock guard. It initiates a
140    /// new account if the account doesn't exist.
141    pub(super) fn write_account_or_new_lock(
142        &self, address: &AddressWithSpace,
143    ) -> DbResult<AccountWriteGuard<'_>> {
144        fn init_if_no_account(
145            address: &AddressWithSpace,
146        ) -> DbResult<OverlayAccount> {
147            // It is guaranteed that the address is valid.
148
149            // Note that it is possible to first send money to a pre-calculated
150            // contract address and then deploy contracts. So we are
151            // going to *allow* sending to a contract address and
152            // use new_basic() to create a *stub* there. Because the contract
153            // serialization is a super-set of the normal address
154            // serialization, this should just work.
155            Ok(OverlayAccount::new_basic(address, U256::zero()))
156        }
157        self.write_account_inner(
158            address,
159            RequireFields::None,
160            init_if_no_account,
161        )
162    }
163
164    /// Requests an account via a read-through cache, makes a checkpoint in
165    /// needed, handles "the account doesn't exist" by the passed in function,
166    /// and sets the dirty bit.
167    fn write_account_inner<F>(
168        &self, address: &AddressWithSpace, require: RequireFields, default: F,
169    ) -> DbResult<AccountWriteGuard<'_>>
170    where F: Fn(&AddressWithSpace) -> DbResult<OverlayAccount> {
171        let mut cache = self.cache.write();
172
173        let account_entry = Self::fetch_account_mut(
174            &mut cache,
175            &self.committed_cache,
176            &self.db,
177            address,
178            require,
179        )?;
180
181        // Save the value before modification into the checkpoint.
182        self.copy_cache_entry_to_checkpoint(*address, account_entry);
183
184        // Set the dirty flag in cache.
185        if let AccountEntry::Cached(_, dirty_bit) = &mut account_entry.entry {
186            *dirty_bit = true;
187        } else {
188            account_entry.entry = AccountEntry::new_dirty(default(address)?);
189        }
190
191        Ok(RwLockWriteGuard::map(cache, |c| {
192            c.get_mut(address)
193                .expect("Entry known to exist in the cache.")
194                .dirty_account_mut()
195                .expect("Required account must exist.")
196        }))
197    }
198}
199
200impl State {
201    /// Retrieves data using a read-through caching strategy and automatically
202    /// loads extension fields as required.
203    fn fetch_account_mut<'a>(
204        cache: &'a mut HashMap<AddressWithSpace, AccountEntryWithWarm>,
205        committed_cache: &'a HashMap<AddressWithSpace, AccountEntry>,
206        db: &StateDb, address: &AddressWithSpace, require: RequireFields,
207    ) -> DbResult<&'a mut AccountEntryWithWarm> {
208        let account_entry = match cache.entry(*address) {
209            Occupied(e) => e.into_mut(),
210            Vacant(e) => {
211                let entry = match committed_cache.get(address) {
212                    Some(committed) => committed.clone_from_committed_cache(),
213                    None => {
214                        let address = *e.key();
215                        AccountEntry::new_loaded(db.get_account(&address)?)
216                    }
217                };
218                // The item is set to "cold" by default when loading. After
219                // processing the checkpoint-related logic, it will be marked as
220                // "warm."
221                e.insert(entry.with_warm(false))
222            }
223        };
224        Self::load_account_ext_fields(require, &mut account_entry.entry, db)?;
225        Ok(account_entry)
226    }
227
228    /// Load required extension fields of an account as required.
229    fn load_account_ext_fields(
230        require: RequireFields, account_entry: &mut AccountEntry, db: &StateDb,
231    ) -> DbResult<()> {
232        let account = unwrap_or_return!(account_entry.account_mut(), Ok(()));
233
234        if !account.should_load_ext_fields(require) {
235            return Ok(());
236        }
237
238        match require {
239            RequireFields::None => Ok(()),
240            RequireFields::Code => account.cache_code(db),
241            RequireFields::DepositList => account.cache_ext_fields(
242                true,  /* cache_deposit_list */
243                false, /* cache_vote_list */
244                db,
245            ),
246            RequireFields::VoteStakeList => account.cache_ext_fields(
247                false, /* cache_deposit_list */
248                true,  /* cache_vote_list */
249                db,
250            ),
251        }
252    }
253}