dusk_node/
database.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4//
5// Copyright (c) DUSK NETWORK. All rights reserved.
6
7use std::collections::HashSet;
8use std::path::Path;
9
10pub mod rocksdb;
11
12use anyhow::Result;
13use node_data::ledger::{
14    Block, Fault, Header, Label, SpendingId, SpentTransaction, Transaction,
15};
16use node_data::message::{payload, ConsensusHeader};
17use serde::{Deserialize, Serialize};
18
19pub struct LightBlock {
20    pub header: Header,
21    pub transactions_ids: Vec<[u8; 32]>,
22    pub faults_ids: Vec<[u8; 32]>,
23}
24
25pub trait DB: Send + Sync + 'static {
26    type P<'a>: Persist;
27
28    /// Creates or open a database located at this path.
29    ///
30    /// Panics if opening db or creating one fails.
31    fn create_or_open<T>(path: T, opts: DatabaseOptions) -> Self
32    where
33        T: AsRef<Path>;
34
35    /// Provides a managed execution of a read-only isolated transaction.
36    fn view<F, T>(&self, f: F) -> T
37    where
38        F: for<'a> FnOnce(&Self::P<'a>) -> T;
39
40    /// Provides a managed execution of a read-write atomic transaction.
41    ///
42    /// An atomic transaction is an indivisible and irreducible series of
43    /// database operations such that either all occur, or nothing occurs.
44    ///
45    /// Transaction commit will happen only if no error is returned by `fn`
46    /// and no panic is raised on `fn` execution.
47    fn update<F, T>(&self, f: F) -> Result<T>
48    where
49        F: for<'a> FnOnce(&mut Self::P<'a>) -> Result<T>;
50
51    fn update_dry_run<F, T>(&self, dry_run: bool, f: F) -> Result<T>
52    where
53        F: for<'a> FnOnce(&mut Self::P<'a>) -> Result<T>;
54
55    fn close(&mut self);
56}
57
58/// Implements both read-write and read-only transactions to DB.
59pub trait Ledger {
60    /// Read-write transactions
61    /// Returns disk footprint of the committed transaction
62    fn store_block(
63        &mut self,
64        header: &Header,
65        txs: &[SpentTransaction],
66        faults: &[Fault],
67        label: Label,
68    ) -> Result<usize>;
69
70    fn delete_block(&mut self, b: &Block) -> Result<()>;
71    fn block_header(&self, hash: &[u8]) -> Result<Option<Header>>;
72
73    fn light_block(&self, hash: &[u8]) -> Result<Option<LightBlock>>;
74
75    fn block(&self, hash: &[u8]) -> Result<Option<Block>>;
76    fn block_hash_by_height(&self, height: u64) -> Result<Option<[u8; 32]>>;
77    fn block_by_height(&self, height: u64) -> Result<Option<Block>>;
78
79    fn store_blob_data(&self, hash: &[u8; 32], data: Vec<u8>) -> Result<()>;
80    fn blob_data_by_hash(&self, hash: &[u8; 32]) -> Result<Option<Vec<u8>>>;
81    fn store_blobs_height(
82        &self,
83        block_height: u64,
84        blob_hashes: &[[u8; 32]],
85    ) -> Result<()>;
86    fn blobs_by_height(
87        &self,
88        block_height: u64,
89    ) -> Result<Option<Vec<[u8; 32]>>>;
90    fn delete_blobs_by_height(&self, block_height: u64) -> Result<()>;
91
92    fn block_exists(&self, hash: &[u8]) -> Result<bool>;
93
94    fn ledger_tx(&self, tx_id: &[u8]) -> Result<Option<SpentTransaction>>;
95    fn ledger_txs(
96        &self,
97        tx_ids: Vec<&[u8; 32]>,
98    ) -> Result<Vec<SpentTransaction>>;
99
100    fn ledger_tx_exists(&self, tx_id: &[u8]) -> Result<bool>;
101
102    fn block_label_by_height(
103        &self,
104        height: u64,
105    ) -> Result<Option<([u8; 32], Label)>>;
106
107    fn store_block_label(
108        &mut self,
109        height: u64,
110        hash: &[u8; 32],
111        label: Label,
112    ) -> Result<()>;
113
114    fn faults_by_block(&self, start_height: u64) -> Result<Vec<Fault>>;
115    fn faults(&self, faults_ids: &[[u8; 32]]) -> Result<Vec<Fault>>;
116
117    fn latest_block(&self) -> Result<LightBlock>;
118}
119
120pub trait ConsensusStorage {
121    /// Candidate Storage
122    fn store_candidate(&mut self, cm: Block) -> Result<()>;
123    fn candidate(&self, hash: &[u8]) -> Result<Option<Block>>;
124
125    /// Fetches a candidate block by lookup key (prev_block_hash, iteration).
126    fn candidate_by_iteration(
127        &self,
128        ch: &ConsensusHeader,
129    ) -> Result<Option<Block>>;
130
131    fn clear_candidates(&mut self) -> Result<()>;
132
133    fn delete_candidate<F>(&mut self, closure: F) -> Result<()>
134    where
135        F: FnOnce(u64) -> bool + std::marker::Copy;
136
137    fn count_candidates(&self) -> usize;
138
139    /// ValidationResult Storage
140    fn store_validation_result(
141        &mut self,
142        ch: &ConsensusHeader,
143        vr: &payload::ValidationResult,
144    ) -> Result<()>;
145
146    fn validation_result(
147        &self,
148        ch: &ConsensusHeader,
149    ) -> Result<Option<payload::ValidationResult>>;
150
151    fn clear_validation_results(&mut self) -> Result<()>;
152
153    fn delete_validation_results<F>(&mut self, closure: F) -> Result<()>
154    where
155        F: FnOnce([u8; 32]) -> bool + std::marker::Copy;
156
157    fn count_validation_results(&self) -> usize;
158}
159
160pub trait Mempool {
161    /// Adds a transaction to the mempool with a timestamp.
162    fn store_mempool_tx(
163        &mut self,
164        tx: &Transaction,
165        timestamp: u64,
166    ) -> Result<()>;
167
168    /// Gets a transaction from the mempool.
169    fn mempool_tx(&self, tx_id: [u8; 32]) -> Result<Option<Transaction>>;
170
171    /// Checks if a transaction exists in the mempool.
172    fn mempool_tx_exists(&self, tx_id: [u8; 32]) -> Result<bool>;
173
174    /// Deletes a transaction from the mempool.
175    ///
176    /// If `cascade` is true, all dependant transactions are deleted
177    ///
178    /// Return a vector with all the deleted tx_id
179    fn delete_mempool_tx(
180        &mut self,
181        tx_id: [u8; 32],
182        cascade: bool,
183    ) -> Result<Vec<[u8; 32]>>;
184
185    /// Get transactions hash from the mempool, searching by spendable ids
186    fn mempool_txs_by_spendable_ids(
187        &self,
188        n: &[SpendingId],
189    ) -> HashSet<[u8; 32]>;
190
191    /// Get an iterator over the mempool transactions sorted by gas price
192    fn mempool_txs_sorted_by_fee(
193        &self,
194    ) -> Box<dyn Iterator<Item = Transaction> + '_>;
195
196    /// Get an iterator over the mempool transactions hash by gas price
197    fn mempool_txs_ids_sorted_by_fee(
198        &self,
199    ) -> Box<dyn Iterator<Item = (u64, [u8; 32])> + '_>;
200
201    /// Get an iterator over the mempool transactions hash by gas price (asc)
202    fn mempool_txs_ids_sorted_by_low_fee(
203        &self,
204    ) -> Box<dyn Iterator<Item = (u64, [u8; 32])> + '_>;
205
206    /// Get all transactions hashes.
207    fn mempool_txs_ids(&self) -> Result<Vec<[u8; 32]>>;
208
209    /// Get all expired transactions.
210    fn mempool_expired_txs(&self, timestamp: u64) -> Result<Vec<[u8; 32]>>;
211
212    /// Number of persisted transactions
213    fn mempool_txs_count(&self) -> usize;
214}
215
216pub trait Metadata {
217    /// Assigns an value to a key in the Metadata CF
218    fn op_write<T: AsRef<[u8]>>(&mut self, key: &[u8], value: T) -> Result<()>;
219
220    /// Reads an value of a key from the Metadata CF
221    fn op_read(&self, key: &[u8]) -> Result<Option<Vec<u8>>>;
222}
223
224pub trait Persist:
225    Ledger + ConsensusStorage + Mempool + Metadata + core::fmt::Debug
226{
227    // Candidate block functions
228
229    fn clear_database(&mut self) -> Result<()>;
230    fn commit(self) -> Result<()>;
231    fn rollback(self) -> Result<()>;
232}
233
234pub fn into_array<const N: usize>(value: &[u8]) -> [u8; N] {
235    let mut res = [0u8; N];
236    res.copy_from_slice(&value[0..N]);
237    res
238}
239
240#[derive(Serialize, Deserialize, Clone, Debug)]
241pub struct DatabaseOptions {
242    /// Max write buffer size per Blocks-related CF. By default, there are two
243    /// write buffers (MemTables) per CF.
244    pub blocks_cf_max_write_buffer_size: usize,
245
246    /// Disables Block Cache for non-Mempool CFs
247    ///
248    /// Block Cache is useful in optimizing DB reads. For
249    /// non-block-explorer nodes, DB reads for block retrieval should
250    /// not be buffered in memory.
251    pub blocks_cf_disable_block_cache: bool,
252
253    /// Max write buffer size per Mempool CF.
254    pub mempool_cf_max_write_buffer_size: usize,
255
256    /// Enables a set of flags for collecting DB stats as log data.
257    pub enable_debug: bool,
258
259    /// Create the database if missing
260    pub create_if_missing: bool,
261}
262
263impl Default for DatabaseOptions {
264    fn default() -> Self {
265        Self {
266            blocks_cf_max_write_buffer_size: 1024 * 1024, // 1 MiB
267            mempool_cf_max_write_buffer_size: 10 * 1024 * 1024, // 10 MiB
268            blocks_cf_disable_block_cache: true,
269            enable_debug: false,
270            create_if_missing: true,
271        }
272    }
273}