From be43216b3f5893e5ca178d94a61052f0ada742aa Mon Sep 17 00:00:00 2001
From: Someone Else <118022351+SyntheticBird45@users.noreply.github.com>
Date: Thu, 20 Apr 2023 17:20:32 +0000
Subject: [PATCH] initial database code (#6)
* commit to start the draft pull request.
added a space
* Please don't look to close.It might hurt your eyes
* impl associated types
* errors, docs & divided ro/rw tx
Added some more errors to DB_FAILURES, rewrited crates docs, and specified
WriteTransaction subtype which implement write mode method.
* more changes see description
changed blockchain_db folder by database. Implemented (just for test) get_block_hash, open, from to Interface.
Also rewrited a declarative macro for tables. Will have to add Dummy Tables later.
* small changes
* Organized modules & implemented get_block_hash
* write prototype & error
Added prototype functions for clear(), put() & delete() in mdbx implementation. They still don't
consider table flags. Also added a temporary DB_FAILURES::EncodingError for monero-rs consensus_encode
errors. Still have to rethink about it to resend a reference to the data that can't be encoded.
* Multiple changes
- hse.rs
Added hse.rs that will contain db implementations for HSE. Since the codebase can't welcome unsafe
code, the wrapper will be written outside of the project.
- lib.rs
Added a specific FailedToCommit error. (will investigate if really necessary).
Added DupTable trait, which is a Table with DUPSORT/DUPFIXED support and its declarative macro.
Added two other tables, blockheaders that give block's header with specified hash & blockbody that give block's body with specified hash
Added Cursor methods, that are likely to be deprecated if I found a way to implemen Iterator on top of it.
Added WriteCursor trait & methods, which is basically put & del.
Added mandatory type for Cursors in Transaction & WriteTransactions
Refactored get_block_hash interface method.
- mdbx.rs
Added partial implementation of Cursor & WriteCursor trait for libmdbx::Cursor. Only the first() & get() methods are implemented
Added implementation of get & commit for Transaction
* put mdbx as features with its dependency
* save
* refactored some method with macros
* more mdbx errors, docs correction, moved to error.rs
* finish nodup mdbx impl, errors.rs, macros, tables
Finished the initial implementation of Cursor, WriteCursor, Transaction and WriteTransaction in mdbx.rs. Corrected some macros in mdbx.rs to simplify the implementations. There is certainly rooms to more flexible macros. Also added 3 other tables. I started to divide errors into category to more easily handle them at higher-level. Due to the large number of errors i just moved them into another file. There is know DB_SERIAL enum for errors relating of decoding/encoding error & DB_FULL enum for every errors relating a component being overeaching its capacity.
* bye bye match statement in mdbx.rs
* defined all blockchain tables (not txpool)
* dupsort/fixed support, dupcursor, basic block interface
* tables, types, encoding and documentations
Redefined all the database types from @Boog900's monero-rs db branch and added the needed
implementations. The database now use bincode2 for encoding and decoding. We observe that bincode was
5 times faster at serializing than monero::consensus_encode. Since we still use monero-rs types but can't implement
foreign trait to them, the encoding module contain a compatibility layer, the time we switch from monero-rs to properly
implement it. All the tables are now defined. (can be subject to change if there is good reason for). added documentations
to modules and types.
* replaced macros and added hfversion table
* save
* multiple changes
* modified database schema. deprecated output global index and splited up pre-rct from rct output.
* Fixed DupCursor function to return subkey (thx to rust turbofish inference).
* Added some output functions
* Added two new DB_FAILURES, one to handle a prohibited None case and one for undefined case where a dev msg is needed.
* fixed TxOutputIdx, previously used global index, now is a tuple of amount/amount_index.
* i hate lifetimes
* read-only method now use read-only tx
* initial output fn
* some tx functions. Yes I'll refactor them
* moved interface in a module
* redefined errors, more tx fn, None->error
* corrected a table + started blk fns
* save
* fixed TxOutputIdx + pop_block
* IIRC I finished initial interface fns
* fixed table name const + db build/check/open fn
* switched important tables to dummy keys + rm blockhfversion
* minor docs correction
* fixed mentioned issues
* make a test bin, just for fun
* fixed issues + cargo fmt
* removed monerod part
* fixed a comment
---
.gitignore | 3 +-
Cargo.toml | 17 +-
blockchain_db/Cargo.toml | 18 -
blockchain_db/src/lib.rs | 883 -------------
blockchain_db/src/rocksdb.rs | 64 -
cuprate/Cargo.toml | 12 +
cuprate/src/cli.rs | 50 +
cuprate/src/main.rs | 13 +
database/Cargo.toml | 33 +
{blockchain_db => database}/LICENSE | 0
database/src/encoding.rs | 78 ++
database/src/error.rs | 53 +
database/src/hse.rs | 11 +
database/src/interface.rs | 1036 +++++++++++++++
database/src/lib.rs | 221 ++++
database/src/mdbx.rs | 474 +++++++
database/src/table.rs | 127 ++
database/src/types.rs | 516 ++++++++
net/levin/src/bucket_sink.rs | 14 +-
net/levin/src/bucket_stream.rs | 7 +-
net/levin/src/lib.rs | 7 +-
net/levin/src/message_sink.rs | 15 +-
net/levin/src/message_stream.rs | 14 +-
net/monero-wire/src/internal_macros.rs | 10 +-
net/monero-wire/src/messages.rs | 156 ++-
net/monero-wire/src/messages/admin.rs | 1429 +++++++++++---------
net/monero-wire/src/messages/protocol.rs | 1531 ++++++++++++----------
net/monero-wire/src/network_address.rs | 10 +-
rustfmt.toml | 8 -
src/blocks/mod.rs | 3 -
src/cryptonote_basic/difficulty.rs | 1 -
src/cryptonote_basic/mod.rs | 1 -
src/cryptonote_protocol/enums.rs | 1 -
src/cryptonote_protocol/mod.rs | 1 -
src/main.rs | 3 -
35 files changed, 4431 insertions(+), 2389 deletions(-)
delete mode 100644 blockchain_db/Cargo.toml
delete mode 100644 blockchain_db/src/lib.rs
delete mode 100644 blockchain_db/src/rocksdb.rs
create mode 100644 cuprate/Cargo.toml
create mode 100644 cuprate/src/cli.rs
create mode 100644 cuprate/src/main.rs
create mode 100644 database/Cargo.toml
rename {blockchain_db => database}/LICENSE (100%)
create mode 100644 database/src/encoding.rs
create mode 100644 database/src/error.rs
create mode 100644 database/src/hse.rs
create mode 100644 database/src/interface.rs
create mode 100644 database/src/lib.rs
create mode 100644 database/src/mdbx.rs
create mode 100644 database/src/table.rs
create mode 100644 database/src/types.rs
delete mode 100644 rustfmt.toml
delete mode 100644 src/blocks/mod.rs
delete mode 100644 src/cryptonote_basic/difficulty.rs
delete mode 100644 src/cryptonote_basic/mod.rs
delete mode 100644 src/cryptonote_protocol/enums.rs
delete mode 100644 src/cryptonote_protocol/mod.rs
diff --git a/.gitignore b/.gitignore
index 8fddb052..02c92a60 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
target/
Cargo.lock
-.vscode
\ No newline at end of file
+.vscode
+monerod
\ No newline at end of file
diff --git a/Cargo.toml b/Cargo.toml
index 9a10dd62..c78d4bc5 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -18,14 +18,18 @@ authors=[
[workspace]
members = [
- "blockchain_db",
+ "cuprate",
+ "database",
"net/levin",
"net/monero-wire"
]
[workspace.dependencies]
-monero = {version = "*", features = ['serde']}
+monero = { version = "*" }
+bincode = { version = "2.0.0-rc.3" }
serde = { version = "*", features =["derive"]}
+tracing = "*"
+tracing-subscriber = "*"
# As suggested by /u/danda :
thiserror = "*"
@@ -38,4 +42,11 @@ lto = "thin"
panic = "abort"
[build]
-rustflags=["-Zcf-protection=full", "-Zsanitizer=cfi", "-Crelocation-model=pie", "-Cstack-protector=all"]
+linker="clang"
+rustflags=[
+ "-Clink-arg=-fuse-ld=mold",
+ "-Zcf-protection=full",
+ "-Zsanitizer=cfi",
+ "-Crelocation-model=pie",
+ "-Cstack-protector=all",
+]
\ No newline at end of file
diff --git a/blockchain_db/Cargo.toml b/blockchain_db/Cargo.toml
deleted file mode 100644
index 720426af..00000000
--- a/blockchain_db/Cargo.toml
+++ /dev/null
@@ -1,18 +0,0 @@
-[package]
-name = "blockchain_db"
-version = "0.0.1"
-edition = "2021"
-rust-version = "1.67.0"
-license = "AGPL-3.0-only"
-
-# All Contributors on github
-authors=[
- "SyntheticBird45 <@someoneelse495495:matrix.org>"
- ]
-
-[dependencies]
-monero = {workspace = true, features = ['serde']}
-serde = { workspace = true}
-thiserror = {workspace = true }
-
-rocksdb = { version = "*", features = ["multi-threaded-cf"]}
\ No newline at end of file
diff --git a/blockchain_db/src/lib.rs b/blockchain_db/src/lib.rs
deleted file mode 100644
index 4b79f0f6..00000000
--- a/blockchain_db/src/lib.rs
+++ /dev/null
@@ -1,883 +0,0 @@
-// Copyright (C) 2023 Cuprate Contributors
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Affero General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Affero General Public License for more details.
-//
-// You should have received a copy of the GNU Affero General Public License
-// along with this program. If not, see .
-
-//!
-//! blockchain_db crates:
-//! Contains the implementation of interaction between the blockchain and the database backend.
-//! There is actually only one storage engine available:
-//! - RocksDB
-//! There is two other storage engine planned:
-//! - HSE (Heteregeonous Storage Engine)
-//! - LMDB (like monerod)
-
-#![deny(unused_attributes)]
-#![forbid(unsafe_code)]
-#![allow(non_camel_case_types)]
-#![deny(clippy::expect_used, clippy::panic)]
-
-use thiserror::Error;
-use monero::{Hash, Transaction, Block, BlockHeader, consensus::Encodable, util::ringct::RctSig};
-use std::{error::Error, ops::Range};
-
-const MONERO_DEFAULT_LOG_CATEGORY: &str = "blockchain.db";
-
-pub type difficulty_type = u128;
-type Blobdata = Vec;
-type BlobdataRef = [u8];
-type TxOutIndex = (Hash, u64);
-
-/// Methods tracking how a tx was received and relayed
-pub enum RelayMethod {
- none, //< Received via RPC with `do_not_relay` set
- local, //< Received via RPC; trying to send over i2p/tor, etc.
- forward, //< Received over i2p/tor; timer delayed before ipv4/6 public broadcast
- stem, //< Received/send over network using Dandelion++ stem
- fluff, //< Received/sent over network using Dandelion++ fluff
- block, //< Received in block, takes precedence over others
-}
-
-// the database types are going to be defined in the monero rust library.
-pub enum RelayCategory {
- broadcasted, //< Public txes received via block/fluff
- relayable, //< Every tx not marked `relay_method::none`
- legacy, //< `relay_category::broadcasted` + `relay_method::none` for rpc relay requests or historical reasons
- all, //< Everything in the db
-}
-
-fn matches_category(relay_method: RelayMethod, relay_category: RelayCategory) -> bool {
- todo!()
-}
-
-/**
- * @brief a struct containing output metadata
- */
-pub struct output_data_t {
- pubkey: monero::util::key::PublicKey, //< the output's public key (for spend verification)
- unlock_time: u64, //< the output's unlock time (or height)
- height: u64, //< the height of the block which created the output
- commitment: monero::util::ringct::Key, //< the output's amount commitment (for spend verification)
-}
-
-pub struct tx_data_t {
- tx_id: u64,
- unlock_time: u64,
- block_id: u64,
-}
-
-pub struct alt_block_data_t {
- height: u64,
- cumulative_weight: u64,
- cumulative_difficulty_low: u64,
- cumulative_difficulty_high: u64,
- already_generated_coins: u64,
-}
-
-pub struct txpool_tx_meta_t {
- max_used_block_id: monero::cryptonote::hash::Hash,
- last_failed_id: monero::cryptonote::hash::Hash,
- weight: u64,
- fee: u64,
- max_used_block_height: u64,
- last_failed_height: u64,
- receive_time: u64,
- last_relayed_time: u64, //< If received over i2p/tor, randomized forward time. If Dandelion++stem, randomized embargo time. Otherwise, last relayed timestamp
- // 112 bytes
- kept_by_block: u8,
- relayed: u8,
- do_not_relay: u8,
- double_spend_seen: u8,
- pruned: u8,
- is_local: u8,
- dandelionpp_stem: u8,
- is_forwarding: u8,
- bf_padding: u8,
-
- padding: [u8; 76],
-}
-
-impl txpool_tx_meta_t {
- fn set_relay_method(relay_method: RelayMethod) {}
-
- fn upgrade_relay_method(relay_method: RelayMethod) -> bool {
- todo!()
- }
-
- /// See `relay_category` description
- fn matches(category: RelayCategory) -> bool {
- return matches_category(todo!(), category);
- }
-}
-
-pub enum OBJECT_TYPE {
- BLOCK,
- BLOCK_BLOB,
- TRANSACTION,
- TRANSACTION_BLOB,
- OUTPUTS,
- TXPOOL,
-}
-
-#[non_exhaustive] // < to remove
-#[allow(dead_code)] // < to remove
-#[derive(Error, Debug)]
-pub enum DB_FAILURES {
- #[error("DB_ERROR: `{0}`. The database is likely corrupted.")]
- DB_ERROR(String),
- #[error("DB_ERROR_TXN_START: `{0}`. The database failed starting a txn.")]
- DB_ERROR_TXN_START(String),
- #[error("DB_OPEN_FAILURE: Failed to open the database.")]
- DB_OPEN_FAILURE,
- #[error("DB_CREATE_FAILURE: Failed to create the database.")]
- DB_CREATE_FAILURE,
- #[error("DB_SYNC_FAILURE: Failed to sync the database.")]
- DB_SYNC_FAILURE,
- #[error("BLOCK_DNE: `{0}`. The block requested does not exist")]
- BLOCK_DNE(String),
- #[error("BLOCK_PARENT_DNE: `{0}` The parent of the block does not exist")]
- BLOCK_PARENT_DNE(String),
- #[error("BLOCK_EXISTS. The block to be added already exists!")]
- BLOCK_EXISTS,
- #[error("BLOCK_INVALID: `{0}`. The block to be added did not pass validation!")]
- BLOCK_INVALID(String),
- #[error("TX_EXISTS. The transaction to be added already exists!")]
- TX_EXISTS,
- #[error("TX_DNE: `{0}`. The transaction requested does not exist!")]
- TX_DNE(String),
- #[error("OUTPUTS_EXISTS. The output to be added already exists!")]
- OUTPUT_EXISTS,
- #[error("OUTPUT_DNE: `{0}`. The output requested does not exist")]
- OUTPUT_DNE(String),
- #[error("KEY_IMAGE_EXISTS. The spent key imge to be added already exists!")]
- KEY_IMAGE_EXISTS,
-
- #[error("ARITHMETIC_COUNT: `{0}`. An error occured due to a bad arithmetic/count logic")]
- ARITHEMTIC_COUNT(String),
- #[error("HASH_DNE. ")]
- HASH_DNE(Option),
-}
-
-pub trait KeyValueDatabase {
- fn add_data_to_cf(cf: &str, data: &D) -> Result;
-}
-
-pub trait BlockchainDB: KeyValueDatabase {
- // supposed to be private
-
- // TODO: understand
-
- //fn remove_block() -> Result<(), DB_FAILURES>; useless as it just delete data from the table. pop_block that use it internally also use remove_transaction to literally delete the block. Can be implemented without
-
- fn add_spent_key() -> Result<(), DB_FAILURES>;
-
- fn remove_spent_key() -> Result<(), DB_FAILURES>;
-
- fn get_tx_amount_output_indices(tx_id: u64, n_txes: usize) -> Vec>;
-
- fn has_key_image(img: &monero::blockdata::transaction::KeyImage) -> bool;
-
- fn prune_outputs(amount: u64);
-
- fn get_blockchain_pruning_seed() -> u32;
-
- // variables part.
- // uint64_t num_calls = 0; //!< a performance metric
- // uint64_t time_blk_hash = 0; //!< a performance metric
- // uint64_t time_add_block1 = 0; //!< a performance metric
- // uint64_t time_add_transaction = 0; //!< a performance metric
-
- // supposed to be protected
-
- // mutable uint64_t time_tx_exists = 0; //!< a performance metric
- // uint64_t time_commit1 = 0; //!< a performance metric
- // bool m_auto_remove_logs = true; //!< whether or not to automatically remove old logs
-
- // HardFork* m_hardfork; | protected: int *m_hardfork
-
- // bool m_open;
- // mutable epee::critical_section m_synchronization_lock; //!< A lock, currently for when BlockchainLMDB needs to resize the backing db file
-
- // supposed to be public
-
- /* handled by the DB.
- fn batch_start(batch_num_blocks: u64, batch_bytes: u64) -> Result;
-
- fn batch_abort() -> Result<(),DB_FAILURES>;
-
- fn set_batch_transactions() -> Result<(),DB_FAILURES>;
-
- fn block_wtxn_start();
- fn block_wtxn_stop();
- fn block_wtxn_abort();
-
- fn block_rtxn_start();
- fn block_rtxn_stop();
- fn block_rtxn_abort();
- */
-
- //fn set_hard_fork(); // (HardFork* hf)
-
- //fn add_block_public() -> Result;
-
- //fn block_exists(h: monero::cryptonote::hash::Hash, height: u64) -> bool;
-
- // fn tx_exists(h: monero::cryptonote::hash::Hash, tx_id: Option) -> Result<(),()>; // Maybe error should be DB_FAILURES, not specified in docs
-
- //fn tx_exist(h: monero::cryptonote::hash::Hash, tx: monero::Transaction) -> bool;
-
- //fn get_tx_blob(h: monero::cryptonote::hash::Hash, tx: String) -> bool;
-
- //fn get_pruned_tx_blob(h: monero::cryptonote::hash::Hash, tx: &mut String) -> bool;
-
- //fn update_pruning() -> bool;
-
- //fn check_pruning() -> bool;
-
- //fn get_max_block_size() -> u64; It is never used
-
- //fn add_max_block_size() -> u64; For reason above
-
- //fn for_all_txpool_txes(wat: fn(wat1: &monero::Hash, wat2: &txpool_tx_meta_t, wat3: &String) -> bool, include_blob: bool, category: RelayCategory) -> Result;
-
- //fn for_all_keys_images(wat: fn(ki: &monero::blockdata::transaction::KeyImage) -> bool) -> Result;
-
- //fn for_blocks_range(h1: &u64, h2: &u64, wat: fn(u: u64, h: &monero::Hash, blk: &Block) -> bool) -> Result; // u: u64 should be mut u: u64
-
- //fn for_all_transactions(wat: fn(h: &monero::Hash, tx: &monero::Transaction) -> bool, pruned: bool) -> Result;
-
- //fn for_all_outputs();
-
- //fn for_all_alt_blocks();
-
- // ------------------------------------------| Blockchain |------------------------------------------------------------
-
- /// `height` fetch the current blockchain height.
- ///
- /// Return the current blockchain height. In case of failures, a DB_FAILURES will be return.
- ///
- /// No parameters is required.
- fn height(&mut self) -> Result;
-
- /// `set_hard_fork_version` sets which hardfork version a height is on.
- ///
- /// In case of failures, a `DB_FAILURES` will be return.
- ///
- /// Parameters:
- /// `height`: is the height where the hard fork happen.
- /// `version`: is the version of the hard fork.
- fn set_hard_fork_version(&mut self);
-
- /// `get_hard_fork_version` checks which hardfork version a height is on.
- ///
- /// In case of failures, a `DB_FAILURES` will be return.
- ///
- /// Parameters:
- /// `height:` is the height to check.
- fn get_hard_fork_version(&mut self);
-
- /// May not need to be used
- fn fixup(&mut self);
-
- // -------------------------------------------| Outputs |------------------------------------------------------------
-
- /// `add_output` add an output data to it's storage .
- ///
- /// It internally keep track of the global output count. The global output count is also used to index outputs based on
- /// their order of creations.
- ///
- /// Should return the amount output index. In case of failures, a DB_FAILURES will be return.
- ///
- /// Parameters:
- /// `tx_hash`: is the hash of the transaction where the output comes from.
- /// `output`: is the output to store.
- /// `index`: is the local output's index (from transaction).
- /// `unlock_time`: is the unlock time (height) of the output.
- /// `commitment`: is the RingCT commitment of this output.
- fn add_output(
- &mut self,
- tx_hash: &Hash,
- output: &Hash,
- index: TxOutIndex,
- unlock_time: u64,
- commitment: RctSig,
- ) -> Result;
-
- /// `add_tx_amount_output_indices` store amount output indices for a tx's outputs
- ///
- /// TODO
- fn add_tx_amount_output_indices() -> Result<(), DB_FAILURES>;
-
- /// `get_output_key` get some of an output's data
- ///
- /// Return the public key, unlock time, and block height for the output with the given amount and index, collected in a struct
- /// In case of failures, a `DB_FAILURES` will be return. Precisely, if the output cannot be found, an `OUTPUT_DNE` error will be return.
- /// If any of the required part for the final struct isn't found, a `DB_ERROR` will be return
- ///
- /// Parameters:
- /// `amount`: is the corresponding amount of the output
- /// `index`: is the output's index (indexed by amount)
- /// `include_commitment` : `true` by default.
- fn get_output_key(
- &mut self,
- amount: u64,
- index: u64,
- include_commitmemt: bool,
- ) -> Result;
-
- /// `get_output_tx_and_index_from_global`gets an output's transaction hash and index from output's global index.
- ///
- /// Return a tuple containing the transaction hash and the output index. In case of failures, a `DB_FAILURES` will be return.
- ///
- /// Parameters:
- /// `index`: is the output's global index.
- fn get_output_tx_and_index_from_global(&mut self, index: u64) -> Result;
-
- /// `get_output_key_list` gets outputs' metadata from a corresponding collection.
- ///
- /// Return a collection of output's metadata. In case of failurse, a `DB_FAILURES` will be return.
- ///
- /// Parameters:
- /// `amounts`: is the collection of amounts corresponding to the requested outputs.
- /// `offsets`: is a collection of outputs' index (indexed by amount).
- /// `allow partial`: `false` by default.
- fn get_output_key_list(
- &mut self,
- amounts: &Vec,
- offsets: &Vec,
- allow_partial: bool,
- ) -> Result, DB_FAILURES>;
-
- /// `get_output_tx_and_index` gets an output's transaction hash and index
- ///
- /// Return a tuple containing the transaction hash and the output index. In case of failures, a `DB_FAILURES` will be return.
- ///
- /// Parameters:
- /// `amount`: is the corresponding amount of the output
- /// `index`: is the output's index (indexed by amount)
- fn get_output_tx_and_index(&mut self, amount: u64, index: u64) -> Result;
-
- /// `get_num_outputs` fetches the number of outputs of a given amount.
- ///
- /// Return a count of outputs of the given amount. in case of failures a `DB_FAILURES` will be return.
- ///
- /// Parameters:
- /// `amount`: is the output amount being looked up.
- fn get_num_outputs(amount: &u64) -> Result;
-
- // -----------------------------------------| Transactions |----------------------------------------------------------
-
- /// `add_transaction` add the corresponding transaction and its hash to the specified block.
- ///
- /// In case of failures, a DB_FAILURES will be return. Precisely, a TX_EXISTS will be returned if the
- /// transaction to be added already exists in the database.
- ///
- /// Parameters:
- /// `blk_hash`: is the hash of the block which inherit the transaction
- /// `tx`: is obviously the transaction to add
- /// `tx_hash`: is the hash of the transaction.
- /// `tx_prunable_hash_ptr`: is the hash of the prunable part of the transaction.
- fn add_transaction(
- &mut self,
- blk_hash: &Hash,
- tx: Transaction,
- tx_hash: &Hash,
- tx_prunable_hash_ptr: &Hash,
- ) -> Result<(), DB_FAILURES>;
-
- /// `add_transaction_data` add the specified transaction data to its storage.
- ///
- /// It only add the transaction blob and tx's metadata, not the collection of outputs.
- ///
- /// Return the hash of the transaction added. In case of failures, a DB_FAILURES will be return.
- ///
- /// Parameters:
- /// `blk_hash`: is the hash of the block containing the transaction
- /// `tx_and_hash`: is a tuple containing the transaction and it's hash
- /// `tx_prunable_hash`: is the hash of the prunable part of the transaction
- fn add_transaction_data(
- &mut self,
- blk_hash: &Hash,
- tx_and_hash: (Transaction, &Hash),
- tx_prunable_hash: &Hash,
- ) -> Result;
-
- /// `remove_transaction_data` remove data about a transaction specified by its hash.
- ///
- /// In case of failures, a `DB_FAILURES` will be return. Precisely, a `TX_DNE` will be return if the specified transaction can't be found.
- ///
- /// Parameters:
- /// `tx_hash`: is the transaction's hash to remove data from.
- fn remove_transaction_data(&mut self, tx_hash: &Hash) -> Result<(), DB_FAILURES>;
-
- /// `get_tx_count` fetches the total number of transactions stored in the database
- ///
- /// Should return the count. In case of failure, a DB_FAILURES will be return.
- ///
- /// No parameters is required.
- fn get_tx_count(&mut self) -> Result;
-
- /// `tx_exists` check if a transaction exist with the given hash.
- ///
- /// Return `true` if the transaction exist, `false` otherwise. In case of failure, a DB_FAILURES will be return.
- ///
- /// Parameters :
- /// `h` is the given hash of transaction to check.
- /// `tx_id` is an optional mutable reference to get the transaction id out of the found transaction.
- fn tx_exists(&mut self, h: &Hash, tx_id: &mut Option) -> Result;
-
- /// `get_tx_unlock_time` fetch a transaction's unlock time/height
- ///
- /// Should return the unlock time/height in u64. In case of failure, a DB_FAILURES will be return.
- ///
- /// Parameters:
- /// `h`: is the given hash of the transaction to check.
- fn get_tx_unlock_time(&mut self, h: &Hash) -> Result;
-
- /// `get_tx` fetches the transaction with the given hash.
- ///
- /// Should return the transaction. In case of failure, a DB_FAILURES will be return.
- ///
- /// Parameters:
- /// `h`: is the given hash of transaction to fetch.
- fn get_tx(&mut self, h: &Hash) -> Result;
-
- /// `get_pruned_tx` fetches the transaction base with the given hash.
- ///
- /// Should return the transaction. In case of failure, a DB_FAILURES will be return.
- ///
- /// Parameters:
- /// `h`: is the given hash of transaction to fetch.
- fn get_pruned_tx(&mut self, h: &Hash) -> Result;
-
- /// `get_tx_list` fetches the transactions with given hashes.
- ///
- /// Should return a vector with the requested transactions. In case of failures, a DB_FAILURES will be return.
- /// Precisly, a HASH_DNE error will be returned with the correspondig hash of transaction that is not found in the DB.
- ///
- /// `hlist`: is the given collection of hashes correspondig to the transactions to fetch.
- fn get_tx_list(&mut self, hlist: &Vec) -> Result, DB_FAILURES>;
-
- /// `get_tx_blob` fetches the transaction blob with the given hash.
- ///
- /// Should return the transaction blob. In case of failure, a DB_FAILURES will be return.
- ///
- /// Parameters:
- /// `h`: is the given hash of the transaction to fetch.
- fn get_tx_blob(&mut self, h: &Hash) -> Result;
-
- /// `get_pruned_tx_blob` fetches the pruned transaction blob with the given hash.
- ///
- /// Should return the transaction blob. In case of failure, a DB_FAILURES will be return.
- ///
- /// Parameters:
- /// `h`: is the given hash of the transaction to fetch.
- fn get_pruned_tx_blob(&mut self, h: &Hash) -> Result;
-
- /// `get_prunable_tx_blob` fetches the prunable transaction blob with the given hash.
- ///
- /// Should return the transaction blob, In case of failure, a DB_FAILURES, will be return.
- ///
- /// Parameters:
- /// `h`: is the given hash of the transaction to fetch.
- fn get_prunable_tx_blob(&mut self, h: &Hash) -> Result;
-
- /// `get_prunable_tx_hash` fetches the prunable transaction hash
- ///
- /// Should return the hash of the prunable transaction data. In case of failures, a DB_FAILURES, will be return.
- ///
- /// Parameters:
- /// `tx_hash`: is the given hash of the transaction to fetch.
- fn get_prunable_tx_hash(&mut self, tx_hash: &Hash) -> Result;
-
- /// `get_pruned_tx_blobs_from` fetches a number of pruned transaction blob from the given hash, in canonical blockchain order.
- ///
- /// Should return the pruned transactions stored from the one with the given hash. In case of failure, a DB_FAILURES will be return.
- /// Precisly, an ARITHMETIC_COUNT error will be returned if the first transaction does not exist or their are fewer transactions than the count.
- ///
- /// Parameters:
- /// `h`: is the given hash of the first transaction/
- /// `count`: is the number of transaction to fetch in canoncial blockchain order.
- fn get_pruned_tx_blobs_from(&mut self, h: &Hash, count: usize) -> Result, DB_FAILURES>;
-
- /// `get_tx_block_height` fetches the height of a transaction's block
- ///
- /// Should return the height of the block containing the transaction with the given hash. In case
- /// of failures, a DB FAILURES will be return. Precisely, a TX_DNE error will be return if the transaction cannot be found.
- ///
- /// Parameters:
- /// `h`: is the fiven hash of the first transaction
- fn get_tx_block_height(&mut self, h: &Hash) -> Result;
-
- // -----------------------------------------| Blocks |----------------------------------------------------------
-
- /// `add_block` add the block and metadata to the db.
- ///
- /// In case of failures, a `DB_FAILURES` will be return. Precisely, a BLOCK_EXISTS error will be returned if
- /// the block to be added already exist. a BLOCK_INVALID will be returned if the block to be added did not pass validation.
- ///
- /// Parameters:
- /// `blk`: is the block to be added
- /// `block_weight`: is the weight of the block (data's total)
- /// `long_term_block_weight`: is the long term weight of the block (data's total)
- /// `cumulative_difficulty`: is the accumulated difficulty at this block.
- /// `coins_generated` is the number of coins generated after this block.
- /// `blk_hash`: is the hash of the block.
- fn add_block(
- blk: Block,
- blk_hash: Hash,
- block_weight: u64,
- long_term_block_weight: u64,
- cumulative_difficulty: u128,
- coins_generated: u64,
- ) -> Result<(), DB_FAILURES>;
-
- /// `pop_block` pops the top block off the blockchain.
- ///
- /// Return the block that was popped. In case of failures, a `DB_FAILURES` will be return.
- ///
- /// No parameters is required.
- fn pop_block(&mut self) -> Result;
-
- /// `blocks_exists` check if the given block exists
- ///
- /// Return `true` if the block exist, `false` otherwise. In case of failures, a `DB_FAILURES` will be return.
- ///
- /// Parameters:
- /// `h`: is the given hash of the requested block.
- fn block_exists(&mut self, h: &Hash) -> Result;
-
- /// `get_block` fetches the block with the given hash.
- ///
- /// Return the requested block. In case of failures, a `DB_FAILURES` will be return. Precisely, a `BLOCK_DNE`
- /// error will be returned if the requested block can't be found.
- ///
- /// Parameters:
- /// `h`: is the given hash of the requested block.
- fn get_block(&mut self, h: &Hash) -> Result;
-
- /// `get_block_from_height` fetches the block located at the given height.
- ///
- /// Return the requested block. In case of failures, a `DB_FAILURES` will be return. Precisely, a `BLOCK_DNE`
- /// error will be returned if the requested block can't be found.
- ///
- /// Parameters:
- /// `height`: is the height where the requested block is located.
- fn get_block_from_height(&mut self, height: u64) -> Result;
-
- /// `get_block_from_range` fetches the blocks located from and to the specified heights.
- ///
- /// Return the requested blocks. In case of failures, a `DB_FAILURES` will be return. Precisely, a `BLOCK_DNE`
- /// error will be returned if at least requested block can't be found. If the range requested past the end of the blockchain,
- /// an `ARITHMETIC_COUNT` error will be return.
- ///
- /// Parameters:
- /// `height_range`: is the range of height where the requested blocks are located.
- fn get_blocks_from_range(&mut self, height_range: Range) -> Result, DB_FAILURES>;
-
- /// `get_block_blob` fetches the block blob with the given hash.
- ///
- /// Return the requested block blob. In case of failures, a `DB_FAILURES` will be return. Precisely, a `BLOCK_DNE`
- /// error will be returned if the requested block can't be found.
- ///
- /// Parameters:
- /// `h`: is the given hash of the requested block.
- fn get_block_blob(&mut self, h: &Hash) -> Result;
-
- /// `get_block_blob_from_height` fetches the block blob located at the given height in the blockchain.
- ///
- /// Return the requested block blob. In case of failures, a `DB_FAILURES` will be return. Precisely, a `BLOCK_DNE`
- /// error will be returned if the requested block can't be found.
- ///
- /// Parameters:
- /// `height`: is the given height of the corresponding block blob to fetch.
- fn get_block_blob_from_height(&mut self, height: u64) -> Result;
-
- /// `get_block_header` fetches the block's header with the given hash.
- ///
- /// Return the requested block header. In case of failures, a `DB_FAILURES` will be return. Precisely, a `BLOCK_DNE`
- /// error will be returned if the requested block can't be found.
- ///
- /// Parameters:
- /// `h`: is the given hash of the requested block.
- fn get_block_header(&mut self, h: &Hash) -> Result;
-
- /// `get_block_hash_from_height` fetch block's hash located at the given height.
- ///
- /// Return the hash of the block with the given height. In case of failures, a DB_FAILURES will be return. Precisely, a `BLOCK_DNE`
- /// error will be returned if the requested block can't be found.
- ///
- /// Parameters:
- /// `height`: is the given height where the requested block is located.
- fn get_block_hash_from_height(&mut self, height: u64) -> Result;
-
- /// `get_blocks_hashes_from_range` fetch blocks' hashes located from, between and to the given heights.
- ///
- /// Return a collection of hases corresponding to the scoped blocks. In case of failures, a DB_FAILURES will be return. Precisely, a `BLOCK_DNE`
- /// error will be returned if at least one of the requested blocks can't be found.
- ///
- /// Parameters:
- /// `height`: is the given height where the requested block is located.
- fn get_blocks_hashes_from_range(&mut self, range: Range) -> Result, DB_FAILURES>;
-
- /// `get_top_block` fetch the last/top block of the blockchain
- ///
- /// Return the last/top block of the blockchain. In case of failures, a DB_FAILURES, will be return.
- ///
- /// No parameters is required.
- fn get_top_block(&mut self) -> Block;
-
- /// `get_top_block_hash` fetch the block's hash located at the top of the blockchain (the last one).
- ///
- /// Return the hash of the last block. In case of failures, a DB_FAILURES will be return.
- ///
- /// No parameters is required
- fn get_top_block_hash(&mut self) -> Result;
-
- // ! TODO: redefine the result & docs. see what could be improved. Do we really need this function?
- /// `get_blocks_from` fetches a variable number of blocks and transactions from the given height, in canonical blockchain order as long as it meets the parameters.
- ///
- /// Should return the blocks stored starting from the given height. The number of blocks returned is variable, based on the max_size defined. There will be at least `min_block_count`
- /// if possible, even if this contravenes max_tx_count. In case of failures, a `DB_FAILURES` error will be return.
- ///
- /// Parameters:
- /// `start_height`: is the given height to start from.
- /// `min_block_count`: is the minimum number of blocks to return. If there are fewer blocks, it'll return fewer blocks than the minimum.
- /// `max_block_count`: is the maximum number of blocks to return.
- /// `max_size`: is the maximum size of block/transaction data to return (can be exceeded on time if min_count is met).
- /// `max_tx_count`: is the maximum number of txes to return.
- /// `pruned`: is whether to return full or pruned tx data.
- /// `skip_coinbase`: is whether to return or skip coinbase transactions (they're in blocks regardless).
- /// `get_miner_tx_hash`: is whether to calculate and return the miner (coinbase) tx hash.
- fn get_blocks_from(
- &mut self,
- start_height: u64,
- min_block_count: u64,
- max_block_count: u64,
- max_size: usize,
- max_tx_count: u64,
- pruned: bool,
- skip_coinbase: bool,
- get_miner_tx_hash: bool,
- ) -> Result)>, DB_FAILURES>;
-
- /// `get_block_height` gets the height of the block with a given hash
- ///
- /// Return the requested height.
- fn get_block_height(&mut self, h: &Hash) -> Result;
-
- /// `get_block_weights` fetch the block's weight located at the given height.
- ///
- /// Return the requested block weight. In case of failures, a `DB_FAILURES` will be return. Precisely, a `BLOCK_DNE`
- /// error will be returned if the requested block can't be found.
- ///
- /// Parameters:
- /// `height`: is the given height where the requested block is located.
- fn get_block_weight(&mut self, height: u64) -> Result;
-
- /// `get_block_weights` fetch the last `count` blocks' weights.
- ///
- /// Return a collection of weights. In case of failures, a `DB_FAILURES` will be return. Precisely, an 'ARITHMETIC_COUNT'
- /// error will be returned if there are fewer than `count` blocks.
- ///
- /// Parameters:
- /// `start_height`: is the height to seek before collecting block weights.
- /// `count`: is the number of last blocks' weight to fetch.
- fn get_block_weights(&mut self, start_height: u64, count: usize) -> Result, DB_FAILURES>;
-
- /// `get_block_already_generated_coins` fetch a block's already generated coins
- ///
- /// Return the total coins generated as of the block with the given height. In case of failures, a `DB_FAILURES` will be return. Precisely, a `BLOCK_DNE`
- /// error will be returned if the requested block can't be found.
- ///
- /// Parameters:
- /// `height`: is the given height of the block to seek.
- fn get_block_already_generated_coins(&mut self, height: u64) -> Result;
-
- /// `get_block_long_term_weight` fetch a block's long term weight.
- ///
- /// Should return block's long term weight. In case of failures, a DB_FAILURES will be return. Precisely, a `BLOCK_DNE`
- /// error will be returned if the requested block can't be found.
- ///
- /// Parameters:
- /// `height`: is the given height where the requested block is located.
- fn get_block_long_term_weight(&mut self, height: u64) -> Result;
-
- /// `get_long_term_block_weights` fetch the last `count` blocks' long term weights
- ///
- /// Should return a collection of blocks' long term weights. In case of failures, a DB_FAILURES will be return. Precisely, a `BLOCK_DNE`
- /// error will be returned if the requested block can't be found. If there are fewer than `count` blocks, the returned collection will be
- /// smaller than `count`.
- ///
- /// Parameters:
- /// `start_height`: is the height to seek before collecting block weights.
- /// `count`: is the number of last blocks' long term weight to fetch.
- fn get_long_term_block_weights(&mut self, height: u64, count: usize) -> Result, DB_FAILURES>;
-
- /// `get_block_timestamp` fetch a block's timestamp.
- ///
- /// Should return the timestamp of the block with given height. In case of failures, a DB_FAILURES will be return. Precisely, a `BLOCK_DNE`
- /// error will be returned if the requested block can't be found.
- ///
- /// Parameters:
- /// `height`: is the given height where the requested block to fetch timestamp is located.
- fn get_block_timestamp(&mut self, height: u64) -> Result;
-
- /// `get_block_cumulative_rct_outputs` fetch a blocks' cumulative number of RingCT outputs
- ///
- /// Should return the number of RingCT outputs in the blockchain up to the blocks located at the given heights. In case of failures, a DB_FAILURES will be return. Precisely, a `BLOCK_DNE`
- /// error will be returned if the requested block can't be found.
- ///
- /// Parameters:
- /// `heights`: is the collection of height to check for RingCT distribution.
- fn get_block_cumulative_rct_outputs(&mut self, heights: Vec) -> Result, DB_FAILURES>;
-
- /// `get_top_block_timestamp` fetch the top block's timestamp
- ///
- /// Should reutnr the timestamp of the most recent block. In case of failures, a DB_FAILURES will be return.
- ///
- /// No parameters is required.
- fn get_top_block_timestamp(&mut self) -> Result;
-
- /// `correct_block_cumulative_difficulties` correct blocks cumulative difficulties that were incorrectly calculated due to the 'difficulty drift' bug
- ///
- /// Should return nothing. In case of failures, a DB_FAILURES will be return. Precisely, a `BLOCK_DNE`
- /// error will be returned if the requested block can't be found.
- ///
- /// Parameters:
- /// `start_height`: is the height of the block where the drifts start.
- /// `new_cumulative_difficulties`: is the collection of new cumulative difficulties to be stored
- fn correct_block_cumulative_difficulties(
- &mut self,
- start_height: u64,
- new_cumulative_difficulties: Vec,
- ) -> Result<(), DB_FAILURES>;
-
- // --------------------------------------------| Alt-Block |------------------------------------------------------------
-
- /// `add_alt_block` add a new alternative block.
- ///
- /// In case of failures, a DB_FAILURES will be return.
- ///
- /// Parameters:
- /// blkid: is the hash of the original block
- /// data: is the metadata for the block
- /// blob: is the blobdata of this alternative block.
- fn add_alt_block(&mut self, blkid: &Hash, data: &alt_block_data_t, blob: &Blobdata) -> Result<(), DB_FAILURES>;
-
- /// `get_alt_block` gets the specified alternative block.
- ///
- /// Return a tuple containing the blobdata of the alternative block and its metadata. In case of failures, a DB_FAILURES will be return.
- ///
- /// Parameters:
- /// `blkid`: is the hash of the requested alternative block.
- fn get_alt_block(&mut self, blkid: &Hash) -> Result<(alt_block_data_t, Blobdata), DB_FAILURES>;
-
- /// `remove_alt_block` remove the specified alternative block
- ///
- /// In case of failures, a DB_FAILURES will be return.
- ///
- /// Parameters:
- /// `blkid`: is the hash of the alternative block to remove.
- fn remove_alt_block(&mut self, blkid: &Hash) -> Result<(), DB_FAILURES>;
-
- /// `get_alt_block` gets the total number of alternative blocks stored
- ///
- /// In case of failures, a DB_FAILURES will be return.
- ///
- /// No parameters is required.
- fn get_alt_block_count(&mut self) -> Result;
-
- /// `drop_alt_block` drop all alternative blocks.
- ///
- /// In case of failures, a DB_FAILURES will be return.
- ///
- /// No parameters is required.
- fn drop_alt_blocks(&mut self) -> Result<(), DB_FAILURES>;
-
- // --------------------------------------------| TxPool |------------------------------------------------------------
-
- /// `add_txpool_tx` add a Pool's transaction to the database.
- ///
- /// In case of failures, a DB_FAILURES will be return.
- ///
- /// Parameters:
- /// `txid`: is the hash of the transaction to add.
- /// `blob`: is the blobdata of the transaction to add.
- /// `details`: is the metadata of the transaction pool at this specific transaction.
- fn add_txpool_tx(&mut self, txid: &Hash, blob: &BlobdataRef, details: &txpool_tx_meta_t)
- -> Result<(), DB_FAILURES>;
-
- /// `update_txpool_tx` replace pool's transaction metadata.
- ///
- /// In case of failures, a DB_FAILURES will be return.
- ///
- /// Parameters:
- /// `txid`: is the hash of the transaction to edit
- /// `details`: is the new metadata to insert.
- fn update_txpool_tx(&mut self, txid: &monero::Hash, details: &txpool_tx_meta_t) -> Result<(), DB_FAILURES>;
-
- /// `get_txpool_tx_count` gets the number of transactions in the txpool.
- ///
- /// Return the number of transaction in the txpool. In case of failures, a DB_FAILURES will be return.
- ///
- /// Parameters:
- /// `tx_category`: is the relay's category where the tx are coming from. (RelayCategory::broadcasted by default)
- fn get_txpool_tx_count(&mut self, tx_category: RelayCategory) -> Result;
-
- /// `txpool_has_tx`checks if the specified transaction exist in the transaction's pool and if it belongs
- /// to the specified category.
- ///
- /// Return `true` if the condition above are met, `false otherwise`. In case of failures, a DB_FAILURES will be return.
- ///
- /// Parameters:
- /// `txid`: is the hash of the transaction to check for
- /// `tx_category`: is the relay's category where the tx is supposed to come from.
- fn txpool_has_tx(&mut self, txid: &Hash, tx_category: &RelayCategory) -> Result;
-
- /// `remove_txpool_tx` remove the specified transaction from the transaction pool.
- ///
- /// In case of failures, a DB_FAILURES will be return.
- ///
- /// Parameters:
- /// `txid`: is the hash of the transaction to remove.
- fn remove_txpool_tx(&mut self, txid: &Hash) -> Result<(), DB_FAILURES>;
-
- /// `get_txpool_tx_meta` gets transaction's pool metadata recorded at the specified transaction.
- ///
- /// In case of failures, a DB_FAILURES will be return.
- ///
- /// Parameters:
- /// `txid`: is the hash of metadata's transaction hash.
- fn get_txpool_tx_meta(&mut self, txid: &Hash) -> Result;
-
- /// `get_txpool_tx_blob` gets the txpool transaction's blob.
- ///
- /// In case of failures, a DB_FAILURES will be return.
- ///
- /// Parameters:
- /// `txid`: is the hash of the transaction to fetch blobdata from.
- /// `tx_category`: is the relay's category where the tx are coming from. < monerod note: for filtering out hidden/private txes.
- fn get_txpool_tx_blob(&mut self, txid: &Hash, tx_category: RelayCategory) -> Result;
-
- /// `txpool_tx_matches_category` checks if the corresponding transaction belongs to the specified category.
- ///
- /// Return `true` if the transaction belongs to the category, `false` otherwise. In case of failures, a DB_FAILURES will be return.
- ///
- /// Parameters:
- /// `tx_hash`: is the hash of the transaction to lookup.
- /// `category`: is the relay's category to check.
- fn txpool_tx_matches_category(&mut self, tx_hash: &Hash, category: RelayCategory) -> Result;
-}
-
-// functions defined as useless : init_options(), is_open(), reset_stats(), show_stats(), open(), close(), get_output_histogram(), safesyncmode, get_filenames(), get_db_name(), remove_data_file(), lock(), unlock(), is_read_only(), get_database_size(), get_output_distribution(), set_auto_remove_logs(), check_hard_fork_info(), drop_hard_fork_info(), get_indexing_base();
diff --git a/blockchain_db/src/rocksdb.rs b/blockchain_db/src/rocksdb.rs
deleted file mode 100644
index 3866e8e5..00000000
--- a/blockchain_db/src/rocksdb.rs
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright (C) 2023 Cuprate Contributors
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Affero General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Affero General Public License for more details.
-//
-// You should have received a copy of the GNU Affero General Public License
-// along with this program. If not, see .
-
-//!
-//! RocksDB implementation.
-//!
-//! Database Schema:
-//! ---------------------------------------
-//! Column | Key | Data
-//! ---------------------------------------
-//! *block*------------------------------------------------------
-//!
-//! blocks height {blob}
-//! heights hash height
-//! b_metadata height {b_metdata}
-//!
-//! *transactions*-----------------------------------------------
-//!
-//! tx_prefix tx ID {blob}
-//! tx_prunable tx ID {blob}
-//! tx_hash tx ID hash
-//! tx_opti_h hash height
-//! tx_outputs tx ID {amount,output,indices}
-//!
-//! *outputs*----------------------------------------------------
-//!
-//! ouputs_txs op ID {tx hash, l_index}
-//! outputs_am amount {amount output index, metdata}
-//!
-//! *spent keys*--------------------------------------------------
-//!
-//! spent_keys hash well... obvious?
-//!
-//! *tx pool*------------------------------------------------------
-//!
-//! txp_meta hash {txp_metadata}
-//! txp_blob hash {blob}
-//!
-//! *alt blocks*----------------------------------------------------
-//!
-//! alt_blocks hash {bock data, block blob}
-
-// Defining tables
-const CF_BLOCKS: &str = "blocks";
-const CF_HEIGHTS: &str = "heights";
-const CF_BLOCK_METADATA: &str = "b_metadata";
-const CF_TX_PREFIX: &str = "tx_prefix";
-const CF_TX_PRUNABLE: &str = "tx_prunable";
-const CF_TX_HASH: &str = "tx_hash";
-const CF_TX_OPTI_H: &str = "tx_opti_h";
-const CF_TX_OUTPUTS: &str = "tx_outputs";
-const CF_OUTPUTS_TXS: &str = "outputs_txs";
\ No newline at end of file
diff --git a/cuprate/Cargo.toml b/cuprate/Cargo.toml
new file mode 100644
index 00000000..4379627e
--- /dev/null
+++ b/cuprate/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "cuprate-bin"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+clap = { version = "*", features = [] }
+clap_complete = "*"
+tracing = { workspace = true }
+tracing-subscriber = { workspace = true }
\ No newline at end of file
diff --git a/cuprate/src/cli.rs b/cuprate/src/cli.rs
new file mode 100644
index 00000000..9e7c8d7a
--- /dev/null
+++ b/cuprate/src/cli.rs
@@ -0,0 +1,50 @@
+use crate::CUPRATE_VERSION;
+use clap::{value_parser, Arg, ArgAction, ArgGroup, ArgMatches, Command};
+use tracing::{event, span, Level, Span};
+
+/// This function simply contains clap arguments
+pub fn args() -> ArgMatches {
+ Command::new("Cuprate")
+ .version(CUPRATE_VERSION)
+ .author("Cuprate's contributors")
+ .about("An upcoming experimental, modern, and secure monero node")
+ // Generic Arguments
+ .arg(
+ Arg::new("log")
+ .long("log-level")
+ .value_name("Level")
+ .help("Set the log level")
+ .value_parser(value_parser!(u8))
+ .default_value("1")
+ .long_help("Set the log level. There is 3 log level: <1~INFO, 2~DEBUG >3~TRACE.")
+ .required(false)
+ .action(ArgAction::Set),
+ )
+ .get_matches()
+}
+
+/// This function initialize the FmtSubscriber used by tracing to display event in the console. It send back a span used during runtime.
+pub fn init(matches: &ArgMatches) -> Span {
+ // Getting the log level from args
+ let log_level = matches.get_one::("log").unwrap();
+ let level_filter = match log_level {
+ 2 => Level::DEBUG,
+ x if x > &2 => Level::TRACE,
+ _ => Level::INFO,
+ };
+
+ // Initializing tracing subscriber and runtime span
+ let subscriber = tracing_subscriber::FmtSubscriber::builder()
+ .with_max_level(level_filter)
+ .with_target(false)
+ .finish();
+ tracing::subscriber::set_global_default(subscriber).expect("Failed to set global subscriber for tracing. We prefer to abort the node since without it you have no output in the console");
+ let runtime_span = span!(Level::INFO, "Runtime");
+ let _guard = runtime_span.enter();
+
+ // Notifying log level
+ event!(Level::INFO, "Log level set to {}", level_filter);
+
+ drop(_guard);
+ runtime_span
+}
diff --git a/cuprate/src/main.rs b/cuprate/src/main.rs
new file mode 100644
index 00000000..eb378f34
--- /dev/null
+++ b/cuprate/src/main.rs
@@ -0,0 +1,13 @@
+use tracing::{event, info, span, Level};
+
+pub mod cli;
+
+const CUPRATE_VERSION: &str = "0.1.0";
+
+fn main() {
+ // Collecting options
+ let matches = cli::args();
+
+ // Initializing tracing subscriber and runtime span
+ let _runtime_span = cli::init(&matches);
+}
diff --git a/database/Cargo.toml b/database/Cargo.toml
new file mode 100644
index 00000000..37c49ae4
--- /dev/null
+++ b/database/Cargo.toml
@@ -0,0 +1,33 @@
+[package]
+name = "cuprate-database"
+version = "0.0.1"
+edition = "2021"
+license = "AGPL-3.0-only"
+
+# All Contributors on github
+authors=[
+ "SyntheticBird45 <@someoneelse495495:matrix.org>",
+ "Boog900"
+ ]
+
+[features]
+mdbx = ["dep:libmdbx"]
+hse = []
+
+[dependencies]
+monero = {workspace = true, features = ["serde"]}
+tiny-keccak = { version = "2.0", features = ["sha3"] }
+serde = { workspace = true}
+thiserror = {workspace = true }
+bincode = { workspace = true }
+libmdbx = { version = "0.3.1", optional = true }
+
+[build]
+linker="clang"
+rustflags=[
+ "-Clink-arg=-fuse-ld=mold",
+ "-Zcf-protection=full",
+ "-Zsanitizer=cfi",
+ "-Crelocation-model=pie",
+ "-Cstack-protector=all",
+]
\ No newline at end of file
diff --git a/blockchain_db/LICENSE b/database/LICENSE
similarity index 100%
rename from blockchain_db/LICENSE
rename to database/LICENSE
diff --git a/database/src/encoding.rs b/database/src/encoding.rs
new file mode 100644
index 00000000..aa4681d6
--- /dev/null
+++ b/database/src/encoding.rs
@@ -0,0 +1,78 @@
+//! ### Encoding module
+//! The encoding module contains a trait that permit compatibility between `monero-rs` consensus encoding/decoding logic and `bincode` traits.
+//! The database tables only accept types that implement [`bincode::Encode`] and [`bincode::Decode`] and since we can't implement these on `monero-rs` types directly
+//! we use a wrapper struct `Compat` that permit us to use `monero-rs`'s `consensus_encode`/`consensus_decode` functions under bincode traits.
+//! The choice of using `bincode` comes from performance measurement at encoding. Sometimes `bincode` implementations was 5 times faster than `monero-rs` impl.
+
+use bincode::{de::read::Reader, enc::write::Writer};
+use monero::consensus::{Decodable, Encodable};
+use std::{fmt::Debug, io::Read, ops::Deref};
+
+#[derive(Debug, Clone)]
+/// A single-tuple struct, used to contains monero-rs types that implement [`monero::consensus::Encodable`] and [`monero::consensus::Decodable`]
+pub struct Compat(pub T);
+
+/// A wrapper around a [`bincode::de::read::Reader`] type. Permit us to use [`std::io::Read`] and feed monero-rs functions with an actual `&[u8]`
+pub struct ReaderCompat<'src, R: Reader>(pub &'src mut R);
+
+// Actual implementation of `std::io::read` for `bincode`'s `Reader` types
+impl<'src, R: Reader> Read for ReaderCompat<'src, R> {
+ fn read(&mut self, buf: &mut [u8]) -> std::io::Result {
+ self.0
+ .read(buf)
+ .map_err(|_| std::io::Error::new(std::io::ErrorKind::Other, "bincode reader Error"))?;
+ Ok(buf.len())
+ }
+}
+
+// Convenient implementation. `Deref` and `From`
+impl Deref for Compat {
+ type Target = T;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl From for Compat {
+ fn from(value: T) -> Self {
+ Compat(value)
+ }
+}
+
+// TODO: Investigate specialization optimization
+// Implementation of `bincode::Decode` for monero-rs `Decodable` type
+impl bincode::Decode for Compat {
+ fn decode(
+ decoder: &mut D,
+ ) -> Result {
+ Ok(Compat(
+ Decodable::consensus_decode(&mut ReaderCompat(decoder.reader()))
+ .map_err(|_| bincode::error::DecodeError::Other("Monero-rs decoding failed"))?,
+ ))
+ }
+}
+
+// Implementation of `bincode::BorrowDecode` for monero-rs `Decodable` type
+impl<'de, T: Encodable + Decodable + Debug> bincode::BorrowDecode<'de> for Compat {
+ fn borrow_decode>(
+ decoder: &mut D,
+ ) -> Result {
+ Ok(Compat(
+ Decodable::consensus_decode(&mut ReaderCompat(decoder.borrow_reader()))
+ .map_err(|_| bincode::error::DecodeError::Other("Monero-rs decoding failed"))?,
+ ))
+ }
+}
+
+// Implementation of `bincode::Encode` for monero-rs `Encodable` type
+impl bincode::Encode for Compat {
+ fn encode(
+ &self,
+ encoder: &mut E,
+ ) -> Result<(), bincode::error::EncodeError> {
+ let writer = encoder.writer();
+ let buf = monero::consensus::serialize(&self.0);
+ writer.write(&buf)
+ }
+}
diff --git a/database/src/error.rs b/database/src/error.rs
new file mode 100644
index 00000000..25b1f8d8
--- /dev/null
+++ b/database/src/error.rs
@@ -0,0 +1,53 @@
+//! ### Error module
+//! This module contains all errors abstraction used by the database crate. By implementing [`From`] to the specific errors of storage engine crates, it let us
+//! handle more easily any type of error that can happen. This module does **NOT** contain interpretation of these errors, as these are defined for Blockchain abstraction. This is another difference
+//! from monerod which interpret these errors directly in its database functions:
+//! ```cpp
+//! /**
+//! * @brief A base class for BlockchainDB exceptions
+//! */
+//! class DB_EXCEPTION : public std::exception
+//! ```
+//! see `blockchain_db/blockchain_db.h` in monerod `src/` folder for more details.
+
+#[derive(thiserror::Error, Debug)]
+/// `DB_FAILURES` is an enum for backend-agnostic, internal database errors. The `From` Trait must be implemented to the specific backend errors to match DB_FAILURES.
+pub enum DB_FAILURES {
+ #[error("MDBX returned an error {0}")]
+ MDBX_Error(#[from] libmdbx::Error),
+
+ #[error("\n Failed to encode some data : `{0}`")]
+ SerializeIssue(DB_SERIAL),
+
+ #[error("\nObject already exist in the database : {0}")]
+ AlreadyExist(&'static str),
+
+ #[error("NotFound? {0}")]
+ NotFound(&'static str),
+
+ #[error("\n `{0}`")]
+ Other(&'static str),
+
+ #[error(
+ "\n A transaction tried to commit to the db, but failed."
+ )]
+ FailedToCommit,
+}
+
+#[derive(thiserror::Error, Debug)]
+pub enum DB_SERIAL {
+ #[error("An object failed to be serialized into bytes. It is likely an issue from monero-rs library. Please report this error on cuprate's github : https://github.com/Cuprate/cuprate/issues")]
+ ConsensusEncode,
+
+ #[error("Bytes failed to be deserialized into the requested object. It is likely an issue from monero-rs library. Please report this error on cuprate's github : https://github.com/Cuprate/cuprate/issues")]
+ ConsensusDecode(Vec),
+
+ #[error("monero-rs encoding|decoding logic failed : {0}")]
+ MoneroEncode(#[from] monero::consensus::encode::Error),
+
+ #[error("Bincode failed to decode a type from the database : {0}")]
+ BincodeDecode(#[from] bincode::error::DecodeError),
+
+ #[error("Bincode failed to encode a type for the database : {0}")]
+ BincodeEncode(#[from] bincode::error::EncodeError),
+}
diff --git a/database/src/hse.rs b/database/src/hse.rs
new file mode 100644
index 00000000..2d07b3f7
--- /dev/null
+++ b/database/src/hse.rs
@@ -0,0 +1,11 @@
+/* There is nothing here as no wrapper exist for HSE yet */
+
+/* KVS supported functions :
+-------------------------------------
+hse_kvs_delete
+hse_kvs_get
+hse_kvs_name_get
+hse_kvs_param_get
+hse_kvs_prefix_delete
+hse_kvs_put
+*/
\ No newline at end of file
diff --git a/database/src/interface.rs b/database/src/interface.rs
new file mode 100644
index 00000000..cacd0fd6
--- /dev/null
+++ b/database/src/interface.rs
@@ -0,0 +1,1036 @@
+//! ### Interface module
+//! This module contains all the implementations of the database interface.
+//! These are all the functions that can be executed through DatabaseRequest.
+//!
+//! The following functions have been separated through 6 categories:
+//! -| Blockchain |-
+//! -| Blocks |-
+//! -| Transactions |-
+//! -| Outputs |-
+//! -| SpentKeys |-
+//! -| Categories |-
+
+// TODO: add_transaction() not finished due to ringct zeroCommit missing function
+// TODO: in add_transaction_data() Investigate unprunable_size == 0 condition of monerod
+// TODO: Do we need correct_block_cumulative_difficulties()
+// TODO: remove_tx_outputs() can be done otherwise since we don't use global output index
+// TODO: Check all documentations
+
+use crate::{
+ database::{Database, Interface},
+ error::DB_FAILURES,
+ table::{self},
+ transaction::{self, DupCursor, DupWriteCursor, Transaction, WriteCursor, WriteTransaction},
+ types::{
+ calculate_prunable_hash, get_transaction_prunable_blob, AltBlock, BlockMetadata,
+ OutputMetadata, TransactionPruned, TxIndex, TxOutputIdx,
+ },
+ BINCODE_CONFIG,
+};
+use monero::{
+ blockdata::transaction::KeyImage, cryptonote::hash::Hashable, util::ringct::Key, Block,
+ BlockHeader, Hash, TxIn, TxOut,
+};
+
+// Implementation of Interface
+impl<'service, D: Database<'service>> Interface<'service, D> {
+ // --------------------------------| Blockchain |--------------------------------
+
+ /// `height` fetch the current blockchain height.
+ ///
+ /// Return the current blockchain height. In case of failures, a DB_FAILURES will be return.
+ ///
+ /// No parameters is required.
+ fn height(&'service self) -> Result {
+ let ro_tx = self.db.tx().map_err(Into::into)?;
+ ro_tx.num_entries::().map(|n| n as u64)
+ }
+
+ // ----------------------------------| Blocks |---------------------------------
+
+ /// `add_block` add the block and metadata to the db.
+ ///
+ /// In case of failures, a `DB_FAILURES`
+ ///
+ /// Parameters:
+ /// `blk`: is the block to be added
+ /// `txs`: is the collection of transactions related to this block
+ /// `block_weight`: is the weight of the block (data's total)
+ /// `long_term_block_weight`: is the long term weight of the block (data's total)
+ /// `cumulative_difficulty`: is the accumulated difficulty at this block.
+ /// `coins_generated` is the number of coins generated after this block.
+ fn add_block(
+ &'service self,
+ blk: Block,
+ txs: Vec,
+ block_weight: u64,
+ long_term_block_weight: u64,
+ cumulative_difficulty: u128,
+ coins_generated: u64,
+ ) -> Result<(), DB_FAILURES> {
+ // *sanity*
+ if blk.tx_hashes.len() != txs.len() {
+ return Err(DB_FAILURES::Other("sanity : Inconsistent tx/hashed sizes"));
+ }
+
+ let blk_hash = blk.id();
+
+ // let parent_height = self.height()?;
+
+ let mut num_rct_outs = 0u64;
+ self.add_transaction(blk.miner_tx.clone())?;
+
+ if blk.miner_tx.prefix.version.0 == 2 {
+ num_rct_outs += blk.miner_tx.prefix.outputs.len() as u64;
+ }
+
+ // let mut tx_hash = Hash::null();
+ for tx in txs.into_iter()
+ /*.zip(0usize..)*/
+ {
+ // tx_hash = blk.tx_hashes[tx.1];
+ for out in tx.prefix.outputs.iter() {
+ if out.amount.0 == 0 {
+ num_rct_outs += 1;
+ }
+ }
+ self.add_transaction(tx /*.0*/)?;
+ }
+
+ let blk_metadata = BlockMetadata {
+ timestamp: blk.header.timestamp.0,
+ total_coins_generated: coins_generated,
+ weight: block_weight,
+ cumulative_difficulty,
+ block_hash: blk_hash.into(),
+ cum_rct: num_rct_outs, // num_rct_outs here is the rct outs of the block only. The cumulative rct will be added in `add_block_data` fn
+ long_term_block_weight,
+ };
+
+ self.add_block_data(blk, blk_metadata)
+ }
+
+ /// `add_block_data` add the actual block's data and metadata to the db. Underlying function of `add_block`
+ ///
+ /// In case of failures, a `DB_FAILURES` will be return.
+ ///
+ /// Parameters:
+ /// `blk`: is the block to add
+ /// `blk_metadata`: is the block's metadata to add
+ fn add_block_data(
+ &'service self,
+ blk: Block,
+ mut blk_metadata: BlockMetadata,
+ ) -> Result<(), DB_FAILURES> {
+ let height = self.height()?;
+
+ let mut cursor_blockhash = self.write_cursor_dup::()?;
+ let mut cursor_blockmetadata = self.write_cursor_dup::()?;
+
+ if cursor_blockhash
+ .get_dup(&(), &blk_metadata.block_hash)?
+ .is_some()
+ {
+ return Err(DB_FAILURES::AlreadyExist(
+ "Attempting to insert a block already existent in the database",
+ ))?;
+ }
+
+ if height > 0 {
+ let parent_height: u64 = cursor_blockhash
+ .get_dup(&(), &blk.header.prev_id.into())?
+ .ok_or(DB_FAILURES::NotFound("Can't find parent block"))?;
+
+ if parent_height != height - 1 {
+ return Err(DB_FAILURES::Other("Top block is not a new block's parent"));
+ }
+ }
+
+ if blk.header.major_version.0 > 3 {
+ let last_height = height - 1;
+
+ let parent_cum_rct = self.get_block_cumulative_rct_outputs(last_height)?;
+ blk_metadata.cum_rct += parent_cum_rct;
+ }
+ self.put::(&height, &blk.into())?;
+ cursor_blockhash.put_cursor_dup(&(), &blk_metadata.block_hash, &height)?;
+ cursor_blockmetadata.put_cursor_dup(&(), &height, &blk_metadata)
+ // blockhfversion missing but do we really need this table?
+ }
+
+ /// `pop_block` pops the top block off the blockchain.
+ ///
+ /// Return the block that was popped. In case of failures, a `DB_FAILURES` will be return.
+ ///
+ /// No parameters is required.
+ fn pop_block(&'service self) -> Result {
+ // First we delete block from table
+ let height = self.height()?;
+ if height == 0 {
+ return Err(DB_FAILURES::Other(
+ "Attempting to remove block from an empty blockchain",
+ ));
+ }
+
+ let mut cursor_blockhash = self.write_cursor_dup::()?;
+ let mut cursor_blockmetadata = self.write_cursor_dup::()?;
+
+ let blk = self
+ .get::(&(height - 1))?
+ .ok_or(DB_FAILURES::NotFound(
+ "Attempting to remove block that's not in the db",
+ ))?
+ .0;
+
+ let hash = cursor_blockmetadata
+ .get_dup(&(), &(height - 1))?
+ .ok_or(DB_FAILURES::NotFound("Failed to retrieve block metadata"))?
+ .block_hash;
+
+ self.delete::(&(height - 1), &None)?;
+ if cursor_blockhash.get_dup(&(), &hash)?.is_some() {
+ cursor_blockhash.del()?;
+ }
+
+ cursor_blockmetadata.del()?;
+
+ // Then we delete all its revelent txs
+ for tx_hash in blk.tx_hashes.iter() {
+ // 1 more condition in monerod TODO:
+ self.remove_transaction(*tx_hash)?;
+ }
+ self.remove_transaction(blk.miner_tx.hash())?;
+ Ok(blk)
+ }
+
+ /// `blocks_exists` check if the given block exists
+ ///
+ /// Return `true` if the block exist, `false` otherwise. In case of failures, a `DB_FAILURES` will be return.
+ ///
+ /// Parameters:
+ /// `hash`: is the given hash of the requested block.
+ fn block_exists(&'service self, hash: Hash) -> Result {
+ let ro_tx = self.db.tx().map_err(Into::into)?;
+ let mut cursor_blockhash = ro_tx.cursor_dup::()?;
+ Ok(cursor_blockhash.get_dup(&(), &hash.into())?.is_some())
+ }
+
+ /// `get_block_hash` fetch the block's hash located at the give height.
+ ///
+ /// Return the hash of the last block. In case of failures, a DB_FAILURES will be return.
+ ///
+ /// No parameters is required
+ fn get_block_hash(&'service self, height: u64) -> Result {
+ let ro_tx = self.db.tx().map_err(Into::into)?;
+ let mut cursor_blockmetadata = ro_tx.cursor_dup::()?;
+ let metadata = cursor_blockmetadata
+ .get_dup(&(), &height)?
+ .ok_or(DB_FAILURES::NotFound("Failed to find block's metadata"))?;
+
+ Ok(metadata.block_hash.0)
+ }
+
+ /// `get_block_height` gets the height of the block with a given hash
+ ///
+ /// Return the requested height.
+ fn get_block_height(&'service self, hash: Hash) -> Result {
+ let ro_tx = self.db.tx().map_err(Into::into)?;
+ let mut cursor_blockhash = ro_tx.cursor_dup::()?;
+
+ cursor_blockhash
+ .get_dup(&(), &hash.into())?
+ .ok_or(DB_FAILURES::NotFound("Failed to find block height"))
+ }
+
+ /// `get_block_weights` fetch the block's weight located at the given height.
+ ///
+ /// Return the requested block weight. In case of failures, a `DB_FAILURES` will be return.
+ ///
+ /// Parameters:
+ /// `height`: is the given height where the requested block is located.
+ fn get_block_weight(&'service self, height: u64) -> Result {
+ let ro_tx = self.db.tx().map_err(Into::into)?;
+ let mut cursor_blockmetadata = ro_tx.cursor_dup::()?;
+
+ let metadata = cursor_blockmetadata
+ .get_dup(&(), &height)?
+ .ok_or(DB_FAILURES::NotFound("Failed to find block's metadata"))?;
+
+ Ok(metadata.weight)
+ }
+
+ /// `get_block_already_generated_coins` fetch a block's already generated coins
+ ///
+ /// Return the total coins generated as of the block with the given height. In case of failures, a `DB_FAILURES` will be return.
+ ///
+ /// Parameters:
+ /// `height`: is the given height of the block to seek.
+ fn get_block_already_generated_coins(&'service self, height: u64) -> Result {
+ let ro_tx = self.db.tx().map_err(Into::into)?;
+ let mut cursor_blockmetadata = ro_tx.cursor_dup::()?;
+
+ let metadata = cursor_blockmetadata
+ .get_dup(&(), &height)?
+ .ok_or(DB_FAILURES::NotFound("Failed to find block's metadata"))?;
+
+ Ok(metadata.total_coins_generated)
+ }
+
+ /// `get_block_long_term_weight` fetch a block's long term weight.
+ ///
+ /// Should return block's long term weight. In case of failures, a DB_FAILURES will be return.
+ ///
+ /// Parameters:
+ /// `height`: is the given height where the requested block is located.
+ fn get_block_long_term_weight(&'service self, height: u64) -> Result {
+ let ro_tx = self.db.tx().map_err(Into::into)?;
+ let mut cursor_blockmetadata = ro_tx.cursor_dup::()?;
+
+ let metadata = cursor_blockmetadata
+ .get_dup(&(), &height)?
+ .ok_or(DB_FAILURES::NotFound("Failed to find block's metadata"))?;
+
+ Ok(metadata.long_term_block_weight)
+ }
+
+ /// `get_block_timestamp` fetch a block's timestamp.
+ ///
+ /// Should return the timestamp of the block with given height. In case of failures, a DB_FAILURES will be return.
+ ///
+ /// Parameters:
+ /// `height`: is the given height where the requested block to fetch timestamp is located.
+ fn get_block_timestamp(&'service self, height: u64) -> Result {
+ let ro_tx = self.db.tx().map_err(Into::into)?;
+ let mut cursor_blockmetadata = ro_tx.cursor_dup::()?;
+
+ let metadata = cursor_blockmetadata
+ .get_dup(&(), &height)?
+ .ok_or(DB_FAILURES::NotFound("Failed to find block's metadata"))?;
+
+ Ok(metadata.timestamp)
+ }
+
+ /// `get_block_cumulative_rct_outputs` fetch a blocks' cumulative number of RingCT outputs
+ ///
+ /// Should return the number of RingCT outputs in the blockchain up to the blocks located at the given heights. In case of failures, a DB_FAILURES will be return.
+ ///
+ /// Parameters:
+ /// `height`: is the height to check for RingCT distribution.
+ fn get_block_cumulative_rct_outputs(&'service self, height: u64) -> Result {
+ let ro_tx = self.db.tx().map_err(Into::into)?;
+ let mut cursor_blockmetadata = ro_tx.cursor_dup::()?;
+
+ let metadata = cursor_blockmetadata
+ .get_dup(&(), &height)?
+ .ok_or(DB_FAILURES::NotFound("Failed to find block's metadata"))?;
+
+ Ok(metadata.cum_rct)
+ }
+
+ fn get_block(&'service self, hash: Hash) -> Result {
+ let ro_tx = self.db.tx().map_err(Into::into)?;
+ let mut cursor_blockhash = ro_tx.cursor_dup::()?;
+
+ let blk_height: u64 = cursor_blockhash
+ .get_dup(&(), &hash.into())?
+ .ok_or(DB_FAILURES::NotFound("Can't find block"))?;
+
+ Ok(ro_tx
+ .get::(&blk_height)?
+ .ok_or(DB_FAILURES::NotFound("Can't find block"))?
+ .0)
+ }
+
+ fn get_block_from_height(&'service self, height: u64) -> Result {
+ let ro_tx = self.db.tx().map_err(Into::into)?;
+
+ Ok(ro_tx
+ .get::(&height)?
+ .ok_or(DB_FAILURES::NotFound("Can't find block"))?
+ .0)
+ }
+
+ /// `get_block_header` fetches the block's header with the given hash.
+ ///
+ /// Return the requested block header. In case of failures, a `DB_FAILURES` will be return. Precisely, a `BLOCK_DNE`
+ /// error will be returned if the requested block can't be found.
+ ///
+ /// Parameters:
+ /// `hash`: is the given hash of the requested block.
+ fn get_block_header(&'service self, hash: Hash) -> Result {
+ let ro_tx = self.db.tx().map_err(Into::into)?;
+ let mut cursor_blockhash = ro_tx.cursor_dup::()?;
+
+ let blk_height: u64 = cursor_blockhash
+ .get_dup(&(), &hash.into())?
+ .ok_or(DB_FAILURES::NotFound("Can't find block"))?;
+
+ Ok(ro_tx
+ .get::(&blk_height)?
+ .ok_or(DB_FAILURES::NotFound("Can't find block"))?
+ .0
+ .header)
+ }
+
+ fn get_block_header_from_height(
+ &'service self,
+ height: u64,
+ ) -> Result {
+ let ro_tx = self.db.tx().map_err(Into::into)?;
+
+ Ok(ro_tx
+ .get::(&(height - 1))?
+ .ok_or(DB_FAILURES::NotFound("Can't find block"))?
+ .0
+ .header)
+ }
+
+ /// `get_top_block` fetch the last/top block of the blockchain
+ ///
+ /// Return the last/top block of the blockchain. In case of failures, a DB_FAILURES, will be return.
+ ///
+ /// No parameters is required.
+ fn get_top_block(&'service self) -> Result {
+ let ro_tx = self.db.tx().map_err(Into::into)?;
+
+ let blk_height = self.height()?;
+
+ Ok(ro_tx
+ .get::(&blk_height)?
+ .ok_or(DB_FAILURES::NotFound("Can't find block"))?
+ .0)
+ }
+
+ /// `get_top_block_hash` fetch the block's hash located at the top of the blockchain (the last one).
+ ///
+ /// Return the hash of the last block. In case of failures, a DB_FAILURES will be return.
+ ///
+ /// No parameters is required
+ fn get_top_block_hash(&'service self) -> Result {
+ let ro_tx = self.db.tx().map_err(Into::into)?;
+ let height = self.height()?;
+ let mut cursor_blockmetadata = ro_tx.cursor_dup::()?;
+
+ let metadata = cursor_blockmetadata
+ .get_dup(&(), &(height - 1))?
+ .ok_or(DB_FAILURES::NotFound("Failed to find block's metadata"))?;
+
+ Ok(metadata.block_hash.0)
+ }
+
+ // ------------------------------| Transactions |-----------------------------
+
+ /// `add_transaction` add the corresponding transaction and its hash to the specified block.
+ ///
+ /// In case of failures, a DB_FAILURES will be return. Precisely, a TX_EXISTS will be returned if the
+ /// transaction to be added already exists in the database.
+ ///
+ /// Parameters:
+ /// `blk_hash`: is the hash of the block which inherit the transaction
+ /// `tx`: is obviously the transaction to add
+ /// `tx_hash`: is the hash of the transaction.
+ /// `tx_prunable_hash_ptr`: is the hash of the prunable part of the transaction.
+ fn add_transaction(&'service self, tx: monero::Transaction) -> Result<(), DB_FAILURES> {
+ let is_coinbase: bool = tx.prefix.inputs.is_empty();
+ let tx_hash = tx.hash();
+
+ let mut tx_prunable_blob = Vec::new();
+ get_transaction_prunable_blob(&tx, &mut tx_prunable_blob).unwrap();
+
+ let tx_prunable_hash: Option = calculate_prunable_hash(&tx, &tx_prunable_blob);
+
+ for txin in tx.prefix.inputs.iter() {
+ if let TxIn::ToKey {
+ amount: _,
+ key_offsets: _,
+ k_image,
+ } = txin
+ {
+ self.add_spent_key(k_image.clone())?;
+ } else {
+ return Err(DB_FAILURES::Other(
+ "Unsupported input type, aborting transaction addition",
+ ));
+ }
+ }
+
+ let tx_id =
+ self.add_transaction_data(tx.clone(), tx_prunable_blob, tx_hash, tx_prunable_hash)?;
+
+ let tx_num_outputs = tx.prefix.outputs.len();
+ let amount_output_dinces: Vec = Vec::with_capacity(tx_num_outputs);
+
+ for txout in tx.prefix.outputs.iter().zip(0..tx_num_outputs) {
+ if is_coinbase && tx.prefix.version.0 == 2 {
+ let commitment: Option = None;
+ // ZeroCommit is from RingCT Module, not finishable yet
+ }
+ }
+ todo!()
+ }
+
+ /// `add_transaction_data` add the specified transaction data to its storage.
+ ///
+ /// It only add the transaction blob and tx's metadata, not the collection of outputs.
+ ///
+ /// Return the hash of the transaction added. In case of failures, a DB_FAILURES will be return.
+ ///
+ /// Parameters:
+ /// `tx`: is the transaction to add
+ /// `tx_prunable_blob`; is its prunable blob.
+ /// `tx_hash`: is the transaction's hash
+ /// `tx_prunable_hash`: is the hash of the prunable part of the transaction
+ fn add_transaction_data(
+ &'service self,
+ tx: monero::Transaction,
+ tx_prunable_blob: Vec,
+ tx_hash: Hash,
+ tx_prunable_hash: Option,
+ ) -> Result {
+ // Checking if the transaction already exist in the database
+ let res = self.get::(&tx_hash.into())?;
+ if res.is_some() {
+ return Err(DB_FAILURES::AlreadyExist(
+ "Attempting to add transaction that's already in the db",
+ ));
+ }
+
+ // Inserting tx index in table::txsindetifier
+ let height = self.height()?;
+ let tx_id = self.get_num_tx()?;
+
+ let txindex = TxIndex {
+ tx_id,
+ unlock_time: tx.prefix.unlock_time.0,
+ height,
+ };
+
+ self.put::(&tx_hash.into(), &txindex)?;
+
+ // TODO: Investigate unprunable_size == 0 condition
+ // Inserting tx pruned part in table::txspruned
+ let tx_pruned = TransactionPruned {
+ prefix: tx.prefix.clone(),
+ rct_signatures: tx.rct_signatures,
+ };
+ self.put::(&tx_id, &tx_pruned)?;
+
+ // Inserting tx prunable part in table::txs
+ self.put::(&tx_id, &tx_prunable_blob)?;
+
+ // Checking to see if the database is pruned and inserting into table::txsprunabletip accordingly
+ if self.get_blockchain_pruning_seed()? > 0 {
+ self.put::(&tx_id, &height)?;
+ }
+
+ // V2 Tx store hash of their prunable part
+ if let Some(tx_prunable_hash) = tx_prunable_hash {
+ self.put::(&tx_id, &tx_prunable_hash.into())?;
+ }
+ Ok(tx_id)
+ }
+
+ fn remove_transaction(&'service self, tx_hash: Hash) -> Result<(), DB_FAILURES> {
+ let txpruned = self.get_pruned_tx(tx_hash)?;
+
+ for input in txpruned.prefix.inputs.iter() {
+ if let TxIn::ToKey {
+ amount: _,
+ key_offsets: _,
+ k_image,
+ } = input
+ {
+ self.remove_spent_key(k_image.clone())?;
+ }
+ }
+
+ self.remove_transaction_data(txpruned.prefix, tx_hash)
+ }
+
+ fn remove_transaction_data(
+ &'service self,
+ txprefix: monero::TransactionPrefix,
+ tx_hash: Hash,
+ ) -> Result<(), DB_FAILURES> {
+ // Checking if the transaction exist and fetching its index
+ let txindex =
+ self.get::(&tx_hash.into())?
+ .ok_or(DB_FAILURES::NotFound(
+ "Attempting to remove transaction that isn't in the db",
+ ))?;
+
+ self.delete::(&txindex.tx_id, &None)?;
+ self.delete::(&txindex.tx_id, &None)?;
+ // If Its in Tip blocks range we must delete it
+ if self.get::(&txindex.tx_id)?.is_some() {
+ self.delete::(&txindex.tx_id, &None)?;
+ }
+ // If v2 Tx we must delete the prunable hash
+ if txprefix.version.0 > 1 {
+ self.delete::(&txindex.tx_id, &None)?;
+ }
+
+ self.remove_tx_outputs(txprefix, txindex.tx_id)?;
+
+ self.delete::(&txindex.tx_id, &None)?;
+ self.delete::(&tx_hash.into(), &None)
+ }
+
+ fn remove_tx_outputs(
+ &'service self,
+ txprefix: monero::TransactionPrefix,
+ tx_id: u64,
+ ) -> Result<(), DB_FAILURES> {
+ let amount_output_indices: TxOutputIdx = self
+ .get::(&tx_id)?
+ .ok_or(DB_FAILURES::NotFound("Failed to find tx's outputs indices"))?;
+
+ if amount_output_indices.0.is_empty() {
+ return Err(DB_FAILURES::Other(
+ "Attempting to remove outputs of a an empty tx",
+ ));
+ }
+
+ // Checking if the input is a coinbase input
+ #[allow(clippy::match_like_matches_macro)]
+ let is_coinbase_input: bool = match &txprefix.inputs[0] {
+ TxIn::Gen { height: _ } if txprefix.version.0 > 1 && txprefix.inputs.len() == 1 => true,
+ _ => false,
+ };
+ for o in 0..txprefix.outputs.len() {
+ let amount = match is_coinbase_input {
+ true => 0,
+ false => txprefix.outputs[o].amount.0,
+ };
+ self.remove_output(Some(amount), amount_output_indices.0[o])?;
+ }
+ Ok(())
+ }
+
+ /// `get_num_tx` fetches the total number of transactions stored in the database
+ ///
+ /// Should return the count. In case of failure, a DB_FAILURES will be return.
+ ///
+ /// No parameters is required.
+ fn get_num_tx(&'service self) -> Result {
+ let ro_tx = self.db.tx().map_err(Into::into)?;
+ ro_tx.num_entries::().map(|n| n as u64)
+ }
+
+ /// `tx_exists` check if a transaction exist with the given hash.
+ ///
+ /// Return `true` if the transaction exist, `false` otherwise. In case of failure, a DB_FAILURES will be return.
+ ///
+ /// Parameters :
+ /// `hash` is the given hash of transaction to check.
+ fn tx_exists(&'service self, hash: Hash) -> Result {
+ let ro_tx = self.db.tx().map_err(Into::into)?;
+ Ok(ro_tx.get::(&hash.into())?.is_some())
+ }
+
+ /// `get_tx_unlock_time` fetch a transaction's unlock time/height
+ ///
+ /// Should return the unlock time/height in u64. In case of failure, a DB_FAILURES will be return.
+ ///
+ /// Parameters:
+ /// `hash`: is the given hash of the transaction to check.
+ fn get_tx_unlock_time(&'service self, hash: Hash) -> Result {
+ let ro_tx = self.db.tx().map_err(Into::into)?;
+
+ // Getting the tx index
+ let txindex =
+ ro_tx
+ .get::(&hash.into())?
+ .ok_or(DB_FAILURES::NotFound(
+ "wasn't able to find a transaction in the database",
+ ))?;
+
+ Ok(txindex.unlock_time)
+ }
+
+ /// `get_tx` fetches the transaction with the given hash.
+ ///
+ /// Should return the transaction. In case of failure, a DB_FAILURES will be return.
+ ///
+ /// Parameters:
+ /// `hash`: is the given hash of transaction to fetch.
+ fn get_tx(&'service self, hash: Hash) -> Result {
+ // Getting the pruned tx
+ let pruned_tx = self.get_pruned_tx(hash)?;
+
+ // Getting the tx index
+ let ro_tx = self.db.tx().map_err(Into::into)?;
+ let txindex =
+ ro_tx
+ .get::(&hash.into())?
+ .ok_or(DB_FAILURES::NotFound(
+ "failed to find index of a transaction",
+ ))?;
+
+ // Getting its prunable part
+ let prunable_part =
+ ro_tx
+ .get::(&txindex.tx_id)?
+ .ok_or(DB_FAILURES::NotFound(
+ "failed to find prunable part of a transaction",
+ ))?;
+
+ // Making it a Transaction
+ pruned_tx
+ .into_transaction(&prunable_part)
+ .map_err(|err| DB_FAILURES::SerializeIssue(err.into()))
+ }
+
+ /// `get_tx_list` fetches the transactions with given hashes.
+ ///
+ /// Should return a vector with the requested transactions. In case of failures, a DB_FAILURES will be return.
+ /// Precisly, a HASH_DNE error will be returned with the correspondig hash of transaction that is not found in the DB.
+ ///
+ /// `hlist`: is the given collection of hashes correspondig to the transactions to fetch.
+ fn get_tx_list(
+ &'service self,
+ hash_list: Vec,
+ ) -> Result, DB_FAILURES> {
+ let mut result: Vec = Vec::with_capacity(hash_list.len());
+
+ for hash in hash_list {
+ result.push(self.get_tx(hash)?);
+ }
+ Ok(result)
+ }
+
+ /// `get_pruned_tx` fetches the transaction base with the given hash.
+ ///
+ /// Should return the transaction. In case of failure, a DB_FAILURES will be return.
+ ///
+ /// Parameters:
+ /// `hash`: is the given hash of transaction to fetch.
+ fn get_pruned_tx(&'service self, hash: Hash) -> Result {
+ let ro_tx = self.db.tx().map_err(Into::into)?;
+
+ let txindex =
+ ro_tx
+ .get::(&hash.into())?
+ .ok_or(DB_FAILURES::NotFound(
+ "wasn't able to find a transaction in the database",
+ ))?;
+
+ ro_tx
+ .get::(&txindex.tx_id)?
+ .ok_or(DB_FAILURES::NotFound(
+ "failed to find prefix of a transaction",
+ ))
+ }
+
+ /// `get_tx_block_height` fetches the height of a transaction's block
+ ///
+ /// Should return the height of the block containing the transaction with the given hash. In case
+ /// of failures, a DB FAILURES will be return. Precisely, a TX_DNE error will be return if the transaction cannot be found.
+ ///
+ /// Parameters:
+ /// `hash`: is the fiven hash of the first transaction
+ fn get_tx_block_height(&'service self, hash: Hash) -> Result {
+ let ro_tx = self.db.tx().map_err(Into::into)?;
+ let txindex = ro_tx
+ .get::(&hash.into())?
+ .ok_or(DB_FAILURES::NotFound("txindex not found"))?;
+ Ok(txindex.height)
+ }
+
+ // --------------------------------| Outputs |--------------------------------
+
+ /// `add_output` add an output data to it's storage .
+ ///
+ /// It internally keep track of the global output count. The global output count is also used to index outputs based on
+ /// their order of creations.
+ ///
+ /// Should return the amount output index. In case of failures, a DB_FAILURES will be return.
+ ///
+ /// Parameters:
+ /// `tx_hash`: is the hash of the transaction where the output comes from.
+ /// `output`: is the output's publickey to store.
+ /// `index`: is the local output's index (from transaction).
+ /// `unlock_time`: is the unlock time (height) of the output.
+ /// `commitment`: is the RingCT commitment of this output.
+ fn add_output(
+ &'service self,
+ tx_hash: Hash,
+ output: TxOut,
+ local_index: u64,
+ unlock_time: u64,
+ commitment: Option,
+ ) -> Result {
+ let height = self.height()?;
+
+ let mut cursor_outputmetadata = self.write_cursor_dup::()?;
+
+ let pubkey = output.target.as_one_time_key().map(Into::into);
+ let mut out_metadata = OutputMetadata {
+ tx_hash: tx_hash.into(),
+ local_index,
+ pubkey,
+ unlock_time,
+ height,
+ commitment: None,
+ };
+
+ // RingCT Outputs
+ if let Some(commitment) = commitment {
+ out_metadata.commitment = Some(commitment.into());
+
+ let amount_index = self.get_rct_num_outputs()? + 1;
+ cursor_outputmetadata.put_cursor_dup(&(), &amount_index, &out_metadata)?;
+ Ok(amount_index)
+ }
+ // Pre-RingCT Outputs
+ else {
+ let amount_index = self.get_pre_rct_num_outputs(output.amount.0)? + 1;
+ let mut cursor = self.write_cursor_dup::()?;
+ cursor.put_cursor_dup(&output.amount.0, &amount_index, &out_metadata)?;
+ Ok(amount_index)
+ }
+ }
+
+ fn remove_output(&'service self, amount: Option, index: u64) -> Result<(), DB_FAILURES> {
+ let mut cursor_outputmetadata = self.write_cursor_dup::()?;
+
+ if let Some(amount) = amount {
+ if amount == 0 {
+ cursor_outputmetadata
+ .get_dup(&(), &index)?
+ .ok_or(DB_FAILURES::NotFound(
+ "Failed to find PostRCT output metadata",
+ ))?;
+ cursor_outputmetadata.del()
+ } else {
+ let mut cursor = self.write_cursor_dup::()?;
+ let _ = cursor
+ .get_dup(&amount, &index)?
+ .ok_or(DB_FAILURES::NotFound(
+ "Failed to find PreRCT output metadata",
+ ))?;
+ cursor.del()
+ }
+ } else {
+ cursor_outputmetadata
+ .get_dup(&(), &index)?
+ .ok_or(DB_FAILURES::NotFound(
+ "Failed to find PostRCT output metadata",
+ ))?;
+ cursor_outputmetadata.del()
+ }
+ }
+
+ /// `get_output` get an output's data
+ ///
+ /// Return the public key, unlock time, and block height for the output with the given amount and index, collected in a struct
+ /// In case of failures, a `DB_FAILURES` will be return. Precisely, if the output cannot be found, an `OUTPUT_DNE` error will be return.
+ /// If any of the required part for the final struct isn't found, a `DB_ERROR` will be return
+ ///
+ /// Parameters:
+ /// `amount`: is the corresponding amount of the output
+ /// `index`: is the output's index (indexed by amount)
+ /// `include_commitment` : `true` by default.
+ fn get_output(
+ &'service self,
+ amount: Option,
+ index: u64,
+ ) -> Result {
+ let ro_tx = self.db.tx().map_err(Into::into)?;
+ let mut cursor_outputmetadata = ro_tx.cursor_dup::()?;
+
+ if let Some(amount) = amount {
+ if amount > 0 {
+ let mut cursor = ro_tx.cursor_dup::()?;
+ return cursor
+ .get_dup(&amount, &index)?
+ .ok_or(DB_FAILURES::NotFound(
+ "Failed to find PreRCT output metadata",
+ ));
+ }
+ }
+ cursor_outputmetadata
+ .get_dup(&(), &index)?
+ .ok_or(DB_FAILURES::NotFound(
+ "Failed to find PostRCT output metadata",
+ ))
+ }
+
+ /// `get_output_list` gets a collection of output's data from a corresponding index collection.
+ ///
+ /// Return a collection of output's data. In case of failurse, a `DB_FAILURES` will be return.
+ ///
+ /// Parameters:
+ /// `amounts`: is the collection of amounts corresponding to the requested outputs.
+ /// `offsets`: is a collection of outputs' index (indexed by amount).
+ /// `allow partial`: `false` by default.
+ fn get_output_list(
+ &'service self,
+ amounts: Option>,
+ offsets: Vec,
+ ) -> Result, DB_FAILURES> {
+ let ro_tx = self.db.tx().map_err(Into::into)?;
+ let mut cursor_outputmetadata = ro_tx.cursor_dup::()?;
+ let mut result: Vec = Vec::new();
+
+ // Pre-RingCT output to be found.
+ if let Some(amounts) = amounts {
+ let mut cursor = ro_tx.cursor_dup::()?;
+
+ for ofs in amounts.into_iter().zip(offsets) {
+ if ofs.0 == 0 {
+ let output = cursor_outputmetadata.get_dup(&(), &ofs.1)?.ok_or(
+ DB_FAILURES::NotFound("An output hasn't been found in the database"),
+ )?;
+ result.push(output);
+ } else {
+ let output = cursor
+ .get_dup(&ofs.0, &ofs.1)?
+ .ok_or(DB_FAILURES::NotFound(
+ "An output hasn't been found in the database",
+ ))?;
+ result.push(output);
+ }
+ }
+ // No Pre-RingCT outputs to be found.
+ } else {
+ for ofs in offsets {
+ let output =
+ cursor_outputmetadata
+ .get_dup(&(), &ofs)?
+ .ok_or(DB_FAILURES::NotFound(
+ "An output hasn't been found in the database",
+ ))?;
+ result.push(output);
+ }
+ }
+
+ Ok(result)
+ }
+
+ /// `get_rct_num_outputs` fetches the number post-RingCT output.
+ ///
+ /// Return the number of post-RingCT outputs. In case of failures a `DB_FAILURES` will be return.
+ ///
+ /// No parameters is required
+ fn get_rct_num_outputs(&'service self) -> Result {
+ let ro_tx = self.db.tx().map_err(Into::into)?;
+
+ ro_tx
+ .num_entries::()
+ .map(|n| n as u64)
+ }
+
+ /// `get_pre_rct_num_outputs` fetches the number of preRCT outputs of a given amount.
+ ///
+ /// Return a count of outputs of the given amount. in case of failures a `DB_FAILURES` will be return.
+ ///
+ /// Parameters:
+ /// `amount`: is the output amount being looked up.
+ fn get_pre_rct_num_outputs(&'service self, amount: u64) -> Result {
+ let ro_tx = self.db.tx().map_err(Into::into)?;
+ let mut cursor = ro_tx.cursor_dup::()?;
+
+ transaction::Cursor::set(&mut cursor, &amount)?;
+ let out_metadata: Option<(u64, OutputMetadata)> =
+ transaction::DupCursor::last_dup(&mut cursor)?;
+ if let Some(out_metadata) = out_metadata {
+ return Ok(out_metadata.0);
+ }
+ Err(DB_FAILURES::Other("failed to decode the subkey and value"))
+ }
+
+ // ------------------------------| Spent Keys |------------------------------
+
+ /// `add_spent_key` add the supplied key image to the spent key image record
+ fn add_spent_key(&'service self, key_image: KeyImage) -> Result<(), DB_FAILURES> {
+ let mut cursor_spentkeys = self.write_cursor_dup::()?;
+ cursor_spentkeys.put_cursor_dup(&(), &key_image.into(), &())
+ }
+
+ /// `remove_spent_key` remove the specified key image from the spent key image record
+ fn remove_spent_key(&'service self, key_image: KeyImage) -> Result<(), DB_FAILURES> {
+ let mut cursor_spentkeys = self.write_cursor_dup::()?;
+ cursor_spentkeys.get_dup(&(), &key_image.into())?;
+ cursor_spentkeys.del()
+ }
+
+ /// `is_spent_key_recorded` check if the specified key image has been spent
+ fn is_spent_key_recorded(&'service self, key_image: KeyImage) -> Result {
+ let mut cursor_spentkeys = self.write_cursor_dup::()?;
+ Ok(cursor_spentkeys.get_dup(&(), &key_image.into())?.is_some())
+ }
+
+ // --------------------------------------------| Alt-Block |------------------------------------------------------------
+
+ /// `add_alt_block` add a new alternative block.
+ ///
+ /// In case of failures, a DB_FAILURES will be return.
+ ///
+ /// Parameters:
+ /// blkid: is the hash of the original block
+ /// data: is the metadata for the block
+ /// blob: is the blobdata of this alternative block.
+ fn add_alt_block(
+ &'service self,
+ altblock_hash: Hash,
+ data: AltBlock,
+ ) -> Result<(), DB_FAILURES> {
+ self.put::(&altblock_hash.into(), &data)
+ }
+
+ /// `get_alt_block` gets the specified alternative block.
+ ///
+ /// Return a tuple containing the blobdata of the alternative block and its metadata. In case of failures, a DB_FAILURES will be return.
+ ///
+ /// Parameters:
+ /// `blkid`: is the hash of the requested alternative block.
+ fn get_alt_block(&'service self, altblock_hash: Hash) -> Result {
+ let ro_tx = self.db.tx().map_err(Into::into)?;
+ ro_tx
+ .get::(&altblock_hash.into())?
+ .ok_or(DB_FAILURES::NotFound(
+ "Failed to find an AltBLock in the db",
+ ))
+ }
+
+ /// `remove_alt_block` remove the specified alternative block
+ ///
+ /// In case of failures, a DB_FAILURES will be return.
+ ///
+ /// Parameters:
+ /// `blkid`: is the hash of the alternative block to remove.
+ fn remove_alt_block(&mut self, altblock_hash: Hash) -> Result<(), DB_FAILURES> {
+ self.delete::(&altblock_hash.into(), &None)
+ }
+
+ /// `get_alt_block` gets the total number of alternative blocks stored
+ ///
+ /// In case of failures, a DB_FAILURES will be return.
+ ///
+ /// No parameters is required.
+ fn get_alt_block_count(&'service self) -> Result {
+ let ro_tx = self.db.tx().map_err(Into::into)?;
+ ro_tx.num_entries::().map(|n| n as u64)
+ }
+
+ /// `drop_alt_block` drop all alternative blocks.
+ ///
+ /// In case of failures, a DB_FAILURES will be return.
+ ///
+ /// No parameters is required.
+ fn drop_alt_blocks(&mut self) -> Result<(), DB_FAILURES> {
+ self.clear::()
+ }
+
+ // --------------------------------| Properties |--------------------------------
+
+ // No pruning yet
+ fn get_blockchain_pruning_seed(&'service self) -> Result {
+ let ro_tx = self.db.tx().map_err(Into::into)?;
+
+ ro_tx
+ .get::(&0)?
+ .ok_or(DB_FAILURES::NotFound("Can't find prunning seed"))
+ }
+}
diff --git a/database/src/lib.rs b/database/src/lib.rs
new file mode 100644
index 00000000..1d880f5e
--- /dev/null
+++ b/database/src/lib.rs
@@ -0,0 +1,221 @@
+// Copyright (C) 2023 Cuprate Contributors
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+//! The cuprate-db crate implement (as its name suggests) the relations between the blockchain/txpool objects and their databases.
+//! `lib.rs` contains all the generics, trait and specification for interfaces between blockchain and a backend-agnostic database
+//! Every other files in this folder are implementation of these traits/methods to real storage engine.
+//!
+//! At the moment, the only storage engine available is MDBX.
+//! The next storage engine planned is HSE (Heteregeonous Storage Engine) from Micron.
+//!
+//! For more informations, please consult this docs:
+
+#![deny(unused_attributes)]
+#![forbid(unsafe_code)]
+#![allow(non_camel_case_types)]
+#![deny(clippy::expect_used, clippy::panic)]
+#![allow(dead_code, unused_macros)] // temporary
+
+use monero::{util::ringct::RctSig, Block, BlockHeader, Hash};
+use std::ops::Range;
+use thiserror::Error;
+
+#[cfg(feature = "mdbx")]
+pub mod mdbx;
+//#[cfg(feature = "hse")]
+//pub mod hse;
+
+pub mod encoding;
+pub mod error;
+pub mod interface;
+pub mod table;
+pub mod types;
+
+const DEFAULT_BLOCKCHAIN_DATABASE_DIRECTORY: &str = "blockchain";
+const DEFAULT_TXPOOL_DATABASE_DIRECTORY: &str = "txpool_mem";
+const BINCODE_CONFIG: bincode::config::Configuration<
+ bincode::config::LittleEndian,
+ bincode::config::Fixint,
+> = bincode::config::standard().with_fixed_int_encoding();
+
+// ------------------------------------------| Database |------------------------------------------
+
+pub mod database {
+ //! This module contains the Database abstraction trait. Any key/value storage engine implemented need
+ //! to fullfil these associated types and functions, in order to be usable. This module also contains the
+ //! Interface struct which is used by the DB Reactor to interact with the database.
+
+ use crate::{
+ error::DB_FAILURES,
+ transaction::{Transaction, WriteTransaction},
+ };
+ use std::{ops::Deref, path::PathBuf, sync::Arc};
+
+ /// `Database` Trait implement all the methods necessary to generate transactions as well as execute specific functions. It also implement generic associated types to identify the
+ /// different transaction modes (read & write) and it's native errors.
+ pub trait Database<'a> {
+ type TX: Transaction<'a>;
+ type TXMut: WriteTransaction<'a>;
+ type Error: Into;
+
+ // Create a transaction from the database
+ fn tx(&'a self) -> Result;
+
+ // Create a mutable transaction from the database
+ fn tx_mut(&'a self) -> Result;
+
+ // Open a database from the specified path
+ fn open(path: PathBuf) -> Result
+ where
+ Self: std::marker::Sized;
+
+ // Check if the database is built.
+ fn check_all_tables_exist(&'a self) -> Result<(), Self::Error>;
+
+ // Build the database
+ fn build(&'a self) -> Result<(), Self::Error>;
+ }
+
+ /// `Interface` is a struct containing a shared pointer to the database and transaction's to be used for the implemented method of Interface.
+ pub struct Interface<'a, D: Database<'a>> {
+ pub db: Arc,
+ pub tx: Option<>::TXMut>,
+ }
+
+ // Convenient implementations for database
+ impl<'service, D: Database<'service>> Interface<'service, D> {
+ fn from(db: Arc) -> Result {
+ Ok(Self { db, tx: None })
+ }
+
+ fn open(&'service mut self) -> Result<(), DB_FAILURES> {
+ let tx = self.db.tx_mut().map_err(Into::into)?;
+ self.tx = Some(tx);
+ Ok(())
+ }
+ }
+
+ impl<'service, D: Database<'service>> Deref for Interface<'service, D> {
+ type Target = >::TXMut;
+
+ fn deref(&self) -> &Self::Target {
+ return self.tx.as_ref().unwrap();
+ }
+ }
+}
+
+// ------------------------------------------| DatabaseTx |------------------------------------------
+
+pub mod transaction {
+ //! This module contains the abstractions of Transactional Key/Value database functions.
+ //! Any key/value database/storage engine can be implemented easily for Cuprate as long as
+ //! these functions or equivalent logic exist for it.
+
+ use crate::{
+ error::DB_FAILURES,
+ table::{DupTable, Table},
+ };
+
+ // Abstraction of a read-only cursor, for simple tables
+ #[allow(clippy::type_complexity)]
+ pub trait Cursor<'t, T: Table> {
+ fn first(&mut self) -> Result