storage_interface/
lib.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 anyhow::Result;
9use diem_crypto::{hash::SPARSE_MERKLE_PLACEHOLDER_HASH, HashValue};
10use diem_types::{
11    committed_block::CommittedBlock,
12    contract_event::ContractEvent,
13    epoch_change::EpochChangeProof,
14    epoch_state::EpochState,
15    ledger_info::{
16        deserialize_ledger_info_unchecked, LedgerInfoWithSignatures,
17    },
18    proof::definition::LeafCount,
19    reward_distribution_event::RewardDistributionEventV2,
20    term_state::PosState,
21    transaction::{TransactionInfo, TransactionToCommit, Version},
22};
23use serde::{Deserialize, Serialize};
24use std::sync::Arc;
25use thiserror::Error;
26
27pub mod state_view;
28
29#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
30pub struct StartupInfo {
31    /// The latest ledger info.
32    /// This struct is only used locally, so loaded signatures must be valid.
33    #[serde(deserialize_with = "deserialize_ledger_info_unchecked")]
34    pub latest_ledger_info: LedgerInfoWithSignatures,
35    /// If the above ledger info doesn't carry a validator set, the latest
36    /// validator set. Otherwise `None`.
37    pub latest_epoch_state: Option<EpochState>,
38    pub committed_tree_state: TreeState,
39    pub synced_tree_state: Option<TreeState>,
40
41    pub committed_pos_state: PosState,
42}
43
44impl StartupInfo {
45    pub fn new(
46        latest_ledger_info: LedgerInfoWithSignatures,
47        latest_epoch_state: Option<EpochState>,
48        committed_tree_state: TreeState, synced_tree_state: Option<TreeState>,
49        committed_pos_state: PosState,
50    ) -> Self {
51        Self {
52            latest_ledger_info,
53            latest_epoch_state,
54            committed_tree_state,
55            synced_tree_state,
56            committed_pos_state,
57        }
58    }
59
60    #[cfg(any(feature = "fuzzing"))]
61    pub fn new_for_testing() -> Self {
62        use diem_types::on_chain_config::ValidatorSet;
63
64        let latest_ledger_info = LedgerInfoWithSignatures::genesis(
65            HashValue::zero(),
66            ValidatorSet::empty(),
67        );
68        let latest_epoch_state = None;
69        let committed_tree_state = TreeState {
70            num_transactions: 0,
71            ledger_frozen_subtree_hashes: Vec::new(),
72            account_state_root_hash: *SPARSE_MERKLE_PLACEHOLDER_HASH,
73        };
74        let synced_tree_state = None;
75
76        Self {
77            latest_ledger_info,
78            latest_epoch_state,
79            committed_tree_state,
80            synced_tree_state,
81        }
82    }
83
84    pub fn get_epoch_state(&self) -> &EpochState {
85        self.latest_ledger_info
86            .ledger_info()
87            .next_epoch_state()
88            .unwrap_or_else(|| {
89                self.latest_epoch_state
90                    .as_ref()
91                    .expect("EpochState must exist")
92            })
93    }
94}
95
96#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
97pub struct TreeState {
98    pub num_transactions: LeafCount,
99    pub ledger_frozen_subtree_hashes: Vec<HashValue>,
100    pub account_state_root_hash: HashValue,
101}
102
103impl TreeState {
104    pub fn new(
105        num_transactions: LeafCount,
106        ledger_frozen_subtree_hashes: Vec<HashValue>,
107        account_state_root_hash: HashValue,
108    ) -> Self {
109        Self {
110            num_transactions,
111            ledger_frozen_subtree_hashes,
112            account_state_root_hash,
113        }
114    }
115
116    pub fn describe(&self) -> &'static str {
117        if self.num_transactions != 0 {
118            "DB has been bootstrapped."
119        } else if self.account_state_root_hash
120            != *SPARSE_MERKLE_PLACEHOLDER_HASH
121        {
122            "DB has no transaction, but a non-empty pre-genesis state."
123        } else {
124            "DB is empty, has no transaction or state."
125        }
126    }
127}
128
129#[derive(Debug, Deserialize, Error, PartialEq, Serialize)]
130pub enum Error {
131    #[error("Service error: {:?}", error)]
132    ServiceError { error: String },
133
134    #[error("Serialization error: {0}")]
135    SerializationError(String),
136}
137
138impl From<anyhow::Error> for Error {
139    fn from(error: anyhow::Error) -> Self {
140        Self::ServiceError {
141            error: format!("{}", error),
142        }
143    }
144}
145
146impl From<bcs::Error> for Error {
147    fn from(error: bcs::Error) -> Self {
148        Self::SerializationError(format!("{}", error))
149    }
150}
151
152#[derive(Clone, Copy, Eq, PartialEq)]
153pub enum Order {
154    Ascending,
155    Descending,
156}
157
158/// Trait that is implemented by a DB that supports certain public (to client)
159/// read APIs expected of a Diem DB
160pub trait DbReader: Send + Sync {
161    /// See [`DiemDB::get_epoch_ending_ledger_infos`].
162    ///
163    /// [`DiemDB::get_epoch_ending_ledger_infos`]:
164    /// ../pos-ledger-db/struct.DiemDB.html#method.get_epoch_ending_ledger_infos
165    fn get_epoch_ending_ledger_infos(
166        &self, start_epoch: u64, end_epoch: u64,
167    ) -> Result<EpochChangeProof>;
168
169    /// See [`DiemDB::get_block_timestamp`].
170    ///
171    /// [`DiemDB::get_block_timestamp`]:
172    /// ../pos-ledger-db/struct.DiemDB.html#method.get_block_timestamp
173    fn get_block_timestamp(&self, version: u64) -> Result<u64>;
174
175    /// Returns the latest ledger info.
176    fn get_latest_ledger_info(&self) -> Result<LedgerInfoWithSignatures>;
177
178    /// Returns the latest ledger info.
179    fn get_latest_version(&self) -> Result<Version> {
180        Ok(self.get_latest_ledger_info()?.ledger_info().version())
181    }
182
183    /// Returns the latest version and committed block timestamp
184    fn get_latest_commit_metadata(&self) -> Result<(Version, u64)> {
185        let ledger_info_with_sig = self.get_latest_ledger_info()?;
186        let ledger_info = ledger_info_with_sig.ledger_info();
187        Ok((ledger_info.version(), ledger_info.timestamp_usecs()))
188    }
189
190    /// Gets information needed from storage during the main node startup.
191    /// See [`DiemDB::get_startup_info`].
192    ///
193    /// [`DiemDB::get_startup_info`]:
194    /// ../pos-ledger-db/struct.DiemDB.html#method.get_startup_info
195    fn get_startup_info(
196        &self, need_pos_state: bool,
197    ) -> Result<Option<StartupInfo>>;
198
199    /// Gets the latest TreeState no matter if db has been bootstrapped.
200    /// Used by the Db-bootstrapper.
201    fn get_latest_tree_state(&self) -> Result<TreeState>;
202
203    /// Get the ledger info of the epoch that `known_version` belongs to.
204    fn get_epoch_ending_ledger_info(
205        &self, known_version: u64,
206    ) -> Result<LedgerInfoWithSignatures>;
207
208    /// Gets the latest transaction info.
209    /// N.B. Unlike get_startup_info(), even if the db is not bootstrapped, this
210    /// can return `Some` -- those from a db-restore run.
211    fn get_latest_transaction_info_option(
212        &self,
213    ) -> Result<Option<(Version, TransactionInfo)>> {
214        unimplemented!()
215    }
216
217    fn get_pos_state(&self, _block_id: &HashValue) -> Result<PosState> {
218        unimplemented!()
219    }
220
221    fn get_latest_pos_state(&self) -> Arc<PosState> { unimplemented!() }
222}
223
224/// Trait that is implemented by a DB that supports certain public (to client)
225/// write APIs expected of a Diem DB. This adds write APIs to DbReader.
226pub trait DbWriter: Send + Sync {
227    /// Persist transactions. Called by the executor module when either syncing
228    /// nodes or committing blocks during normal operation.
229    /// See [`DiemDB::save_transactions`].
230    ///
231    /// [`DiemDB::save_transactions`]:
232    /// ../pos-ledger-db/struct.DiemDB.html#method.save_transactions
233    fn save_transactions(
234        &self, txns_to_commit: &[TransactionToCommit], first_version: Version,
235        ledger_info_with_sigs: Option<&LedgerInfoWithSignatures>,
236        pos_state: Option<PosState>, committed_blocks: Vec<CommittedBlock>,
237        ledger_infos_with_voted_block: Vec<(
238            HashValue,
239            LedgerInfoWithSignatures,
240        )>,
241    ) -> Result<()>;
242
243    fn save_reward_event(
244        &self, epoch: u64, event: &RewardDistributionEventV2,
245    ) -> Result<()>;
246
247    fn delete_pos_state_by_block(&self, block_id: &HashValue) -> Result<()>;
248}
249
250#[derive(Clone)]
251pub struct DbReaderWriter {
252    pub reader: Arc<dyn DbReader>,
253    pub writer: Arc<dyn DbWriter>,
254}
255
256impl DbReaderWriter {
257    pub fn new<D: 'static + DbReader + DbWriter>(db: D) -> Self {
258        let reader = Arc::new(db);
259        let writer = Arc::clone(&reader);
260
261        Self { reader, writer }
262    }
263
264    pub fn from_arc<D: 'static + DbReader + DbWriter>(arc_db: Arc<D>) -> Self {
265        let reader = Arc::clone(&arc_db);
266        let writer = Arc::clone(&arc_db);
267
268        Self { reader, writer }
269    }
270
271    pub fn wrap<D: 'static + DbReader + DbWriter>(db: D) -> (Arc<D>, Self) {
272        let arc_db = Arc::new(db);
273        (Arc::clone(&arc_db), Self::from_arc(arc_db))
274    }
275}
276
277impl<D> From<D> for DbReaderWriter
278where D: 'static + DbReader + DbWriter
279{
280    fn from(db: D) -> Self { Self::new(db) }
281}
282
283pub trait DBReaderForPoW: Send + Sync + DbReader {
284    fn get_latest_ledger_info_option(&self)
285        -> Option<LedgerInfoWithSignatures>;
286
287    /// TODO(lpl): It's possible to use round number?
288    fn get_block_ledger_info(
289        &self, consensus_block_id: &HashValue,
290    ) -> Result<LedgerInfoWithSignatures>;
291
292    fn get_events_by_version(
293        &self, start_version: u64, end_version: u64,
294    ) -> Result<Vec<ContractEvent>>;
295
296    fn get_epoch_ending_blocks(
297        &self, start_epoch: u64, end_epoch: u64,
298    ) -> Result<Vec<HashValue>>;
299
300    fn get_reward_event(&self, epoch: u64)
301        -> Result<RewardDistributionEventV2>;
302
303    fn get_committed_block_by_hash(
304        &self, block_hash: &HashValue,
305    ) -> Result<CommittedBlock>;
306
307    fn get_committed_block_hash_by_view(&self, view: u64) -> Result<HashValue>;
308
309    fn get_ledger_info_by_voted_block(
310        &self, block_id: &HashValue,
311    ) -> Result<LedgerInfoWithSignatures>;
312
313    fn get_block_hash_by_epoch_and_round(
314        &self, epoch: u64, round: u64,
315    ) -> Result<HashValue>;
316}