mirror of
https://github.com/Cuprate/cuprate.git
synced 2025-03-12 09:29:11 +00:00
add more docs
This commit is contained in:
parent
251c467931
commit
9b3ae11832
13 changed files with 359 additions and 31 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -810,11 +810,15 @@ dependencies = [
|
|||
"cuprate-database",
|
||||
"cuprate-database-service",
|
||||
"cuprate-helper",
|
||||
"cuprate-test-utils",
|
||||
"cuprate-types",
|
||||
"hex",
|
||||
"hex-literal",
|
||||
"monero-serai",
|
||||
"rayon",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tower",
|
||||
]
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ keywords = ["cuprate", "txpool", "transaction", "pool", "database"]
|
|||
[features]
|
||||
|
||||
[dependencies]
|
||||
cuprate-database = { path = "../database" }
|
||||
cuprate-database = { path = "../database", features = ["heed"] }
|
||||
cuprate-database-service = { path = "../service" }
|
||||
cuprate-types = { path = "../../types" }
|
||||
cuprate-helper = { path = "../../helper" , default-features = false, features = ["constants"] }
|
||||
|
@ -26,3 +26,8 @@ tower = { workspace = true }
|
|||
rayon = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
cuprate-test-utils = { path = "../../test-utils" }
|
||||
|
||||
tokio = { workspace = true }
|
||||
tempfile = { workspace = true }
|
||||
hex-literal = { workspace = true }
|
||||
|
|
|
@ -1,7 +1,154 @@
|
|||
use cuprate_database::config::Config as DbConfig;
|
||||
use cuprate_database::config::{Config as DbConfig, SyncMode};
|
||||
use cuprate_database_service::ReaderThreads;
|
||||
use cuprate_helper::fs::cuprate_txpool_dir;
|
||||
use cuprate_helper::fs::{cuprate_blockchain_dir, cuprate_txpool_dir};
|
||||
use std::borrow::Cow;
|
||||
use std::path::Path;
|
||||
use cuprate_database::resize::ResizeAlgorithm;
|
||||
|
||||
/// The default transaction pool weight limit.
|
||||
const DEFAULT_TXPOOL_WEIGHT_LIMIT: usize = 600 * 1024 * 1024;
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- ConfigBuilder
|
||||
/// Builder for [`Config`].
|
||||
///
|
||||
// SOMEDAY: there's are many more options to add in the future.
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct ConfigBuilder {
|
||||
/// [`Config::db_directory`].
|
||||
db_directory: Option<Cow<'static, Path>>,
|
||||
|
||||
/// [`Config::cuprate_database_config`].
|
||||
db_config: cuprate_database::config::ConfigBuilder,
|
||||
|
||||
/// [`Config::reader_threads`].
|
||||
reader_threads: Option<ReaderThreads>,
|
||||
|
||||
/// [`Config::max_txpool_weight`].
|
||||
max_txpool_weight: Option<usize>,
|
||||
}
|
||||
|
||||
impl ConfigBuilder {
|
||||
/// Create a new [`ConfigBuilder`].
|
||||
///
|
||||
/// [`ConfigBuilder::build`] can be called immediately
|
||||
/// after this function to use default values.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
db_directory: None,
|
||||
db_config: cuprate_database::config::ConfigBuilder::new(Cow::Borrowed(
|
||||
cuprate_blockchain_dir(),
|
||||
)),
|
||||
reader_threads: None,
|
||||
max_txpool_weight: None
|
||||
}
|
||||
}
|
||||
|
||||
/// Build into a [`Config`].
|
||||
///
|
||||
/// # Default values
|
||||
/// If [`ConfigBuilder::db_directory`] was not called,
|
||||
/// the default [`cuprate_blockchain_dir`] will be used.
|
||||
///
|
||||
/// For all other values, [`Default::default`] is used.
|
||||
pub fn build(self) -> Config {
|
||||
// INVARIANT: all PATH safety checks are done
|
||||
// in `helper::fs`. No need to do them here.
|
||||
let db_directory = self
|
||||
.db_directory
|
||||
.unwrap_or_else(|| Cow::Borrowed(cuprate_blockchain_dir()));
|
||||
|
||||
let reader_threads = self.reader_threads.unwrap_or_default();
|
||||
|
||||
let max_txpool_weight = self.max_txpool_weight.unwrap_or(DEFAULT_TXPOOL_WEIGHT_LIMIT);
|
||||
|
||||
let db_config = self
|
||||
.db_config
|
||||
.db_directory(db_directory)
|
||||
.reader_threads(reader_threads.as_threads())
|
||||
.build();
|
||||
|
||||
Config {
|
||||
db_config,
|
||||
reader_threads,
|
||||
max_txpool_weight
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets a new maximum weight for the transaction pool.
|
||||
pub const fn max_txpool_weight(mut self, max_txpool_weight: usize) -> Self {
|
||||
self.max_txpool_weight = Some(max_txpool_weight);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set a custom database directory (and file) [`Path`].
|
||||
#[must_use]
|
||||
pub fn db_directory(mut self, db_directory: Cow<'static, Path>) -> Self {
|
||||
self.db_directory = Some(db_directory);
|
||||
self
|
||||
}
|
||||
|
||||
/// Calls [`cuprate_database::config::ConfigBuilder::sync_mode`].
|
||||
#[must_use]
|
||||
pub fn sync_mode(mut self, sync_mode: SyncMode) -> Self {
|
||||
self.db_config = self.db_config.sync_mode(sync_mode);
|
||||
self
|
||||
}
|
||||
|
||||
/// Calls [`cuprate_database::config::ConfigBuilder::resize_algorithm`].
|
||||
#[must_use]
|
||||
pub fn resize_algorithm(mut self, resize_algorithm: ResizeAlgorithm) -> Self {
|
||||
self.db_config = self.db_config.resize_algorithm(resize_algorithm);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set a custom [`ReaderThreads`].
|
||||
#[must_use]
|
||||
pub const fn reader_threads(mut self, reader_threads: ReaderThreads) -> Self {
|
||||
self.reader_threads = Some(reader_threads);
|
||||
self
|
||||
}
|
||||
|
||||
/// Tune the [`ConfigBuilder`] for the highest performing,
|
||||
/// but also most resource-intensive & maybe risky settings.
|
||||
///
|
||||
/// Good default for testing, and resource-available machines.
|
||||
#[must_use]
|
||||
pub fn fast(mut self) -> Self {
|
||||
self.db_config =
|
||||
cuprate_database::config::ConfigBuilder::new(Cow::Borrowed(cuprate_blockchain_dir()))
|
||||
.fast();
|
||||
|
||||
self.reader_threads = Some(ReaderThreads::OnePerThread);
|
||||
self
|
||||
}
|
||||
|
||||
/// Tune the [`ConfigBuilder`] for the lowest performing,
|
||||
/// but also least resource-intensive settings.
|
||||
///
|
||||
/// Good default for resource-limited machines, e.g. a cheap VPS.
|
||||
#[must_use]
|
||||
pub fn low_power(mut self) -> Self {
|
||||
self.db_config =
|
||||
cuprate_database::config::ConfigBuilder::new(Cow::Borrowed(cuprate_blockchain_dir()))
|
||||
.low_power();
|
||||
|
||||
self.reader_threads = Some(ReaderThreads::One);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ConfigBuilder {
|
||||
fn default() -> Self {
|
||||
let db_directory = Cow::Borrowed(cuprate_blockchain_dir());
|
||||
Self {
|
||||
db_directory: Some(db_directory.clone()),
|
||||
db_config: cuprate_database::config::ConfigBuilder::new(db_directory),
|
||||
reader_threads: Some(ReaderThreads::default()),
|
||||
max_txpool_weight: Some(DEFAULT_TXPOOL_WEIGHT_LIMIT)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Config
|
||||
/// `cuprate_txpool` configuration.
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
mod config;
|
||||
pub mod config;
|
||||
mod free;
|
||||
mod ops;
|
||||
mod service;
|
||||
pub mod service;
|
||||
mod tables;
|
||||
mod types;
|
||||
|
||||
pub use config::Config;
|
||||
pub use free::open;
|
||||
|
||||
//re-exports
|
||||
pub use cuprate_database;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum TxPoolWriteError {
|
||||
/// The transaction could not be added as it double spends another tx in the pool.
|
||||
|
|
|
@ -7,6 +7,9 @@ use crate::{tables::SpentKeyImages, types::TransactionHash, TxPoolWriteError};
|
|||
/// Adds the transaction key images to the [`SpentKeyImages`] table.
|
||||
///
|
||||
/// This function will return an error if any of the key images are already spent.
|
||||
///
|
||||
/// # Panics
|
||||
/// This function will panic if any of the [`Input`]s are not [`Input::ToKey`]
|
||||
pub fn add_tx_key_images(
|
||||
inputs: &[Input],
|
||||
tx_hash: &TransactionHash,
|
||||
|
@ -24,6 +27,9 @@ pub fn add_tx_key_images(
|
|||
}
|
||||
|
||||
/// Removes key images from the [`SpentKeyImages`] table.
|
||||
///
|
||||
/// # Panics
|
||||
/// This function will panic if any of the [`Input`]s are not [`Input::ToKey`]
|
||||
pub fn remove_tx_key_images(
|
||||
inputs: &[Input],
|
||||
kis_table: &mut impl DatabaseRw<SpentKeyImages>,
|
||||
|
@ -39,7 +45,6 @@ pub fn remove_tx_key_images(
|
|||
///
|
||||
/// # Panics
|
||||
/// This function will panic if the [`Input`] is not [`Input::ToKey`]
|
||||
///
|
||||
fn ki_from_input(input: &Input) -> [u8; 32] {
|
||||
match input {
|
||||
Input::ToKey { key_image, .. } => key_image.compress().0,
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
use crate::tables::{Tables, TransactionBlobs};
|
||||
use crate::types::TransactionHash;
|
||||
use cuprate_database::{DatabaseRw, RuntimeError};
|
||||
//! Transaction read ops.
|
||||
//!
|
||||
//! This module handles reading full transaction data, like getting a transaction from the pool.
|
||||
use std::sync::Mutex;
|
||||
|
||||
use cuprate_database::RuntimeError;
|
||||
use cuprate_types::TransactionVerificationData;
|
||||
use monero_serai::transaction::Transaction;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use crate::{tables::Tables, types::TransactionHash};
|
||||
|
||||
/// Gets the [`TransactionVerificationData`] of a transaction in the tx-pool, leaving the tx in the pool.
|
||||
pub fn get_transaction_verification_data(
|
||||
|
|
|
@ -1,12 +1,20 @@
|
|||
use crate::ops::key_images::{add_tx_key_images, remove_tx_key_images};
|
||||
use crate::tables::TablesMut;
|
||||
use crate::types::{TransactionHash, TransactionInfo};
|
||||
use crate::TxPoolWriteError;
|
||||
//! Transaction writing ops.
|
||||
//!
|
||||
//! This module handles writing full transaction data, like removing or adding a transaction.
|
||||
use std::sync::Arc;
|
||||
|
||||
use bytemuck::TransparentWrapper;
|
||||
use monero_serai::transaction::{Input, Transaction};
|
||||
|
||||
use cuprate_database::{RuntimeError, StorableVec};
|
||||
use cuprate_types::TransactionVerificationData;
|
||||
use monero_serai::transaction::{Input, Transaction};
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::{
|
||||
ops::key_images::{add_tx_key_images, remove_tx_key_images},
|
||||
tables::TablesMut,
|
||||
types::{TransactionHash, TransactionInfo},
|
||||
TxPoolWriteError,
|
||||
};
|
||||
|
||||
/// Adds a transaction to the tx-pool.
|
||||
///
|
||||
|
@ -14,7 +22,6 @@ use std::sync::Arc;
|
|||
///
|
||||
/// # Panics
|
||||
/// This function will panic if the transactions inputs are not all of type [`Input::ToKey`].
|
||||
///
|
||||
fn add_transaction(
|
||||
tx: Arc<TransactionVerificationData>,
|
||||
state_stem: bool,
|
||||
|
@ -50,6 +57,9 @@ fn add_transaction(
|
|||
}
|
||||
|
||||
/// Removes a transaction from the transaction pool.
|
||||
///
|
||||
/// # Panics
|
||||
/// This function will panic if the transactions inputs are not all of type [`Input::ToKey`].
|
||||
fn remove_transaction(
|
||||
tx_hash: &TransactionHash,
|
||||
tables: &mut impl TablesMut,
|
||||
|
|
|
@ -1,5 +1,130 @@
|
|||
//! [`tower::Service`] integeration + thread-pool.
|
||||
//!
|
||||
//! ## `service`
|
||||
//! The `service` module implements the [`tower`] integration,
|
||||
//! along with the reader/writer thread-pool system.
|
||||
//!
|
||||
//! The thread-pool allows outside crates to communicate with it by
|
||||
//! sending database [`Request`][req_r]s and receiving [`Response`][resp]s `async`hronously -
|
||||
//! without having to actually worry and handle the database themselves.
|
||||
//!
|
||||
//! The system is managed by this crate, and only requires [`init`] by the user.
|
||||
//!
|
||||
//! This module must be enabled with the `service` feature.
|
||||
//!
|
||||
//! ## Handles
|
||||
//! The 2 handles to the database are:
|
||||
//! - [`BCReadHandle`]
|
||||
//! - [`BCWriteHandle`]
|
||||
//!
|
||||
//! The 1st allows any caller to send [`ReadRequest`][req_r]s.
|
||||
//!
|
||||
//! The 2nd allows any caller to send [`WriteRequest`][req_w]s.
|
||||
//!
|
||||
//! The `DatabaseReadHandle` can be shared as it is cheaply [`Clone`]able, however,
|
||||
//! the `DatabaseWriteHandle` cannot be cloned. There is only 1 place in Cuprate that
|
||||
//! writes, so it is passed there and used.
|
||||
//!
|
||||
//! ## Initialization
|
||||
//! The database & thread-pool system can be initialized with [`init()`].
|
||||
//!
|
||||
//! This causes the underlying database/threads to be setup
|
||||
//! and returns a read/write handle to that database.
|
||||
//!
|
||||
//! ## Shutdown
|
||||
//! Upon the above handles being dropped, the corresponding thread(s) will automatically exit, i.e:
|
||||
//! - The last [`BCReadHandle`] is dropped => reader thread-pool exits
|
||||
//! - The last [`BCWriteHandle`] is dropped => writer thread exits
|
||||
//!
|
||||
//! Upon dropping the [`cuprate_database::Env`]:
|
||||
//! - All un-processed database transactions are completed
|
||||
//! - All data gets flushed to disk (caused by [`Drop::drop`] impl on `Env`)
|
||||
//!
|
||||
//! ## Request and Response
|
||||
//! To interact with the database (whether reading or writing data),
|
||||
//! a `Request` can be sent using one of the above handles.
|
||||
//!
|
||||
//! Both the handles implement `tower::Service`, so they can be [`tower::Service::call`]ed.
|
||||
//!
|
||||
//! An `async`hronous channel will be returned from the call.
|
||||
//! This channel can be `.await`ed upon to (eventually) receive
|
||||
//! the corresponding `Response` to your `Request`.
|
||||
//!
|
||||
//! [req_r]: cuprate_types::blockchain::BCReadRequest
|
||||
//!
|
||||
//! [req_w]: cuprate_types::blockchain::BCWriteRequest
|
||||
//!
|
||||
//! [resp]: cuprate_types::blockchain::BCResponse
|
||||
//!
|
||||
//! # Example
|
||||
//! Simple usage of `service`.
|
||||
//!
|
||||
//! ```rust
|
||||
//! use std::sync::Arc;
|
||||
//!
|
||||
//! use hex_literal::hex;
|
||||
//! use tower::{Service, ServiceExt};
|
||||
//!
|
||||
//! use cuprate_test_utils::data::tx_v1_sig0;
|
||||
//!
|
||||
//! use cuprate_txpool::{
|
||||
//! cuprate_database::Env,
|
||||
//! config::ConfigBuilder,
|
||||
//! service::interface::{
|
||||
//! TxpoolWriteRequest,
|
||||
//! TxpoolWriteResponse,
|
||||
//! TxpoolReadRequest,
|
||||
//! TxpoolReadResponse
|
||||
//! }
|
||||
//! };
|
||||
//!
|
||||
//! # #[tokio::main]
|
||||
//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
//! // Create a configuration for the database environment.
|
||||
//! let tmp_dir = tempfile::tempdir()?;
|
||||
//! let db_dir = tmp_dir.path().to_owned();
|
||||
//! let config = ConfigBuilder::new()
|
||||
//! .db_directory(db_dir.into())
|
||||
//! .build();
|
||||
//!
|
||||
//! // Initialize the database thread-pool.
|
||||
//! let (mut read_handle, mut write_handle, _) = cuprate_txpool::service::init(config)?;
|
||||
//!
|
||||
//! // Prepare a request to write block.
|
||||
//! let tx = tx_v1_sig0();
|
||||
//! let request = TxpoolWriteRequest::AddTransaction {
|
||||
//! tx: Arc::new(tx.into()),
|
||||
//! state_stem: false,
|
||||
//! };
|
||||
//!
|
||||
//! // Send the request.
|
||||
//! // We receive back an `async` channel that will
|
||||
//! // eventually yield the result when `service`
|
||||
//! // is done writing the tx.
|
||||
//! let response_channel = write_handle.ready().await?.call(request);
|
||||
//!
|
||||
//! // Block write was OK.
|
||||
//! let response = response_channel.await?;
|
||||
//! assert_eq!(response, TxpoolWriteResponse::AddTransaction);
|
||||
//!
|
||||
//! // Now, let's try getting the block hash
|
||||
//! // of the block we just wrote.
|
||||
//! let request = TxpoolReadRequest::TxBlob(tx_v1_sig0().tx_hash);
|
||||
//! let response_channel = read_handle.ready().await?.call(request);
|
||||
//! let response = response_channel.await?;
|
||||
//!
|
||||
//! // This causes the writer thread on the
|
||||
//! // other side of this handle to exit...
|
||||
//! drop(write_handle);
|
||||
//! // ...and this causes the reader thread-pool to exit.
|
||||
//! drop(read_handle);
|
||||
//! # Ok(()) }
|
||||
//! ```
|
||||
|
||||
mod free;
|
||||
mod interface;
|
||||
pub mod interface;
|
||||
mod read;
|
||||
mod types;
|
||||
mod write;
|
||||
|
||||
pub use free::init;
|
||||
|
|
|
@ -2,9 +2,14 @@ use std::sync::Arc;
|
|||
|
||||
use cuprate_database::{ConcreteEnv, InitError};
|
||||
|
||||
use crate::service::read::init_read_service;
|
||||
use crate::service::types::{TxpoolReadHandle, TxpoolWriteHandle};
|
||||
use crate::Config;
|
||||
use crate::{
|
||||
service::{
|
||||
read::init_read_service,
|
||||
types::{TxpoolReadHandle, TxpoolWriteHandle},
|
||||
write::init_write_service,
|
||||
},
|
||||
Config,
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Init
|
||||
#[cold]
|
||||
|
|
|
@ -26,13 +26,19 @@ pub enum TxpoolReadResponse {
|
|||
|
||||
//---------------------------------------------------------------------------------------------------- TxpoolWriteRequest
|
||||
pub enum TxpoolWriteRequest {
|
||||
AddTransaction(NewTransaction),
|
||||
AddTransaction {
|
||||
tx: Arc<TransactionVerificationData>,
|
||||
state_stem: bool
|
||||
},
|
||||
RemoveTransaction(TransactionHash),
|
||||
PromoteTransactionToFluffPool(TransactionHash),
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- TxpoolWriteResponse
|
||||
pub enum TxpoolWriteResponse {}
|
||||
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)]
|
||||
pub enum TxpoolWriteResponse {
|
||||
AddTransaction
|
||||
}
|
||||
|
||||
pub struct NewTransaction {
|
||||
tx: Arc<TransactionVerificationData>,
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
use cuprate_database::{ConcreteEnv, RuntimeError};
|
||||
use cuprate_database_service::DatabaseWriteHandle;
|
||||
use std::sync::Arc;
|
||||
use cuprate_types::blockchain::BCWriteRequest;
|
||||
use crate::{
|
||||
service::{
|
||||
interface::{TxpoolWriteRequest, TxpoolWriteResponse},
|
||||
|
@ -9,6 +5,10 @@ use crate::{
|
|||
},
|
||||
TxPoolWriteError,
|
||||
};
|
||||
use cuprate_database::{ConcreteEnv, RuntimeError};
|
||||
use cuprate_database_service::DatabaseWriteHandle;
|
||||
use cuprate_types::blockchain::BCWriteRequest;
|
||||
use std::sync::Arc;
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- init_write_service
|
||||
/// Initialize the txpool write service from a [`ConcreteEnv`].
|
||||
|
@ -23,7 +23,8 @@ fn handle_txpool_request(
|
|||
req: &TxpoolWriteRequest,
|
||||
) -> Result<TxpoolWriteResponse, TxPoolWriteError> {
|
||||
match req {
|
||||
TxpoolWriteRequest::AddTransaction(tx) =>
|
||||
TxpoolWriteRequest::AddTransaction { .. } => todo!(),
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,5 +36,3 @@ fn handle_txpool_request(
|
|||
//
|
||||
// Each function will return the [`Response`] that we
|
||||
// should send back to the caller in [`map_request()`].
|
||||
|
||||
/// [`BCWriteRequest::WriteBlock`].
|
|
@ -13,7 +13,7 @@ default = ["blockchain", "epee", "serde"]
|
|||
blockchain = []
|
||||
epee = ["dep:cuprate-epee-encoding"]
|
||||
serde = ["dep:serde"]
|
||||
protptest = ["dep:proptest-derive"]
|
||||
proptest = ["dep:proptest-derive"]
|
||||
|
||||
[dependencies]
|
||||
cuprate-epee-encoding = { path = "../net/epee-encoding", optional = true }
|
||||
|
|
|
@ -181,6 +181,21 @@ impl TransactionVerificationData {
|
|||
}
|
||||
}
|
||||
|
||||
// This impl is mainly for testing, going from a verified tx to a tx-pool tx doesn't have
|
||||
// many use cases.
|
||||
impl From<VerifiedTransactionInformation> for TransactionVerificationData {
|
||||
fn from(value: VerifiedTransactionInformation) -> Self {
|
||||
TransactionVerificationData {
|
||||
tx: value.tx,
|
||||
tx_blob: value.tx_blob,
|
||||
tx_weight: value.tx_weight,
|
||||
fee: value.fee,
|
||||
tx_hash: value.tx_hash,
|
||||
cached_verification_state: StdMutex::new(CachedVerificationState::NotVerified),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Tests
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
|
Loading…
Reference in a new issue