1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
// Copyright 2020 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Interfaces to work with persisted blockchain data. The data can be *Merkelized*,
//! i.e., stored into authenticated data structures, which allow to prove presence or absence
//! of data with logarithmic overhead.
//!
//! See also [the documentation page on storage][doc:storage].
//!
//! # Database
//!
//! A [`Database`] is a container for data persistence. Internally, a `Database` is
//! a collection of named key-value stores (aka column families)
//! with reading isolation and atomic writes. The database is assumed to be embedded,
//! that is, the Exonum process has exclusive access to the DB during blockchain operation.
//! You can interact with the `Database` from multiple threads by cloning its instance.
//!
//! This crate provides two database types: [`RocksDB`] and [`TemporaryDB`].
//!
//! # Snapshot and Fork
//!
//! Snapshots and forks facilitate access to the database.
//!
//! If you need to read the data, you can create a [`Snapshot`] using the [`snapshot`][1] method
//! of the `Database` instance. Snapshots provide read isolation, so you are guaranteed to work
//! with consistent values even if the data in the database changes between reads. `Snapshot`
//! provides all the necessary methods for reading data from the database, so `&Snapshot`
//! is used as a storage view for creating a read-only representation of the [indexes](#indexes).
//!
//! If you need to make changes to the database, you need to create a [`Fork`] using
//! the [`fork`][2] method of the `Database`. Like `Snapshot`, `Fork` provides read isolation,
//! but also allows creating a sequence of changes to the database that are specified
//! as a [`Patch`]. A patch can be atomically [`merge`]d into a database. Different threads
//! may call `merge` concurrently.
//!
//! # `BinaryKey` and `BinaryValue` traits
//!
//! If you need to use your own data types as keys or values in the storage, you need to implement
//! the [`BinaryKey`] or [`BinaryValue`] traits respectively. These traits have already been
//! implemented for most standard types.
//!
//! # Indexes
//!
//! Indexes are structures representing data collections stored in the database.
//! This concept is similar to tables in relational databases. The interfaces
//! of the indexes are similar to ordinary collections (like arrays, maps and sets).
//!
//! Each index occupies a certain set of keys in a single column family of the [`Database`].
//! On the other hand, multiple indexes can be stored in the same column family, provided
//! that their key spaces do not intersect. Isolation is commonly achieved with the help
//! of [`Group`]s or keyed [`IndexAddress`]es.
//!
//! Merkelized indexes can generate cryptographic proofs about inclusion
//! of entries. Having such a proof, an external client may verify locally that the received data
//! was authorized by the blockchain validators, without having to replicate
//! the entire blockchain contents.
//!
//! This crate provides the following index types:
//!
//! - [`Entry`] is a specific index that stores only one value. Useful for global values, such as
//!   configuration. Similar to a combination of [`Box`] and [`Option`].
//! - [`ListIndex`] is a list of items stored in a sequential order. Similar to [`Vec`].
//! - [`SparseListIndex`] is a list of items stored in a sequential order. Similar to `ListIndex`,
//!   but may contain indexes without elements.
//! - [`MapIndex`] is a map of keys and values. Similar to [`BTreeMap`].
//! - [`ProofEntry`] is a Merkelized version of `Entry`.
//! - [`ProofListIndex`] is a Merkelized version of `ListIndex` that supports cryptographic
//!   proofs of existence and is implemented as a Merkle tree.
//! - [`ProofMapIndex`] is a Merkelized version of `MapIndex` that supports cryptographic
//!   proofs of existence and is implemented as a binary Merkle Patricia tree.
//! - [`KeySetIndex`] and [`ValueSetIndex`] are sets of items, similar to [`BTreeSet`] and
//!   [`HashSet`] accordingly.
//!
//! # State aggregation
//!
//! The database automatically aggregates its contents into a single `state_hash`, which commits
//! to the entire Merkelized database contents. For example, this is used in [Exonum] to achieve
//! consensus as to the database state.
//!
//! The `state_hash` of the database is the hash of [`state_aggregator`], a system `ProofMapIndex`
//! with keys being UTF-8 names of aggregated indexes, and values their hashes
//! as per [`ObjectHash`] implementation. An index is aggregated if and only if it satisfies
//! the following constraints:
//!
//! - Index has a matching type (`ProofListIndex`, `ProofMapIndex`, or `ProofEntry`)
//! - Index is not a part of a group, i.e., its address does not contain the `bytes` part
//!
//! The aggregation is automatically updated when a `Fork` is converted into a `Patch`.
//! Thus, `Snapshot`s (including `Patch`es!) are always consistent with respect
//! to the aggregated state; the index hashes in the `state_aggregator` match their actual values.
//! This is **not** the case for `Fork`s, in which `state_aggregator` may be stale.
//!
//! # Migrations
//!
//! The database [provides tooling](migration/index.html) for data migrations. With the help
//! of migration, it is possible to gradually accumulate changes to a set of indexes (including
//! across process restarts) and then atomically apply or discard these changes.
//!
//! [`Database`]: trait.Database.html
//! [`RocksDB`]: struct.RocksDB.html
//! [`TemporaryDB`]: struct.TemporaryDB.html
//! [`Snapshot`]: trait.Snapshot.html
//! [`Fork`]: struct.Fork.html
//! [`Patch`]: struct.Patch.html
//! [1]: trait.Database.html#tymethod.snapshot
//! [2]: trait.Database.html#method.fork
//! [`merge`]: trait.Database.html#tymethod.merge
//! [`BinaryKey`]: trait.BinaryKey.html
//! [`BinaryValue`]: trait.BinaryValue.html
//! [`Entry`]: indexes/struct.Entry.html
//! [`ProofEntry`]: indexes/struct.ProofEntry.html
//! [`ListIndex`]: indexes/struct.ListIndex.html
//! [`SparseListIndex`]: indexes/struct.SparseListIndex.html
//! [`MapIndex`]: indexes/struct.MapIndex.html
//! [`ProofListIndex`]: indexes/proof_list/struct.ProofListIndex.html
//! [`ProofMapIndex`]: indexes/proof_map/struct.ProofMapIndex.html
//! [`KeySetIndex`]: indexes/struct.KeySetIndex.html
//! [`ValueSetIndex`]: indexes/struct.ValueSetIndex.html
//! [`ObjectHash`]: trait.ObjectHash.html
//! [doc:storage]: https://exonum.com/doc/architecture/storage
//! [`Option`]: https://doc.rust-lang.org/std/option/enum.Option.html
//! [`Box`]: https://doc.rust-lang.org/std/boxed/struct.Box.html
//! [`Vec`]: https://doc.rust-lang.org/std/vec/struct.Vec.html
//! [`BTreeMap`]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html
//! [`BTreeSet`]: https://doc.rust-lang.org/std/collections/struct.BTreeSet.html
//! [`HashSet`]: https://doc.rust-lang.org/std/collections/struct.HashSet.html
//! [`state_aggregator`]: struct.SystemSchema.html#method.state_aggregator
//! [`Group`]: indexes/group/struct.Group.html
//! [`IndexAddress`]: struct.IndexAddress.html
//! [Exonum]: https://exonum.com/

