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