storage_interface/
state_view.rs

1// Copyright (c) The Diem Core Contributors
2// SPDX-License-Identifier: Apache-2.0
3
4// Copyright 2021 Conflux Foundation. All rights reserved.
5// Conflux is free software and distributed under GNU General Public License.
6// See http://www.gnu.org/licenses/
7
8use crate::DbReader;
9use anyhow::{format_err, Result};
10use diem_crypto::{hash::SPARSE_MERKLE_PLACEHOLDER_HASH, HashValue};
11use diem_state_view::{StateView, StateViewId};
12use diem_types::{
13    access_path::AccessPath,
14    account_address::{AccountAddress, HashAccountAddress},
15    account_state::AccountState,
16    account_state_blob::AccountStateBlob,
17    proof::SparseMerkleProof,
18    term_state::PosState,
19    transaction::{Version, PRE_GENESIS_VERSION},
20};
21use parking_lot::RwLock;
22use scratchpad::{AccountStatus, SparseMerkleTree};
23use std::{
24    collections::{hash_map::Entry, HashMap},
25    convert::TryInto,
26    sync::Arc,
27};
28
29/// `VerifiedStateView` is like a snapshot of the global state comprised of
30/// state view at two levels, persistent storage and memory.
31pub struct VerifiedStateView<'a> {
32    /// For logging and debugging purpose, identifies what this view is for.
33    id: StateViewId,
34
35    /// A gateway implementing persistent storage interface, which can be a RPC
36    /// client or direct accessor.
37    reader: Arc<dyn DbReader>,
38
39    /// The most recent version in persistent storage.
40    latest_persistent_version: Option<Version>,
41
42    /// The most recent state root hash in persistent storage.
43    latest_persistent_state_root: HashValue,
44
45    /// The in-momery version of sparse Merkle tree of which the states haven't
46    /// been committed.
47    speculative_state: &'a SparseMerkleTree<AccountStateBlob>,
48
49    /// The cache of verified account states from `reader` and
50    /// `speculative_state_view`, represented by a hashmap with an account
51    /// address as key and a pair of an ordered account state map and an
52    /// optional account state proof as value. When the VM queries an
53    /// `access_path`, this cache will first check whether `reader_cache` is
54    /// hit. If hit, it will return the corresponding value of that
55    /// `access_path`; otherwise, the account state will be loaded into the
56    /// cache from scratchpad or persistent storage in order as a
57    /// deserialized ordered map and then be returned. If the VM queries this
58    /// account again, the cached data can be read directly without
59    /// bothering storage layer. The proofs in cache are needed by
60    /// ScratchPad after VM execution to construct an in-memory sparse Merkle
61    /// tree.
62    /// ```text
63    ///                      +----------------------------+
64    ///                      | In-memory SparseMerkleTree <------+
65    ///                      +-------------^--------------+      |
66    ///                                    |                     |
67    ///                                write sets                |
68    ///                                    |          cached account state map
69    ///                            +-------+-------+           proof
70    ///                            |      V M      |             |
71    ///                            +-------^-------+             |
72    ///                                    |                     |
73    ///                      value of `account_address/path`     |
74    ///                                    |                     |
75    ///        +---------------------------+---------------------+-------+
76    ///        | +-------------------------+---------------------+-----+ |
77    ///        | |    account_to_state_cache, account_to_proof_cache   | |
78    ///        | +---------------^---------------------------^---------+ |
79    ///        |                 |                           |           |
80    ///        |     account state blob only        account state blob   |
81    ///        |                 |                         proof         |
82    ///        |                 |                           |           |
83    ///        | +---------------+--------------+ +----------+---------+ |
84    ///        | |      speculative_state       | |       reader       | |
85    ///        | +------------------------------+ +--------------------+ |
86    ///        +---------------------------------------------------------+
87    /// ```
88    account_to_state_cache: RwLock<HashMap<AccountAddress, AccountState>>,
89    account_to_proof_cache:
90        RwLock<HashMap<HashValue, SparseMerkleProof<AccountStateBlob>>>,
91
92    pos_state: PosState,
93}
94
95impl<'a> VerifiedStateView<'a> {
96    /// Constructs a [`VerifiedStateView`] with persistent state view
97    /// represented by `latest_persistent_state_root` plus a storage reader,
98    /// and the in-memory speculative state on top of it represented by
99    /// `speculative_state`.
100    pub fn new(
101        id: StateViewId, reader: Arc<dyn DbReader>,
102        latest_persistent_version: Option<Version>,
103        latest_persistent_state_root: HashValue,
104        speculative_state: &'a SparseMerkleTree<AccountStateBlob>,
105        pos_state: PosState,
106    ) -> Self {
107        // Hack: When there's no transaction in the db but state tree root hash
108        // is not the placeholder hash, it implies that there's
109        // pre-genesis state present.
110        let latest_persistent_version =
111            latest_persistent_version.or_else(|| {
112                if latest_persistent_state_root
113                    != *SPARSE_MERKLE_PLACEHOLDER_HASH
114                {
115                    Some(PRE_GENESIS_VERSION)
116                } else {
117                    None
118                }
119            });
120        Self {
121            id,
122            reader,
123            latest_persistent_version,
124            latest_persistent_state_root,
125            speculative_state,
126            account_to_state_cache: RwLock::new(HashMap::new()),
127            account_to_proof_cache: RwLock::new(HashMap::new()),
128            pos_state,
129        }
130    }
131}
132
133impl<'a> From<VerifiedStateView<'a>>
134    for (
135        HashMap<AccountAddress, AccountState>,
136        HashMap<HashValue, SparseMerkleProof<AccountStateBlob>>,
137    )
138{
139    fn from(view: VerifiedStateView<'a>) -> Self {
140        (
141            view.account_to_state_cache.into_inner(),
142            view.account_to_proof_cache.into_inner(),
143        )
144    }
145}
146
147impl<'a> StateView for VerifiedStateView<'a> {
148    fn id(&self) -> StateViewId { self.id }
149
150    fn get(&self, access_path: &AccessPath) -> Result<Option<Vec<u8>>> {
151        let address = access_path.address;
152        let path = &access_path.path;
153
154        // Lock for read first:
155        if let Some(contents) = self.account_to_state_cache.read().get(&address)
156        {
157            return Ok(contents.get(path).cloned());
158        }
159
160        // Do most of the work outside the write lock.
161        let address_hash = address.hash();
162        let account_blob_option = match self.speculative_state.get(address_hash)
163        {
164            AccountStatus::ExistsInScratchPad(blob) => Some(blob),
165            AccountStatus::DoesNotExist => None,
166            // No matter it is in db or unknown, we have to query from db since
167            // even the former case, we don't have the blob data but
168            // only its hash.
169            AccountStatus::ExistsInDB | AccountStatus::Unknown => {
170                let (blob, proof) = match self.latest_persistent_version {
171                    Some(version) => {
172                        self.reader.get_account_state_with_proof_by_version(
173                            address, version,
174                        )?
175                    }
176                    None => (None, SparseMerkleProof::new(None, vec![])),
177                };
178                proof
179                    .verify(
180                        self.latest_persistent_state_root,
181                        address.hash(),
182                        blob.as_ref(),
183                    )
184                    .map_err(|err| {
185                        format_err!(
186                            "Proof is invalid for address {:?} with state root hash {:?}: {}",
187                            address,
188                            self.latest_persistent_state_root,
189                            err
190                        )
191                    })?;
192                assert!(self
193                    .account_to_proof_cache
194                    .write()
195                    .insert(address_hash, proof)
196                    .is_none());
197                blob
198            }
199        };
200
201        // Now enter the locked region, and write if still empty.
202        let new_account_blob = account_blob_option
203            .as_ref()
204            .map(TryInto::try_into)
205            .transpose()?
206            .unwrap_or_default();
207
208        match self.account_to_state_cache.write().entry(address) {
209            Entry::Occupied(occupied) => Ok(occupied.get().get(path).cloned()),
210            Entry::Vacant(vacant) => {
211                Ok(vacant.insert(new_account_blob).get(path).cloned())
212            }
213        }
214    }
215
216    fn multi_get(
217        &self, _access_paths: &[AccessPath],
218    ) -> Result<Vec<Option<Vec<u8>>>> {
219        unimplemented!();
220    }
221
222    fn is_genesis(&self) -> bool { self.latest_persistent_version.is_none() }
223
224    fn pos_state(&self) -> &PosState { &self.pos_state }
225}