diff --git a/old_database/Cargo.toml b/old_database/Cargo.toml deleted file mode 100644 index 37c49ae..0000000 --- a/old_database/Cargo.toml +++ /dev/null @@ -1,33 +0,0 @@ -[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/old_database/LICENSE b/old_database/LICENSE deleted file mode 100644 index e19903e..0000000 --- a/old_database/LICENSE +++ /dev/null @@ -1,14 +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 . \ No newline at end of file diff --git a/old_database/src/encoding.rs b/old_database/src/encoding.rs deleted file mode 100644 index aa4681d..0000000 --- a/old_database/src/encoding.rs +++ /dev/null @@ -1,78 +0,0 @@ -//! ### 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/old_database/src/error.rs b/old_database/src/error.rs deleted file mode 100644 index 25b1f8d..0000000 --- a/old_database/src/error.rs +++ /dev/null @@ -1,53 +0,0 @@ -//! ### 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/old_database/src/hse.rs b/old_database/src/hse.rs deleted file mode 100644 index 2d07b3f..0000000 --- a/old_database/src/hse.rs +++ /dev/null @@ -1,11 +0,0 @@ -/* 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/old_database/src/interface.rs b/old_database/src/interface.rs deleted file mode 100644 index cde8c0a..0000000 --- a/old_database/src/interface.rs +++ /dev/null @@ -1,1036 +0,0 @@ -//! ### 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 relevant 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. - /// Precisely, a HASH_DNE error will be returned with the corresponding hash of transaction that is not found in the DB. - /// - /// `hlist`: is the given collection of hashes corresponding 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/old_database/src/lib.rs b/old_database/src/lib.rs deleted file mode 100644 index d410b67..0000000 --- a/old_database/src/lib.rs +++ /dev/null @@ -1,221 +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 . - -//! 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 information, 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 fulfil 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, DB_FAILURES>; - - fn get_cursor(&mut self) -> Result, DB_FAILURES>; - - fn last(&mut self) -> Result, DB_FAILURES>; - - fn next(&mut self) -> Result, DB_FAILURES>; - - fn prev(&mut self) -> Result, DB_FAILURES>; - - fn set(&mut self, key: &T::Key) -> Result, DB_FAILURES>; - } - - // Abstraction of a read-only cursor with support for duplicated tables. DupCursor inherit Cursor methods as - // a duplicated table can be treated as a simple table. - #[allow(clippy::type_complexity)] - pub trait DupCursor<'t, T: DupTable>: Cursor<'t, T> { - fn first_dup(&mut self) -> Result, DB_FAILURES>; - - fn get_dup( - &mut self, - key: &T::Key, - subkey: &T::SubKey, - ) -> Result, DB_FAILURES>; - - fn last_dup(&mut self) -> Result, DB_FAILURES>; - - fn next_dup(&mut self) -> Result, DB_FAILURES>; - - fn prev_dup(&mut self) -> Result, DB_FAILURES>; - } - - // Abstraction of a read-write cursor, for simple tables. WriteCursor inherit Cursor methods. - pub trait WriteCursor<'t, T: Table>: Cursor<'t, T> { - fn put_cursor(&mut self, key: &T::Key, value: &T::Value) -> Result<(), DB_FAILURES>; - - fn del(&mut self) -> Result<(), DB_FAILURES>; - } - - // Abstraction of a read-write cursor with support for duplicated tables. DupWriteCursor inherit DupCursor and WriteCursor methods. - pub trait DupWriteCursor<'t, T: DupTable>: WriteCursor<'t, T> { - fn put_cursor_dup( - &mut self, - key: &T::Key, - subkey: &T::SubKey, - value: &T::Value, - ) -> Result<(), DB_FAILURES>; - - /// Delete all data under associated to its key - fn del_nodup(&mut self) -> Result<(), DB_FAILURES>; - } - - // Abstraction of a read-only transaction. - pub trait Transaction<'a>: Send + Sync { - type Cursor: Cursor<'a, T>; - type DupCursor: DupCursor<'a, T> + Cursor<'a, T>; - - fn get(&self, key: &T::Key) -> Result, DB_FAILURES>; - - fn commit(self) -> Result<(), DB_FAILURES>; - - fn cursor(&self) -> Result, DB_FAILURES>; - - fn cursor_dup(&self) -> Result, DB_FAILURES>; - - fn num_entries(&self) -> Result; - } - - // Abstraction of a read-write transaction. WriteTransaction inherits Transaction methods. - pub trait WriteTransaction<'a>: Transaction<'a> { - type WriteCursor: WriteCursor<'a, T>; - type DupWriteCursor: DupWriteCursor<'a, T> + DupCursor<'a, T>; - - fn put(&self, key: &T::Key, value: &T::Value) -> Result<(), DB_FAILURES>; - - fn delete( - &self, - key: &T::Key, - value: &Option, - ) -> Result<(), DB_FAILURES>; - - fn clear(&self) -> Result<(), DB_FAILURES>; - - fn write_cursor(&self) -> Result, DB_FAILURES>; - - fn write_cursor_dup(&self) -> Result, DB_FAILURES>; - } -} diff --git a/old_database/src/mdbx.rs b/old_database/src/mdbx.rs deleted file mode 100644 index e44a58b..0000000 --- a/old_database/src/mdbx.rs +++ /dev/null @@ -1,474 +0,0 @@ -//! ### MDBX implementation -//! This module contains the implementation of all the database traits for the MDBX storage engine. -//! This include basic transactions methods, cursors and errors conversion. - -use crate::{ - database::Database, - error::{DB_FAILURES, DB_SERIAL}, - table::{self, DupTable, Table}, - transaction::{Transaction, WriteTransaction}, - BINCODE_CONFIG, -}; -use libmdbx::{ - Cursor, DatabaseFlags, DatabaseKind, Geometry, Mode, PageSize, SyncMode, TableFlags, - TransactionKind, WriteFlags, RO, RW, -}; -use std::ops::Range; - -// Constant used in mdbx implementation -const MDBX_DEFAULT_SYNC_MODE: SyncMode = SyncMode::Durable; -const MDBX_MAX_MAP_SIZE: usize = 4 * 1024usize.pow(3); // 4TB -const MDBX_GROWTH_STEP: isize = 100 * 1024isize.pow(2); // 100MB -const MDBX_PAGE_SIZE: Option = None; -const MDBX_GEOMETRY: Geometry> = Geometry { - size: Some(0..MDBX_MAX_MAP_SIZE), - growth_step: Some(MDBX_GROWTH_STEP), - shrink_threshold: None, - page_size: MDBX_PAGE_SIZE, -}; - -/// [`mdbx_decode`] is a function which the supplied bytes will be deserialized using `bincode::decode_from_slice(src, BINCODE_CONFIG)` -/// function. Return `Err(DB_FAILURES::SerializeIssue(DB_SERIAL::BincodeDecode(err)))` if it failed to decode the value. It is used for clarity purpose. -fn mdbx_decode(src: &[u8]) -> Result<(T, usize), DB_FAILURES> { - bincode::decode_from_slice(src, BINCODE_CONFIG) - .map_err(|e| DB_FAILURES::SerializeIssue(DB_SERIAL::BincodeDecode(e))) -} - -/// [`mdbx_encode`] is a function that serialize a given value into a vector using `bincode::encode_to_vec(src, BINCODE_CONFIG)` -/// function. Return `Err(DB_FAILURES::SerializeIssue(DB_SERIAL::BincodeEncode(err)))` if it failed to encode the value. It is used for clarity purpose. -fn mdbx_encode(src: &T) -> Result, DB_FAILURES> { - bincode::encode_to_vec(src, BINCODE_CONFIG) - .map_err(|e| DB_FAILURES::SerializeIssue(DB_SERIAL::BincodeEncode(e))) -} - -/// [`mdbx_open_table`] is a simple function used for syntax clarity. It try to open the table, and return a `DB_FAILURES` if it failed. -fn mdbx_open_table<'db, K: TransactionKind, E: DatabaseKind, T: Table>( - tx: &'db libmdbx::Transaction<'db, K, E>, -) -> Result { - tx.open_table(Some(T::TABLE_NAME)) - .map_err(std::convert::Into::::into) -} - -/// [`cursor_pair_decode`] is a function defining a conditional return used in (almost) every cursor functions. If a pair of key/value effectively exist from the cursor, -/// the two values are decoded using `mdbx_decode` function. Return `Err(DB_FAILURES::SerializeIssue(DB_SERIAL::BincodeEncode(err)))` if it failed to encode the value. -/// It is used for clarity purpose. -fn cursor_pair_decode( - pair: Option<(Vec, Vec)>, -) -> Result, DB_FAILURES> { - if let Some(pair) = pair { - let decoded_key = mdbx_decode(pair.0.as_slice())?; - let decoded_value = mdbx_decode(pair.1.as_slice())?; - Ok(Some((decoded_key.0, decoded_value.0))) - } else { - Ok(None) - } -} - -// Implementation of the database trait with mdbx types -impl<'a, E> Database<'a> for libmdbx::Database -where - E: DatabaseKind, -{ - type TX = libmdbx::Transaction<'a, RO, E>; - type TXMut = libmdbx::Transaction<'a, RW, E>; - type Error = libmdbx::Error; - - // Open a Read-Only transaction - fn tx(&'a self) -> Result { - self.begin_ro_txn() - } - - // Open a Read-Write transaction - fn tx_mut(&'a self) -> Result { - self.begin_rw_txn() - } - - // Open the database with the given path - fn open(path: std::path::PathBuf) -> Result { - let db: libmdbx::Database = libmdbx::Database::new() - .set_flags(DatabaseFlags::from(Mode::ReadWrite { - sync_mode: MDBX_DEFAULT_SYNC_MODE, - })) - .set_geometry(MDBX_GEOMETRY) - .set_max_readers(32) - .set_max_tables(15) - .open(path.as_path())?; - - Ok(db) - } - - // Open each tables to verify if the database is complete. - fn check_all_tables_exist(&'a self) -> Result<(), Self::Error> { - let ro_tx = self.begin_ro_txn()?; - // ----- BLOCKS ----- - ro_tx.open_table(Some(table::blockhash::TABLE_NAME))?; - ro_tx.open_table(Some(table::blockmetadata::TABLE_NAME))?; - ro_tx.open_table(Some(table::blocks::TABLE_NAME))?; - ro_tx.open_table(Some(table::altblock::TABLE_NAME))?; - // ------ TXNs ------ - ro_tx.open_table(Some(table::txspruned::TABLE_NAME))?; - ro_tx.open_table(Some(table::txsprunablehash::TABLE_NAME))?; - ro_tx.open_table(Some(table::txsprunabletip::TABLE_NAME))?; - ro_tx.open_table(Some(table::txsprunable::TABLE_NAME))?; - ro_tx.open_table(Some(table::txsoutputs::TABLE_NAME))?; - ro_tx.open_table(Some(table::txsidentifier::TABLE_NAME))?; - // ---- OUTPUTS ----- - ro_tx.open_table(Some(table::prerctoutputmetadata::TABLE_NAME))?; - ro_tx.open_table(Some(table::outputmetadata::TABLE_NAME))?; - // ---- SPT KEYS ---- - ro_tx.open_table(Some(table::spentkeys::TABLE_NAME))?; - // --- PROPERTIES --- - ro_tx.open_table(Some(table::properties::TABLE_NAME))?; - - Ok(()) - } - - // Construct the table of the database - fn build(&'a self) -> Result<(), Self::Error> { - let rw_tx = self.begin_rw_txn()?; - - // Constructing the tables - // ----- BLOCKS ----- - rw_tx.create_table( - Some(table::blockhash::TABLE_NAME), - TableFlags::INTEGER_KEY | TableFlags::DUP_FIXED | TableFlags::DUP_SORT, - )?; - rw_tx.create_table( - Some(table::blockmetadata::TABLE_NAME), - TableFlags::INTEGER_KEY | TableFlags::DUP_FIXED | TableFlags::DUP_SORT, - )?; - rw_tx.create_table(Some(table::blocks::TABLE_NAME), TableFlags::INTEGER_KEY)?; - rw_tx.create_table(Some(table::altblock::TABLE_NAME), TableFlags::INTEGER_KEY)?; - // ------ TXNs ------ - rw_tx.create_table(Some(table::txspruned::TABLE_NAME), TableFlags::INTEGER_KEY)?; - rw_tx.create_table( - Some(table::txsprunable::TABLE_NAME), - TableFlags::INTEGER_KEY, - )?; - rw_tx.create_table( - Some(table::txsprunablehash::TABLE_NAME), - TableFlags::INTEGER_KEY | TableFlags::DUP_FIXED | TableFlags::DUP_SORT, - )?; - rw_tx.create_table( - Some(table::txsprunabletip::TABLE_NAME), - TableFlags::INTEGER_KEY, - )?; - rw_tx.create_table( - Some(table::txsoutputs::TABLE_NAME), - TableFlags::INTEGER_KEY | TableFlags::DUP_FIXED | TableFlags::DUP_SORT, - )?; - rw_tx.create_table( - Some(table::txsidentifier::TABLE_NAME), - TableFlags::INTEGER_KEY | TableFlags::DUP_FIXED | TableFlags::DUP_SORT, - )?; - // ---- OUTPUTS ----- - rw_tx.create_table( - Some(table::prerctoutputmetadata::TABLE_NAME), - TableFlags::INTEGER_KEY | TableFlags::DUP_FIXED | TableFlags::DUP_SORT, - )?; - rw_tx.create_table( - Some(table::outputmetadata::TABLE_NAME), - TableFlags::INTEGER_KEY | TableFlags::DUP_FIXED | TableFlags::DUP_SORT, - )?; - // ---- SPT KEYS ---- - rw_tx.create_table( - Some(table::spentkeys::TABLE_NAME), - TableFlags::INTEGER_KEY | TableFlags::DUP_FIXED | TableFlags::DUP_SORT, - )?; - // --- PROPERTIES --- - rw_tx.create_table(Some(table::properties::TABLE_NAME), TableFlags::INTEGER_KEY)?; - - rw_tx.commit()?; - Ok(()) - } -} - -// Implementation of the Cursor trait for mdbx's Cursors -impl<'a, T, R> crate::transaction::Cursor<'a, T> for Cursor<'a, R> -where - T: Table, - R: TransactionKind, -{ - fn first(&mut self) -> Result, DB_FAILURES> { - let pair = self - .first::, Vec>() - .map_err(std::convert::Into::::into)?; - - cursor_pair_decode(pair) - } - - fn get_cursor( - &mut self, - ) -> Result::Key, ::Value)>, DB_FAILURES> { - let pair = self - .get_current::, Vec>() - .map_err(std::convert::Into::::into)?; - - cursor_pair_decode(pair) - } - - fn last(&mut self) -> Result::Key, ::Value)>, DB_FAILURES> { - let pair = self - .last::, Vec>() - .map_err(std::convert::Into::::into)?; - - cursor_pair_decode(pair) - } - - fn next(&mut self) -> Result::Key, ::Value)>, DB_FAILURES> { - let pair = self - .next::, Vec>() - .map_err(std::convert::Into::::into)?; - - cursor_pair_decode(pair) - } - - fn prev(&mut self) -> Result::Key, ::Value)>, DB_FAILURES> { - let pair = self - .prev::, Vec>() - .map_err(std::convert::Into::::into)?; - - cursor_pair_decode(pair) - } - - fn set(&mut self, key: &T::Key) -> Result::Value>, DB_FAILURES> { - let encoded_key = mdbx_encode(key)?; - - let value = self - .set::>(&encoded_key) - .map_err(std::convert::Into::::into)?; - - if let Some(value) = value { - return Ok(Some(mdbx_decode(value.as_slice())?.0)); - } - Ok(None) - } -} - -// Implementation of the DupCursor trait for mdbx's Cursors -impl<'t, T, R> crate::transaction::DupCursor<'t, T> for Cursor<'t, R> -where - R: TransactionKind, - T: DupTable, -{ - fn first_dup(&mut self) -> Result, DB_FAILURES> { - let value = self - .first_dup::>() - .map_err(std::convert::Into::::into)?; - - if let Some(value) = value { - return Ok(Some(mdbx_decode(value.as_slice())?.0)); - } - Ok(None) - } - - fn get_dup( - &mut self, - key: &T::Key, - subkey: &T::SubKey, - ) -> Result::Value>, DB_FAILURES> { - let (encoded_key, encoded_subkey) = (mdbx_encode(key)?, mdbx_encode(subkey)?); - - let value = self - .get_both::>(&encoded_key, &encoded_subkey) - .map_err(std::convert::Into::::into)?; - - if let Some(value) = value { - return Ok(Some(mdbx_decode(value.as_slice())?.0)); - } - Ok(None) - } - - fn last_dup(&mut self) -> Result, DB_FAILURES> { - let value = self - .last_dup::>() - .map_err(std::convert::Into::::into)?; - - if let Some(value) = value { - return Ok(Some(mdbx_decode(value.as_slice())?.0)); - } - Ok(None) - } - - fn next_dup(&mut self) -> Result, DB_FAILURES> { - let pair = self - .next_dup::, Vec>() - .map_err(std::convert::Into::::into)?; - - if let Some(pair) = pair { - let (decoded_key, decoded_value) = ( - mdbx_decode(pair.0.as_slice())?, - mdbx_decode(pair.1.as_slice())?, - ); - return Ok(Some((decoded_key.0, decoded_value.0))); - } - Ok(None) - } - - fn prev_dup(&mut self) -> Result, DB_FAILURES> { - let pair = self - .prev_dup::, Vec>() - .map_err(std::convert::Into::::into)?; - - if let Some(pair) = pair { - let (decoded_key, decoded_value) = ( - mdbx_decode(pair.0.as_slice())?, - mdbx_decode(pair.1.as_slice())?, - ); - return Ok(Some((decoded_key.0, decoded_value.0))); - } - Ok(None) - } -} - -// Implementation of the WriteCursor trait for mdbx's Cursors in RW permission -impl<'a, T> crate::transaction::WriteCursor<'a, T> for Cursor<'a, RW> -where - T: Table, -{ - fn put_cursor(&mut self, key: &T::Key, value: &T::Value) -> Result<(), DB_FAILURES> { - let (encoded_key, encoded_value) = (mdbx_encode(key)?, mdbx_encode(value)?); - - self.put(&encoded_key, &encoded_value, WriteFlags::empty()) - .map_err(Into::into) - } - - fn del(&mut self) -> Result<(), DB_FAILURES> { - self.del(WriteFlags::empty()).map_err(Into::into) - } -} - -// Implementation of the DupWriteCursor trait for mdbx's Cursors in RW permission -impl<'a, T> crate::transaction::DupWriteCursor<'a, T> for Cursor<'a, RW> -where - T: DupTable, -{ - fn put_cursor_dup( - &mut self, - key: &::Key, - subkey: &::SubKey, - value: &::Value, - ) -> Result<(), DB_FAILURES> { - let (encoded_key, mut encoded_subkey, mut encoded_value) = - (mdbx_encode(key)?, mdbx_encode(subkey)?, mdbx_encode(value)?); - encoded_subkey.append(&mut encoded_value); - - self.put( - encoded_key.as_slice(), - encoded_subkey.as_slice(), - WriteFlags::empty(), - ) - .map_err(Into::into) - } - - fn del_nodup(&mut self) -> Result<(), DB_FAILURES> { - self.del(WriteFlags::NO_DUP_DATA).map_err(Into::into) - } -} - -// Implementation of the Transaction trait for mdbx's Transactions -impl<'a, E, R: TransactionKind> Transaction<'a> for libmdbx::Transaction<'_, R, E> -where - E: DatabaseKind, -{ - type Cursor = Cursor<'a, R>; - type DupCursor = Cursor<'a, R>; - - fn get(&self, key: &T::Key) -> Result, DB_FAILURES> { - let table = mdbx_open_table::<_, _, T>(self)?; - - let encoded_key = mdbx_encode(key)?; - - let value = self - .get::>(&table, &encoded_key) - .map_err(std::convert::Into::::into)?; - if let Some(value) = value { - return Ok(Some(mdbx_decode(value.as_slice())?.0)); - } - Ok(None) - } - - fn cursor(&self) -> Result, DB_FAILURES> { - let table = mdbx_open_table::<_, _, T>(self)?; - - self.cursor(&table).map_err(Into::into) - } - - fn commit(self) -> Result<(), DB_FAILURES> { - let b = self - .commit() - .map_err(std::convert::Into::::into)?; - - if b { - Ok(()) - } else { - Err(DB_FAILURES::FailedToCommit) - } - } - - fn cursor_dup(&self) -> Result, DB_FAILURES> { - let table = mdbx_open_table::<_, _, T>(self)?; - - self.cursor(&table).map_err(Into::into) - } - - fn num_entries(&self) -> Result { - let table = mdbx_open_table::<_, _, T>(self)?; - let stat = self.table_stat(&table)?; - - Ok(stat.entries()) - } -} - -// Implementation of the Transaction trait for mdbx's Transactions with RW permissions -impl<'a, E> WriteTransaction<'a> for libmdbx::Transaction<'a, RW, E> -where - E: DatabaseKind, -{ - type WriteCursor = Cursor<'a, RW>; - type DupWriteCursor = Cursor<'a, RW>; - - fn put(&self, key: &T::Key, value: &T::Value) -> Result<(), DB_FAILURES> { - let table = mdbx_open_table::<_, _, T>(self)?; - - let (encoded_key, encoded_value) = (mdbx_encode(key)?, mdbx_encode(value)?); - - self.put(&table, encoded_key, encoded_value, WriteFlags::empty()) - .map_err(Into::into) - } - - fn delete(&self, key: &T::Key, value: &Option) -> Result<(), DB_FAILURES> { - let table = mdbx_open_table::<_, _, T>(self)?; - - let encoded_key = mdbx_encode(key)?; - if let Some(value) = value { - let encoded_value = mdbx_encode(value)?; - - return self - .del(&table, encoded_key, Some(encoded_value.as_slice())) - .map(|_| ()) - .map_err(Into::into); - } - self.del(&table, encoded_key, None) - .map(|_| ()) - .map_err(Into::into) - } - - fn clear(&self) -> Result<(), DB_FAILURES> { - let table = mdbx_open_table::<_, _, T>(self)?; - - self.clear_table(&table).map_err(Into::into) - } - - fn write_cursor(&self) -> Result, DB_FAILURES> { - let table = mdbx_open_table::<_, _, T>(self)?; - - self.cursor(&table).map_err(Into::into) - } - - fn write_cursor_dup(&self) -> Result, DB_FAILURES> { - let table = mdbx_open_table::<_, _, T>(self)?; - - self.cursor(&table).map_err(Into::into) - } -} diff --git a/old_database/src/table.rs b/old_database/src/table.rs deleted file mode 100644 index 0b2f38a..0000000 --- a/old_database/src/table.rs +++ /dev/null @@ -1,181 +0,0 @@ -//! ### Table module -//! This module contains the definition of the [`Table`] and [`DupTable`] trait, and the actual tables used in the database. -//! [`DupTable`] are just a trait used to define that they support DUPSORT|DUPFIXED operation (as of now we don't know the equivalent for HSE). -//! All tables are defined with docs explaining its purpose, what types are the key and data. -//! For more details please look at Cuprate's book : - -use crate::{ - encoding::Compat, - types::{ - /*OutTx,*/ AltBlock, BlockMetadata, /*RctOutkey,*/ OutputMetadata, - TransactionPruned, TxIndex, /*OutAmountIdx,*/ /*KeyImage,*/ TxOutputIdx, - }, -}; -use bincode::{de::Decode, enc::Encode}; -use monero::{blockdata::transaction::KeyImage, Block, Hash}; - -/// A trait implementing a table interaction for the database. It is implemented to an empty struct to specify the name and table's associated types. These associated -/// types are used to simplify deserialization process. -pub trait Table: Send + Sync + 'static + Clone { - // name of the table - const TABLE_NAME: &'static str; - - // Definition of a key & value types of the database - type Key: Encode + Decode; - type Value: Encode + Decode; -} - -/// A trait implementing a table with duplicated data support. -pub trait DupTable: Table { - // Subkey of the table (prefix of the data) - type SubKey: Encode + Decode; -} - -/// This declarative macro declare a new empty struct and impl the specified name, and corresponding types. -macro_rules! impl_table { - ( $(#[$docs:meta])* $table:ident , $key:ty , $value:ty ) => { - #[derive(Clone)] - $(#[$docs])* - pub(crate) struct $table; - - impl Table for $table { - const TABLE_NAME: &'static str = stringify!($table); - type Key = $key; - type Value = $value; - } - }; -} - -/// This declarative macro declare extend the original impl_table! macro by implementy DupTable trait. -macro_rules! impl_duptable { - ($(#[$docs:meta])* $table:ident, $key:ty, $subkey:ty, $value:ty) => { - impl_table!($(#[$docs])* $table, $key, $value); - - impl DupTable for $table { - type SubKey = $subkey; - } - }; -} - -// ------------------------------------------| Tables definition |------------------------------------------ - -// ----- BLOCKS ----- - -impl_duptable!( - /// `blockhash` is table defining a relation between the hash of a block and its height. Its primary use is to quickly find block's hash by its height. - blockhash, - (), - Compat, - u64 -); - -impl_duptable!( - /// `blockmetadata` store block metadata alongside their corresponding Hash. The blocks metadata can contains the total_coins_generated, weight, long_term_block_weight & cumulative RingCT - blockmetadata, - (), - u64, - BlockMetadata -); - -impl_table!( - /// `blockbody` store blocks' bodies along their Hash. The blocks body contains the coinbase transaction and its corresponding mined transactions' hashes. - blocks, - u64, - Compat -); - -/* -impl_table!( - /// `blockhfversion` keep track of block's hard fork version. If an outdated node continue to run after a hard fork, it needs to know, after updating, what blocks needs to be update. - blockhfversion, u64, u8); -*/ - -impl_table!( - /// `altblock` is a table that permits the storage of blocks from an alternative chain, which may cause a re-org. These blocks can be fetch by their corresponding hash. - altblock, - Compat, - AltBlock -); - -// ------- TXNs ------- - -impl_table!( - /// `txspruned` is table storing TransactionPruned (or Pruned Tx). These can be fetch by the corresponding Transaction ID. - txspruned, - u64, - TransactionPruned -); - -impl_table!( - /// `txsprunable` is a table storing the Prunable part of transactions (Signatures and RctSig), stored as raw bytes. These can be fetch by the corresponding Transaction ID. - txsprunable, - u64, - Vec -); - -impl_duptable!( - /// `txsprunablehash` is a table storing hashes of prunable part of transactions. These hash can be fetch by the corresponding Transaction ID. - txsprunablehash, - u64, - (), - Compat -); - -impl_table!( - /// `txsprunabletip` is a table used for optimization purpose. It defines at which block's height this transaction belong as long as the block is with Tip blocks. These can be fetch by the corresponding Transaction ID. - txsprunabletip, - u64, - u64 -); - -impl_duptable!( - /// `txsoutputs` is a table storing output indices used in a transaction. These can be fetch by the corresponding Transaction ID. - txsoutputs, - u64, - (), - TxOutputIdx -); - -impl_duptable!( - /// `txsidentifier` is a table defining a relation between the hash of a transaction and its transaction Indexes. Its primarily used to quickly find tx's ID by its hash. - txsidentifier, - Compat, - (), - TxIndex -); - -// ---- OUTPUTS ---- - -impl_duptable!( - /// `prerctoutputmetadata` is a duplicated table storing Pre-RingCT output's metadata. The key is the amount of this output, and the subkey is its amount idx. - prerctoutputmetadata, - u64, - u64, - OutputMetadata -); -impl_duptable!( - /// `prerctoutputmetadata` is a table storing RingCT output's metadata. The key is the amount idx of this output since amount is always 0 for RingCT outputs. - outputmetadata, - (), - u64, - OutputMetadata -); - -// ---- SPT KEYS ---- - -impl_duptable!( - /// `spentkeys`is a table storing every KeyImage that have been used to create decoys input. As these KeyImage can't be re used they need to marked. - spentkeys, - (), - Compat, - () -); - -// ---- PROPERTIES ---- - -impl_table!( - /// `spentkeys`is a table storing every KeyImage that have been used to create decoys input. As these KeyImage can't be re used they need to marked. - properties, - u32, - u32 -); diff --git a/old_database/src/types.rs b/old_database/src/types.rs deleted file mode 100644 index f4f806a..0000000 --- a/old_database/src/types.rs +++ /dev/null @@ -1,516 +0,0 @@ -//! ### Types module -//! This module contains definition and implementations of some of the structures stored in the database. -//! Some of these types are just Wrapper for convenience or re-definition of `monero-rs` database type (see Boog900/monero-rs, "db" branch) -//! Since the database do not use dummy keys, these redefined structs are the same as monerod without the prefix data used as a key. -//! All these types implement [`bincode::Encode`] and [`bincode::Decode`]. They can store `monero-rs` types in their field. In this case, these field -//! use the [`Compat`] wrapper. - -use crate::encoding::{Compat, ReaderCompat}; -use bincode::{enc::write::Writer, Decode, Encode}; -use monero::{ - consensus::{encode, Decodable}, - util::ringct::{Key, RctSig, RctSigBase, RctSigPrunable, RctType, Signature}, - Block, Hash, PublicKey, Transaction, TransactionPrefix, TxIn, -}; - -// ---- BLOCKS ---- - -#[derive(Clone, Debug, Encode, Decode)] -/// [`BlockMetadata`] is a struct containing metadata of a block such as the block's `timestamp`, the `total_coins_generated` at this height, its `weight`, its difficulty (`diff_lo`) -/// and cumulative difficulty (`diff_hi`), the `block_hash`, the cumulative RingCT (`cum_rct`) and its long term weight (`long_term_block_weight`). The monerod's struct equivalent is `mdb_block_info_4` -/// This struct is used in [`crate::table::blockmetadata`] table. -pub struct BlockMetadata { - /// Block's timestamp (the time at which it started to be mined) - pub timestamp: u64, - /// Total monero supply, this block included - pub total_coins_generated: u64, - /// Block's weight (sum of all transactions weights) - pub weight: u64, - /// Block's cumulative_difficulty. In monerod this field would have been split into two `u64`, since cpp don't support *natively* `uint128_t`/`u128` - pub cumulative_difficulty: u128, - /// Block's hash - pub block_hash: Compat, - /// Cumulative number of RingCT outputs up to this block - pub cum_rct: u64, - /// Block's long term weight - pub long_term_block_weight: u64, -} - -#[derive(Clone, Debug, Encode, Decode)] -/// [`AltBlock`] is a struct containing an alternative `block` (defining an alternative mainchain) and its metadata (`block_height`, `cumulative_weight`, -/// `cumulative_difficulty_low`, `cumulative_difficulty_high`, `already_generated_coins`). -/// This struct is used in [`crate::table::altblock`] table. -pub struct AltBlock { - /// Alternative block's height. - pub height: u64, - /// Cumulative weight median at this block - pub cumulative_weight: u64, - /// Cumulative difficulty - pub cumulative_difficulty: u128, - /// Total generated coins excluding this block's coinbase reward + fees - pub already_generated_coins: u64, - /// Actual block data, with Prefix and Transactions. - /// It is worth noting that monerod implementation do not contain the block in its struct, but still append it at the end of metadata. - pub block: Compat, -} - -// ---- TRANSACTIONS ---- - -#[derive(Clone, Debug)] -/// [`TransactionPruned`] is, as its name suggest, the pruned part of a transaction, which is the Transaction Prefix and its RingCT ring. -/// This struct is used in the [`crate::table::txsprefix`] table. -pub struct TransactionPruned { - /// The transaction prefix. - pub prefix: TransactionPrefix, - /// The RingCT ring, will only contain the 'sig' field. - pub rct_signatures: RctSig, -} - -impl bincode::Decode for TransactionPruned { - fn decode( - decoder: &mut D, - ) -> Result { - let mut r = ReaderCompat(decoder.reader()); - - // We first decode the TransactionPrefix and get the n° of inputs/outputs - let prefix: TransactionPrefix = Decodable::consensus_decode(&mut r) - .map_err(|_| bincode::error::DecodeError::Other("Monero-rs decoding failed"))?; - - let (inputs, outputs) = (prefix.inputs.len(), prefix.outputs.len()); - - // Handle the prefix accordingly to its version - match *prefix.version { - // First transaction format, Pre-RingCT, so the ring are None - 1 => Ok(TransactionPruned { - prefix, - rct_signatures: RctSig { sig: None, p: None }, - }), - _ => { - let mut rct_signatures = RctSig { sig: None, p: None }; - // No inputs so no RingCT - if inputs == 0 { - return Ok(TransactionPruned { - prefix, - rct_signatures, - }); - } - // Otherwise get the RingCT ring for the tx inputs - if let Some(sig) = RctSigBase::consensus_decode(&mut r, inputs, outputs) - .map_err(|_| bincode::error::DecodeError::Other("Monero-rs decoding failed"))? - { - rct_signatures = RctSig { - sig: Some(sig), - p: None, - }; - } - // And we return it - Ok(TransactionPruned { - prefix, - rct_signatures, - }) - } - } - } -} - -impl bincode::Encode for TransactionPruned { - fn encode( - &self, - encoder: &mut E, - ) -> Result<(), bincode::error::EncodeError> { - let writer = encoder.writer(); - // Encoding the Transaction prefix first - let buf = monero::consensus::serialize(&self.prefix); - writer.write(&buf)?; - match *self.prefix.version { - 1 => {} // First transaction format, Pre-RingCT, so the there is no Rct ring to add - _ => { - if let Some(sig) = &self.rct_signatures.sig { - // If there is ring then we append it at the end - let buf = monero::consensus::serialize(sig); - writer.write(&buf)?; - } - } - } - Ok(()) - } -} - -impl TransactionPruned { - /// Turns a pruned transaction to a normal transaction with the missing pruned data - pub fn into_transaction(self, prunable: &[u8]) -> Result { - let mut r = std::io::Cursor::new(prunable); - match *self.prefix.version { - // Pre-RingCT transactions - 1 => { - let signatures: Result>, encode::Error> = self - .prefix - .inputs - .iter() - .filter_map(|input| match input { - TxIn::ToKey { key_offsets, .. } => { - let sigs: Result, encode::Error> = key_offsets - .iter() - .map(|_| Decodable::consensus_decode(&mut r)) - .collect(); - Some(sigs) - } - _ => None, - }) - .collect(); - Ok(Transaction { - prefix: self.prefix, - signatures: signatures?, - rct_signatures: RctSig { sig: None, p: None }, - }) - } - // Post-RingCT Transactions - _ => { - let signatures = Vec::new(); - let mut rct_signatures = RctSig { sig: None, p: None }; - if self.prefix.inputs.is_empty() { - return Ok(Transaction { - prefix: self.prefix, - signatures, - rct_signatures: RctSig { sig: None, p: None }, - }); - } - if let Some(sig) = self.rct_signatures.sig { - let p = { - if sig.rct_type != RctType::Null { - let mixin_size = if !self.prefix.inputs.is_empty() { - match &self.prefix.inputs[0] { - TxIn::ToKey { key_offsets, .. } => key_offsets.len() - 1, - _ => 0, - } - } else { - 0 - }; - RctSigPrunable::consensus_decode( - &mut r, - sig.rct_type, - self.prefix.inputs.len(), - self.prefix.outputs.len(), - mixin_size, - )? - } else { - None - } - }; - rct_signatures = RctSig { sig: Some(sig), p }; - } - Ok(Transaction { - prefix: self.prefix, - signatures, - rct_signatures, - }) - } - } - } -} - -pub fn get_transaction_prunable_blob( - tx: &monero::Transaction, - w: &mut W, -) -> Result { - let mut len = 0; - match tx.prefix.version.0 { - 1 => { - for sig in tx.signatures.iter() { - for c in sig { - len += monero::consensus::encode::Encodable::consensus_encode(c, w)?; - } - } - } - _ => { - if let Some(sig) = &tx.rct_signatures.sig { - if let Some(p) = &tx.rct_signatures.p { - len += p.consensus_encode(w, sig.rct_type)?; - } - } - } - } - Ok(len) -} - -pub fn calculate_prunable_hash(tx: &monero::Transaction, tx_prunable_blob: &[u8]) -> Option { - // V1 transaction don't have prunable hash - if tx.prefix.version.0 == 1 { - return None; - } - - // Checking if it's a miner tx - if let TxIn::Gen { height: _ } = &tx.prefix.inputs[0] { - if tx.prefix.inputs.len() == 1 { - // Returning miner tx's empty hash - return Some(Hash::from_slice(&[ - 0x70, 0xa4, 0x85, 0x5d, 0x04, 0xd8, 0xfa, 0x7b, 0x3b, 0x27, 0x82, 0xca, 0x53, 0xb6, - 0x00, 0xe5, 0xc0, 0x03, 0xc7, 0xdc, 0xb2, 0x7d, 0x7e, 0x92, 0x3c, 0x23, 0xf7, 0x86, - 0x01, 0x46, 0xd2, 0xc5, - ])); - } - }; - - // Calculating the hash - Some(Hash::new(tx_prunable_blob)) -} - -#[derive(Clone, Debug, Encode, Decode)] -/// [`TxIndex`] is a struct used in the [`crate::table::txsidentifier`]. It store the `unlock_time` of a transaction, the `height` of the block -/// whose transaction belong to and the Transaction ID (`tx_id`) -pub struct TxIndex { - /// Transaction ID - pub tx_id: u64, - /// The unlock time of this transaction (the height at which it is unlocked, it is not a timestamp) - pub unlock_time: u64, - /// The height of the block whose transaction belong to - pub height: u64, // TODO USELESS already in txs_prunable_tip -} - -#[derive(Clone, Debug, Encode, Decode)] -/// [`TxOutputIdx`] is a single-tuple struct used to contain the indexes (amount and amount indices) of the transactions outputs. It is defined for more clarity on its role. -/// This struct is used in [`crate::table::txsoutputs`] table. -pub struct TxOutputIdx(pub Vec); - -// ---- OUTPUTS ---- - -#[derive(Clone, Debug, Encode, Decode)] -/// [`RctOutkey`] is a struct containing RingCT metadata and an output ID. It is equivalent to the `output_data_t` struct in monerod -/// This struct is used in [`crate::table::outputamounts`] -pub struct RctOutkey { - // /// amount_index - //pub amount_index: u64, - /// The output's ID - pub output_id: u64, - /// The output's public key (for spend verification) - pub pubkey: Compat, - /// The output's unlock time (the height at which it is unlocked, it is not a timestamp) - pub unlock_time: u64, - /// The height of the block which used this output - pub height: u64, - /// The output's amount commitment (for spend verification) - /// For compatibility with Pre-RingCT outputs, this field is an option. In fact, monerod distinguish between `pre_rct_output_data_t` and `output_data_t` field like that : - /// ```cpp - /// // This MUST be identical to output_data_t, without the extra rct data at the end - /// struct pre_rct_output_data_t - /// ``` - pub commitment: Option>, -} - -#[derive(Clone, Debug, Encode, Decode)] -/// [`OutputMetadata`] is a struct containing Outputs Metadata. It is used in [`crate::table::outputmetadata`]. It is a struct merging the -/// `out_tx_index` tuple with `output_data_t` structure in monerod, without the output ID. -pub struct OutputMetadata { - pub tx_hash: Compat, - - pub local_index: u64, - - pub pubkey: Option>, - - pub unlock_time: u64, - - pub height: u64, - - pub commitment: Option>, -} - -//#[derive(Clone, Debug, Encode, Decode)] -//// [`OutAmountIdx`] is a struct tuple used to contain the two keys used in [`crate::table::outputamounts`] table. -//// In monerod, the database key is the amount while the *cursor key* (the amount index) is the prefix of the actual data being returned. -//// As we prefer to note use cursor with partial data, we prefer to concat these two into a unique key -//pub struct OutAmountIdx(u64,u64); -// MAYBE NOT FINALLY - -//#[derive(Clone, Debug, Encode, Decode)] -// /// [`OutTx`] is a struct containing the hash of the transaction whose output belongs to, and the local index of this output. -// /// This struct is used in [`crate::table::outputinherit`]. -/*pub struct OutTx { - /// Output's transaction hash - pub tx_hash: Compat, - /// Local index of the output - pub local_index: u64, -}*/ - -#[cfg(test)] -mod tests { - use monero::Hash; - - use super::get_transaction_prunable_blob; - - #[test] - fn calculate_tx_prunable_hash() { - let prunable_blob: Vec = vec![ - 1, 113, 10, 7, 87, 70, 119, 97, 244, 126, 155, 133, 254, 167, 60, 204, 134, 45, 71, 17, - 87, 21, 252, 8, 218, 233, 219, 192, 84, 181, 196, 74, 213, 2, 246, 222, 66, 45, 152, - 159, 156, 19, 224, 251, 110, 154, 188, 91, 129, 53, 251, 82, 134, 46, 93, 119, 136, 35, - 13, 190, 235, 231, 44, 183, 134, 221, 12, 131, 222, 209, 246, 52, 14, 33, 94, 173, 251, - 233, 18, 154, 91, 72, 229, 180, 43, 35, 152, 130, 38, 82, 56, 179, 36, 168, 54, 41, 62, - 49, 208, 35, 245, 29, 27, 81, 72, 140, 104, 4, 59, 22, 120, 252, 67, 197, 130, 245, 93, - 100, 129, 134, 19, 137, 228, 237, 166, 89, 5, 42, 1, 110, 139, 39, 81, 89, 159, 40, - 239, 211, 251, 108, 82, 68, 125, 182, 75, 152, 129, 74, 73, 208, 215, 15, 63, 3, 106, - 168, 35, 56, 126, 66, 2, 189, 53, 201, 77, 187, 102, 127, 154, 60, 209, 33, 217, 109, - 81, 217, 183, 252, 114, 90, 245, 21, 229, 174, 254, 177, 147, 130, 74, 49, 118, 203, - 14, 7, 118, 221, 81, 181, 78, 97, 224, 76, 160, 134, 73, 206, 204, 199, 201, 30, 201, - 77, 4, 78, 237, 167, 76, 92, 104, 247, 247, 203, 141, 243, 72, 52, 83, 61, 35, 147, - 231, 124, 21, 115, 81, 83, 67, 222, 61, 225, 171, 66, 243, 185, 195, 51, 72, 243, 80, - 104, 4, 166, 54, 199, 235, 193, 175, 4, 242, 42, 146, 170, 90, 212, 101, 208, 113, 58, - 65, 121, 55, 179, 206, 92, 50, 94, 171, 33, 67, 108, 220, 19, 193, 155, 30, 58, 46, 9, - 227, 48, 246, 187, 82, 230, 61, 64, 95, 197, 183, 150, 62, 203, 252, 36, 157, 135, 160, - 120, 189, 52, 94, 186, 93, 5, 36, 120, 160, 62, 254, 178, 101, 11, 228, 63, 128, 249, - 182, 56, 100, 9, 5, 2, 81, 243, 229, 245, 43, 234, 35, 216, 212, 46, 165, 251, 183, - 133, 10, 76, 172, 95, 106, 231, 13, 216, 222, 15, 92, 122, 103, 68, 238, 190, 108, 124, - 138, 62, 255, 243, 22, 209, 2, 138, 45, 178, 101, 240, 18, 186, 71, 239, 137, 191, 134, - 128, 221, 181, 173, 242, 111, 117, 45, 255, 138, 101, 79, 242, 42, 4, 144, 245, 193, - 79, 14, 44, 201, 223, 0, 193, 123, 75, 155, 140, 248, 0, 226, 246, 230, 126, 7, 32, - 107, 173, 193, 206, 184, 11, 33, 148, 104, 32, 79, 149, 71, 68, 150, 6, 47, 90, 231, - 151, 14, 121, 196, 169, 249, 117, 154, 167, 139, 103, 62, 97, 250, 131, 160, 92, 239, - 18, 236, 110, 184, 102, 30, 194, 175, 243, 145, 169, 183, 163, 141, 244, 186, 172, 251, - 3, 78, 165, 33, 12, 2, 136, 180, 178, 83, 117, 0, 184, 170, 255, 69, 131, 123, 8, 212, - 158, 162, 119, 137, 146, 63, 95, 133, 186, 91, 255, 152, 187, 107, 113, 147, 51, 219, - 207, 5, 160, 169, 97, 9, 1, 202, 152, 186, 128, 160, 110, 120, 7, 176, 103, 87, 30, - 137, 240, 67, 55, 79, 147, 223, 45, 177, 210, 101, 225, 22, 25, 129, 111, 101, 21, 213, - 20, 254, 36, 57, 67, 70, 93, 192, 11, 180, 75, 99, 185, 77, 75, 74, 63, 182, 183, 208, - 16, 69, 237, 96, 76, 96, 212, 242, 6, 169, 14, 250, 168, 129, 18, 141, 240, 101, 196, - 96, 120, 88, 90, 51, 77, 12, 133, 212, 192, 107, 131, 238, 34, 237, 93, 157, 108, 13, - 255, 187, 163, 106, 148, 108, 105, 244, 243, 174, 189, 180, 48, 102, 57, 170, 118, 211, - 110, 126, 222, 165, 93, 36, 157, 90, 14, 135, 184, 197, 185, 7, 99, 199, 224, 225, 243, - 212, 116, 149, 137, 186, 16, 196, 73, 23, 11, 248, 248, 67, 167, 149, 154, 64, 76, 218, - 119, 135, 239, 34, 48, 66, 57, 109, 246, 3, 141, 169, 42, 157, 222, 21, 40, 183, 168, - 97, 195, 106, 244, 229, 61, 122, 136, 59, 255, 120, 86, 30, 63, 226, 18, 65, 218, 188, - 195, 217, 85, 12, 211, 221, 188, 27, 8, 98, 103, 211, 213, 217, 65, 82, 229, 145, 80, - 147, 220, 57, 143, 20, 189, 253, 106, 13, 21, 170, 60, 24, 48, 162, 234, 0, 240, 226, - 4, 28, 76, 93, 56, 3, 187, 223, 58, 31, 184, 58, 234, 198, 140, 223, 217, 1, 147, 94, - 218, 199, 154, 121, 137, 44, 229, 0, 1, 10, 133, 250, 140, 64, 150, 89, 64, 112, 178, - 221, 87, 19, 24, 104, 252, 28, 65, 207, 28, 195, 217, 73, 12, 16, 83, 55, 199, 84, 117, - 175, 123, 13, 234, 10, 54, 63, 245, 161, 74, 235, 92, 189, 247, 47, 62, 176, 41, 159, - 40, 250, 116, 63, 33, 193, 78, 72, 29, 215, 9, 191, 233, 243, 87, 14, 195, 7, 89, 101, - 0, 28, 0, 234, 205, 59, 142, 119, 119, 52, 143, 80, 151, 211, 184, 235, 98, 222, 206, - 170, 166, 4, 155, 3, 235, 26, 62, 8, 171, 19, 14, 53, 245, 77, 114, 175, 246, 170, 139, - 227, 212, 141, 72, 223, 134, 63, 91, 26, 12, 78, 253, 198, 162, 152, 202, 207, 170, - 254, 8, 4, 4, 175, 207, 84, 10, 108, 179, 157, 132, 110, 76, 201, 247, 227, 158, 106, - 59, 41, 206, 229, 128, 2, 60, 203, 65, 71, 160, 232, 186, 227, 51, 12, 142, 85, 93, 89, - 234, 236, 157, 230, 247, 167, 99, 7, 37, 146, 13, 53, 39, 255, 209, 177, 179, 17, 131, - 59, 16, 75, 180, 21, 119, 88, 4, 12, 49, 140, 3, 110, 235, 231, 92, 13, 41, 137, 21, - 37, 46, 138, 44, 250, 44, 161, 179, 114, 94, 63, 207, 192, 81, 234, 35, 125, 54, 2, - 214, 10, 57, 116, 154, 150, 147, 223, 232, 36, 108, 152, 145, 157, 132, 190, 103, 233, - 155, 141, 243, 249, 120, 72, 168, 14, 196, 35, 54, 107, 167, 218, 209, 1, 209, 197, - 187, 242, 76, 86, 229, 114, 131, 196, 69, 171, 118, 28, 51, 192, 146, 14, 140, 84, 66, - 155, 237, 194, 167, 121, 160, 166, 198, 166, 57, 13, 66, 162, 234, 148, 102, 133, 111, - 18, 166, 77, 156, 75, 84, 220, 80, 35, 81, 141, 23, 197, 162, 23, 167, 187, 187, 187, - 137, 184, 96, 140, 162, 6, 49, 63, 39, 84, 107, 85, 202, 168, 51, 194, 214, 132, 253, - 253, 189, 231, 1, 226, 118, 104, 84, 147, 244, 58, 233, 250, 66, 26, 109, 223, 34, 2, - 2, 112, 141, 147, 230, 134, 73, 45, 105, 180, 223, 52, 95, 40, 235, 209, 50, 67, 193, - 22, 176, 176, 128, 140, 238, 252, 129, 220, 175, 79, 133, 12, 123, 209, 64, 5, 160, 39, - 47, 66, 122, 245, 65, 102, 133, 58, 74, 138, 153, 217, 48, 59, 84, 135, 117, 92, 131, - 44, 109, 40, 105, 69, 29, 14, 142, 71, 87, 112, 68, 134, 0, 14, 158, 14, 68, 15, 180, - 150, 108, 49, 196, 94, 82, 27, 208, 163, 103, 81, 85, 124, 61, 242, 151, 29, 74, 87, - 134, 166, 145, 186, 110, 207, 162, 99, 92, 133, 121, 137, 124, 90, 134, 5, 249, 231, - 181, 222, 38, 170, 141, 113, 204, 172, 169, 173, 63, 81, 170, 76, - ]; - let prunable_hash = Hash::from_slice(&[ - 0x5c, 0x5e, 0x69, 0xd8, 0xfc, 0x0d, 0x22, 0x6a, 0x60, 0x91, 0x47, 0xda, 0x98, 0x36, - 0x06, 0x00, 0xf4, 0xea, 0x49, 0xcc, 0x49, 0x45, 0x2c, 0x5e, 0xf8, 0xba, 0x20, 0xf5, - 0x93, 0xd4, 0x80, 0x7d, - ]); - assert_eq!(prunable_hash, Hash::new(prunable_blob)); - } - - #[test] - fn get_prunable_tx_blob() { - let mut pruned_p_blob: Vec = vec![ - 2, 0, 1, 2, 0, 16, 180, 149, 135, 30, 237, 231, 156, 1, 132, 145, 47, 182, 251, 153, 1, - 225, 234, 94, 219, 134, 23, 222, 210, 30, 208, 213, 12, 136, 158, 5, 159, 148, 15, 206, - 144, 2, 132, 63, 135, 22, 151, 8, 134, 8, 178, 26, 194, 111, 101, 192, 45, 104, 18, - 115, 178, 194, 100, 255, 227, 10, 253, 165, 53, 62, 81, 67, 202, 169, 56, 99, 42, 146, - 175, 137, 85, 195, 27, 151, 2, 0, 3, 207, 28, 183, 85, 7, 58, 81, 205, 53, 9, 191, 141, - 209, 70, 58, 30, 38, 225, 212, 68, 14, 4, 216, 204, 101, 163, 66, 156, 101, 143, 255, - 196, 134, 0, 3, 254, 66, 159, 187, 180, 41, 78, 252, 85, 255, 154, 55, 239, 222, 199, - 37, 159, 210, 71, 186, 188, 46, 134, 181, 236, 221, 173, 43, 93, 50, 138, 249, 221, 44, - 1, 34, 67, 111, 182, 199, 28, 219, 56, 238, 143, 188, 101, 103, 205, 139, 160, 144, - 226, 34, 92, 235, 221, 75, 38, 7, 104, 255, 108, 208, 1, 184, 169, 2, 9, 1, 84, 62, 77, - 107, 119, 22, 148, 222, 6, 128, 128, 211, 14, 242, 200, 16, 137, 239, 249, 55, 59, 16, - 193, 192, 140, 240, 153, 129, 228, 115, 222, 247, 41, 128, 219, 241, 249, 198, 214, 75, - 31, 82, 225, 1, 158, 183, 226, 220, 126, 228, 191, 211, 79, 43, 220, 95, 124, 109, 14, - 162, 170, 68, 37, 62, 21, 139, 182, 246, 152, 36, 156, 172, 197, 20, 145, 85, 9, 8, - 106, 237, 112, 63, 189, 172, 145, 49, 234, 68, 152, 200, 241, 0, 37, - ]; - let prunable_blob: Vec = vec![ - 1, 113, 10, 7, 87, 70, 119, 97, 244, 126, 155, 133, 254, 167, 60, 204, 134, 45, 71, 17, - 87, 21, 252, 8, 218, 233, 219, 192, 84, 181, 196, 74, 213, 2, 246, 222, 66, 45, 152, - 159, 156, 19, 224, 251, 110, 154, 188, 91, 129, 53, 251, 82, 134, 46, 93, 119, 136, 35, - 13, 190, 235, 231, 44, 183, 134, 221, 12, 131, 222, 209, 246, 52, 14, 33, 94, 173, 251, - 233, 18, 154, 91, 72, 229, 180, 43, 35, 152, 130, 38, 82, 56, 179, 36, 168, 54, 41, 62, - 49, 208, 35, 245, 29, 27, 81, 72, 140, 104, 4, 59, 22, 120, 252, 67, 197, 130, 245, 93, - 100, 129, 134, 19, 137, 228, 237, 166, 89, 5, 42, 1, 110, 139, 39, 81, 89, 159, 40, - 239, 211, 251, 108, 82, 68, 125, 182, 75, 152, 129, 74, 73, 208, 215, 15, 63, 3, 106, - 168, 35, 56, 126, 66, 2, 189, 53, 201, 77, 187, 102, 127, 154, 60, 209, 33, 217, 109, - 81, 217, 183, 252, 114, 90, 245, 21, 229, 174, 254, 177, 147, 130, 74, 49, 118, 203, - 14, 7, 118, 221, 81, 181, 78, 97, 224, 76, 160, 134, 73, 206, 204, 199, 201, 30, 201, - 77, 4, 78, 237, 167, 76, 92, 104, 247, 247, 203, 141, 243, 72, 52, 83, 61, 35, 147, - 231, 124, 21, 115, 81, 83, 67, 222, 61, 225, 171, 66, 243, 185, 195, 51, 72, 243, 80, - 104, 4, 166, 54, 199, 235, 193, 175, 4, 242, 42, 146, 170, 90, 212, 101, 208, 113, 58, - 65, 121, 55, 179, 206, 92, 50, 94, 171, 33, 67, 108, 220, 19, 193, 155, 30, 58, 46, 9, - 227, 48, 246, 187, 82, 230, 61, 64, 95, 197, 183, 150, 62, 203, 252, 36, 157, 135, 160, - 120, 189, 52, 94, 186, 93, 5, 36, 120, 160, 62, 254, 178, 101, 11, 228, 63, 128, 249, - 182, 56, 100, 9, 5, 2, 81, 243, 229, 245, 43, 234, 35, 216, 212, 46, 165, 251, 183, - 133, 10, 76, 172, 95, 106, 231, 13, 216, 222, 15, 92, 122, 103, 68, 238, 190, 108, 124, - 138, 62, 255, 243, 22, 209, 2, 138, 45, 178, 101, 240, 18, 186, 71, 239, 137, 191, 134, - 128, 221, 181, 173, 242, 111, 117, 45, 255, 138, 101, 79, 242, 42, 4, 144, 245, 193, - 79, 14, 44, 201, 223, 0, 193, 123, 75, 155, 140, 248, 0, 226, 246, 230, 126, 7, 32, - 107, 173, 193, 206, 184, 11, 33, 148, 104, 32, 79, 149, 71, 68, 150, 6, 47, 90, 231, - 151, 14, 121, 196, 169, 249, 117, 154, 167, 139, 103, 62, 97, 250, 131, 160, 92, 239, - 18, 236, 110, 184, 102, 30, 194, 175, 243, 145, 169, 183, 163, 141, 244, 186, 172, 251, - 3, 78, 165, 33, 12, 2, 136, 180, 178, 83, 117, 0, 184, 170, 255, 69, 131, 123, 8, 212, - 158, 162, 119, 137, 146, 63, 95, 133, 186, 91, 255, 152, 187, 107, 113, 147, 51, 219, - 207, 5, 160, 169, 97, 9, 1, 202, 152, 186, 128, 160, 110, 120, 7, 176, 103, 87, 30, - 137, 240, 67, 55, 79, 147, 223, 45, 177, 210, 101, 225, 22, 25, 129, 111, 101, 21, 213, - 20, 254, 36, 57, 67, 70, 93, 192, 11, 180, 75, 99, 185, 77, 75, 74, 63, 182, 183, 208, - 16, 69, 237, 96, 76, 96, 212, 242, 6, 169, 14, 250, 168, 129, 18, 141, 240, 101, 196, - 96, 120, 88, 90, 51, 77, 12, 133, 212, 192, 107, 131, 238, 34, 237, 93, 157, 108, 13, - 255, 187, 163, 106, 148, 108, 105, 244, 243, 174, 189, 180, 48, 102, 57, 170, 118, 211, - 110, 126, 222, 165, 93, 36, 157, 90, 14, 135, 184, 197, 185, 7, 99, 199, 224, 225, 243, - 212, 116, 149, 137, 186, 16, 196, 73, 23, 11, 248, 248, 67, 167, 149, 154, 64, 76, 218, - 119, 135, 239, 34, 48, 66, 57, 109, 246, 3, 141, 169, 42, 157, 222, 21, 40, 183, 168, - 97, 195, 106, 244, 229, 61, 122, 136, 59, 255, 120, 86, 30, 63, 226, 18, 65, 218, 188, - 195, 217, 85, 12, 211, 221, 188, 27, 8, 98, 103, 211, 213, 217, 65, 82, 229, 145, 80, - 147, 220, 57, 143, 20, 189, 253, 106, 13, 21, 170, 60, 24, 48, 162, 234, 0, 240, 226, - 4, 28, 76, 93, 56, 3, 187, 223, 58, 31, 184, 58, 234, 198, 140, 223, 217, 1, 147, 94, - 218, 199, 154, 121, 137, 44, 229, 0, 1, 10, 133, 250, 140, 64, 150, 89, 64, 112, 178, - 221, 87, 19, 24, 104, 252, 28, 65, 207, 28, 195, 217, 73, 12, 16, 83, 55, 199, 84, 117, - 175, 123, 13, 234, 10, 54, 63, 245, 161, 74, 235, 92, 189, 247, 47, 62, 176, 41, 159, - 40, 250, 116, 63, 33, 193, 78, 72, 29, 215, 9, 191, 233, 243, 87, 14, 195, 7, 89, 101, - 0, 28, 0, 234, 205, 59, 142, 119, 119, 52, 143, 80, 151, 211, 184, 235, 98, 222, 206, - 170, 166, 4, 155, 3, 235, 26, 62, 8, 171, 19, 14, 53, 245, 77, 114, 175, 246, 170, 139, - 227, 212, 141, 72, 223, 134, 63, 91, 26, 12, 78, 253, 198, 162, 152, 202, 207, 170, - 254, 8, 4, 4, 175, 207, 84, 10, 108, 179, 157, 132, 110, 76, 201, 247, 227, 158, 106, - 59, 41, 206, 229, 128, 2, 60, 203, 65, 71, 160, 232, 186, 227, 51, 12, 142, 85, 93, 89, - 234, 236, 157, 230, 247, 167, 99, 7, 37, 146, 13, 53, 39, 255, 209, 177, 179, 17, 131, - 59, 16, 75, 180, 21, 119, 88, 4, 12, 49, 140, 3, 110, 235, 231, 92, 13, 41, 137, 21, - 37, 46, 138, 44, 250, 44, 161, 179, 114, 94, 63, 207, 192, 81, 234, 35, 125, 54, 2, - 214, 10, 57, 116, 154, 150, 147, 223, 232, 36, 108, 152, 145, 157, 132, 190, 103, 233, - 155, 141, 243, 249, 120, 72, 168, 14, 196, 35, 54, 107, 167, 218, 209, 1, 209, 197, - 187, 242, 76, 86, 229, 114, 131, 196, 69, 171, 118, 28, 51, 192, 146, 14, 140, 84, 66, - 155, 237, 194, 167, 121, 160, 166, 198, 166, 57, 13, 66, 162, 234, 148, 102, 133, 111, - 18, 166, 77, 156, 75, 84, 220, 80, 35, 81, 141, 23, 197, 162, 23, 167, 187, 187, 187, - 137, 184, 96, 140, 162, 6, 49, 63, 39, 84, 107, 85, 202, 168, 51, 194, 214, 132, 253, - 253, 189, 231, 1, 226, 118, 104, 84, 147, 244, 58, 233, 250, 66, 26, 109, 223, 34, 2, - 2, 112, 141, 147, 230, 134, 73, 45, 105, 180, 223, 52, 95, 40, 235, 209, 50, 67, 193, - 22, 176, 176, 128, 140, 238, 252, 129, 220, 175, 79, 133, 12, 123, 209, 64, 5, 160, 39, - 47, 66, 122, 245, 65, 102, 133, 58, 74, 138, 153, 217, 48, 59, 84, 135, 117, 92, 131, - 44, 109, 40, 105, 69, 29, 14, 142, 71, 87, 112, 68, 134, 0, 14, 158, 14, 68, 15, 180, - 150, 108, 49, 196, 94, 82, 27, 208, 163, 103, 81, 85, 124, 61, 242, 151, 29, 74, 87, - 134, 166, 145, 186, 110, 207, 162, 99, 92, 133, 121, 137, 124, 90, 134, 5, 249, 231, - 181, 222, 38, 170, 141, 113, 204, 172, 169, 173, 63, 81, 170, 76, - ]; - let mut tx_blob: Vec = Vec::new(); - tx_blob.append(&mut pruned_p_blob); - tx_blob.append(&mut prunable_blob.clone()); - let mut buf = Vec::new(); - #[allow(clippy::expect_used)] - let tx: monero::Transaction = - monero::consensus::encode::deserialize(&tx_blob).expect("failed to serialize"); - #[allow(clippy::expect_used)] - get_transaction_prunable_blob(&tx, &mut buf).expect("failed to get out prunable blob"); - assert_eq!(prunable_blob, buf); - } -}