cuprate-hinto-janai/storage/blockchain
hinto-janai a438279aa8
storage: split cuprate-blockchain <-> cuprate-database (#160)
* storage: port some code `cuprate-blockchain` -> `database`

* database: remove `Tables` references

* database: remove old `cuprate-blockchain` type references

* find/replace `cuprate_blockchain` -> `database`, add `create_db()`

* database: fix redb

* database: use readme for docs, link in `lib.rs`

* database: fix `open_db_ro`, `open_db_rw`, `create_db` behavior

* database: add open table tests

* database: fix tests, remove blockchain specific references

* database: remove `ReaderThreads`, make `db_directory` mandatory

* initial `cuprate-blockchain` split

* fix doc links

* rename, fix database config

* blockchain: create `crate::open()`, `OpenTables::create_tables()`

* more compat fixes

* fix imports

* fix conflicts

* align cargo.toml

* docs

* fixes

* add `unused_crate_dependencies` lint, fix

* blockchain: add open table tests
2024-06-26 22:51:06 +01:00
..
src storage: split cuprate-blockchain <-> cuprate-database (#160) 2024-06-26 22:51:06 +01:00
Cargo.toml storage: split cuprate-blockchain <-> cuprate-database (#160) 2024-06-26 22:51:06 +01:00
DESIGN.md storage: split cuprate-blockchain <-> cuprate-database (#160) 2024-06-26 22:51:06 +01:00
README.md storage: split cuprate-blockchain <-> cuprate-database (#160) 2024-06-26 22:51:06 +01:00

Cuprate's blockchain database.

This documentation is mostly for practical usage of cuprate_blockchain.

For a high-level overview, see the database section in Cuprate's architecture book.

Purpose

This crate does 3 things:

  1. Uses [cuprate_database] as a base database layer
  2. Implements various Monero related operations, [tables], and [types]
  3. Exposes a [tower::Service] backed by a thread-pool

Each layer builds on-top of the previous.

As a user of cuprate_blockchain, consider using the higher-level [service] module, or at the very least the [ops] module instead of interacting with the cuprate_database traits directly.

cuprate_database

Consider reading cuprate_database's crate documentation before this crate, as it is the first layer.

If/when this crate needs is used, be sure to use the version that this crate re-exports, e.g.:

use cuprate_blockchain::{
    cuprate_database::RuntimeError,
};

This ensures the types/traits used from cuprate_database are the same ones used by cuprate_blockchain internally.

Feature flags

The service module requires the service feature to be enabled. See the module for more documentation.

Different database backends are enabled by the feature flags:

  • heed (LMDB)
  • redb

The default is heed.

tracing is always enabled and cannot be disabled via feature-flag.

Invariants when not using service

cuprate_blockchain can be used without the service feature enabled but there are some things that must be kept in mind when doing so.

Failing to uphold these invariants may cause panics.

  1. LMDB requires the user to resize the memory map resizing (see [cuprate_database::RuntimeError::ResizeNeeded]
  2. LMDB has a maximum reader transaction count, currently it is set to 128
  3. LMDB has maximum key/value byte size which must not be exceeded

Examples

The below is an example of using cuprate_blockchain lowest API, i.e. using a mix of this crate and cuprate_database's traits directly - this is NOT recommended.

For examples of the higher-level APIs, see:

  • [ops]
  • [service]
use cuprate_blockchain::{
    cuprate_database::{
        ConcreteEnv,
        Env, EnvInner,
        DatabaseRo, DatabaseRw, TxRo, TxRw,
    },
    config::ConfigBuilder,
    tables::{Tables, TablesMut},
	OpenTables,
};

# fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create a configuration for the database environment.
let tmp_dir = tempfile::tempdir()?;
let db_dir = tmp_dir.path().to_owned();
let config = ConfigBuilder::new()
    .db_directory(db_dir.into())
    .build();

// Initialize the database environment.
let env = cuprate_blockchain::open(config)?;

// Open up a transaction + tables for writing.
let env_inner = env.env_inner();
let tx_rw = env_inner.tx_rw()?;
let mut tables = env_inner.open_tables_mut(&tx_rw)?;

// ⚠️ Write data to the tables directly.
// (not recommended, use `ops` or `service`).
const KEY_IMAGE: [u8; 32] = [88; 32];
tables.key_images_mut().put(&KEY_IMAGE, &())?;

// Commit the data written.
drop(tables);
TxRw::commit(tx_rw)?;

// Read the data, assert it is correct.
let tx_ro = env_inner.tx_ro()?;
let tables = env_inner.open_tables(&tx_ro)?;
let (key_image, _) = tables.key_images().first()?;
assert_eq!(key_image, KEY_IMAGE);
# Ok(()) }