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}