#![warn(
    missing_debug_implementations,
    unsafe_code,
    bare_trait_objects,
    missing_docs
)]
#![warn(clippy::pedantic, clippy::nursery)]
#![allow(
    // Next `cast_*` lints don't give alternatives.
    clippy::cast_possible_wrap, clippy::cast_possible_truncation, clippy::cast_sign_loss,
    // Next lints produce too much noise/false positives.
    clippy::module_name_repetitions, clippy::similar_names, clippy::must_use_candidate,
    clippy::pub_enum_variant_names,
    // '... may panic' lints.
    clippy::indexing_slicing,
    // Too much work to fix.
    clippy::missing_errors_doc, clippy::missing_const_for_fn
)]

#[macro_use] // Code generated by Protobuf requires `serde_derive` macros to be globally available.
extern crate serde_derive;

// Re-export the crypto crate for use in the `ObjectHash` derive macro.
#[doc(hidden)]
pub mod _reexports {
    pub use anyhow::Error;
    pub use exonum_crypto::{hash, Hash};
}

pub use self::{
    backends::{rocksdb::RocksDB, temporarydb::TemporaryDB},
    db::{
        Database, DatabaseExt, Fork, Iter, Iterator, OwnedReadonlyFork, Patch, ReadonlyFork,
        Snapshot,
    },
    error::Error,
    hash::{root_hash, HashTag, ObjectHash, ValidationError},
    keys::BinaryKey,
    lazy::Lazy,
    options::DbOptions,
    values::BinaryValue,
    views::{AsReadonly, IndexAddress, IndexType, ResolvedAddress, SystemSchema},
};
// Workaround for 'Linked file at path {exonum_merkledb_path}/struct.ProofMapIndex.html
// does not exist!'
#[doc(no_inline)]
pub use self::indexes::{
    proof_list::{self, ListProof, ProofListIndex},
    proof_map::{self, MapProof, ProofMapIndex, RawProofMapIndex},
    Entry, Group, KeySetIndex, ListIndex, MapIndex, ProofEntry, SparseListIndex, ValueSetIndex,
};

#[macro_use]
mod macros;
pub mod access;
mod backends;
mod db;
mod error;
pub mod generic;
mod hash;
pub mod indexes;
mod keys;
mod lazy;
pub mod migration;
mod options;
pub mod validation;
mod values;
mod views;

#[cfg(feature = "with-protobuf")]
#[doc(hidden)]
pub mod proto;

#[cfg(feature = "with-protobuf")]
use exonum_proto::ProtobufConvert;

/// A specialized `Result` type for I/O operations with storage.
pub type Result<T> = std::result::Result<T, Error>;