diff --git a/Cargo.lock b/Cargo.lock index 01d5329d..965e2c6c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -702,6 +702,7 @@ dependencies = [ "cuprate-p2p-core", "cuprate-pruning", "cuprate-test-utils", + "cuprate-types", "cuprate-wire", "dashmap", "futures", @@ -800,8 +801,12 @@ version = "0.0.0" name = "cuprate-types" version = "0.0.0" dependencies = [ + "bytes", + "cuprate-epee-encoding", + "cuprate-fixed-bytes", "curve25519-dalek", "monero-serai", + "serde", ] [[package]] @@ -813,6 +818,7 @@ dependencies = [ "cuprate-epee-encoding", "cuprate-fixed-bytes", "cuprate-levin", + "cuprate-types", "hex", "thiserror", ] diff --git a/net/wire/Cargo.toml b/net/wire/Cargo.toml index c71a77b8..101daa39 100644 --- a/net/wire/Cargo.toml +++ b/net/wire/Cargo.toml @@ -14,6 +14,7 @@ tracing = ["cuprate-levin/tracing"] cuprate-levin = { path = "../levin" } cuprate-epee-encoding = { path = "../epee-encoding" } cuprate-fixed-bytes = { path = "../fixed-bytes" } +cuprate-types = { path = "../../types", default-features = false, features = ["epee"] } bitflags = { workspace = true, features = ["std"] } bytes = { workspace = true, features = ["std"] } diff --git a/net/wire/src/p2p/common.rs b/net/wire/src/p2p/common.rs index 91adb908..d585d071 100644 --- a/net/wire/src/p2p/common.rs +++ b/net/wire/src/p2p/common.rs @@ -16,10 +16,9 @@ //! Common types that are used across multiple messages. use bitflags::bitflags; -use bytes::{Buf, BufMut, Bytes}; -use cuprate_epee_encoding::{epee_object, EpeeValue, InnerMarker}; -use cuprate_fixed_bytes::ByteArray; +use cuprate_epee_encoding::epee_object; +pub use cuprate_types::{BlockCompleteEntry, PrunedTxBlobEntry, TransactionBlobs}; use crate::NetworkAddress; @@ -168,113 +167,6 @@ epee_object! { rpc_credits_per_hash: u32 = 0_u32, } -/// A pruned tx with the hash of the missing prunable data -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct PrunedTxBlobEntry { - /// The Tx - pub tx: Bytes, - /// The Prunable Tx Hash - pub prunable_hash: ByteArray<32>, -} - -epee_object!( - PrunedTxBlobEntry, - tx: Bytes, - prunable_hash: ByteArray<32>, -); - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum TransactionBlobs { - Pruned(Vec), - Normal(Vec), - None, -} - -impl TransactionBlobs { - pub fn take_pruned(self) -> Option> { - match self { - TransactionBlobs::Normal(_) => None, - TransactionBlobs::Pruned(txs) => Some(txs), - TransactionBlobs::None => Some(vec![]), - } - } - - pub fn take_normal(self) -> Option> { - match self { - TransactionBlobs::Normal(txs) => Some(txs), - TransactionBlobs::Pruned(_) => None, - TransactionBlobs::None => Some(vec![]), - } - } - - pub fn len(&self) -> usize { - match self { - TransactionBlobs::Normal(txs) => txs.len(), - TransactionBlobs::Pruned(txs) => txs.len(), - TransactionBlobs::None => 0, - } - } - - pub fn is_empty(&self) -> bool { - self.len() == 0 - } -} - -/// A Block that can contain transactions -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct BlockCompleteEntry { - /// True if tx data is pruned - pub pruned: bool, - /// The Block - pub block: Bytes, - /// The Block Weight/Size - pub block_weight: u64, - /// The blocks txs - pub txs: TransactionBlobs, -} - -epee_object!( - BlockCompleteEntry, - pruned: bool = false, - block: Bytes, - block_weight: u64 = 0_u64, - txs: TransactionBlobs = TransactionBlobs::None => tx_blob_read, tx_blob_write, should_write_tx_blobs, -); - -fn tx_blob_read(b: &mut B) -> cuprate_epee_encoding::Result { - let marker = cuprate_epee_encoding::read_marker(b)?; - match marker.inner_marker { - InnerMarker::Object => Ok(TransactionBlobs::Pruned(Vec::read(b, &marker)?)), - InnerMarker::String => Ok(TransactionBlobs::Normal(Vec::read(b, &marker)?)), - _ => Err(cuprate_epee_encoding::Error::Value( - "Invalid marker for tx blobs".to_string(), - )), - } -} - -fn tx_blob_write( - val: TransactionBlobs, - field_name: &str, - w: &mut B, -) -> cuprate_epee_encoding::Result<()> { - if should_write_tx_blobs(&val) { - match val { - TransactionBlobs::Normal(bytes) => { - cuprate_epee_encoding::write_field(bytes, field_name, w)? - } - TransactionBlobs::Pruned(obj) => { - cuprate_epee_encoding::write_field(obj, field_name, w)? - } - TransactionBlobs::None => (), - } - } - Ok(()) -} - -fn should_write_tx_blobs(val: &TransactionBlobs) -> bool { - !val.is_empty() -} - #[cfg(test)] mod tests { diff --git a/net/wire/src/p2p/protocol.rs b/net/wire/src/p2p/protocol.rs index a385099b..73694d57 100644 --- a/net/wire/src/p2p/protocol.rs +++ b/net/wire/src/p2p/protocol.rs @@ -16,14 +16,14 @@ //! This module defines Monero protocol messages //! //! Protocol message requests don't have to be responded to in order unlike -//! admin messages. +//! admin messages. use bytes::Bytes; use cuprate_epee_encoding::{container_as_blob::ContainerAsBlob, epee_object}; use cuprate_fixed_bytes::{ByteArray, ByteArrayVec}; -use super::common::BlockCompleteEntry; +use crate::p2p::common::BlockCompleteEntry; /// A block that SHOULD have transactions #[derive(Debug, Clone, PartialEq, Eq)] diff --git a/p2p/p2p/Cargo.toml b/p2p/p2p/Cargo.toml index e9b03d2c..7cbbdcb1 100644 --- a/p2p/p2p/Cargo.toml +++ b/p2p/p2p/Cargo.toml @@ -13,6 +13,7 @@ cuprate-address-book = { path = "../address-book" } cuprate-pruning = { path = "../../pruning" } cuprate-helper = { path = "../../helper", features = ["asynch"], default-features = false } cuprate-async-buffer = { path = "../async-buffer" } +cuprate-types = { path = "../../types", default-features = false } monero-serai = { workspace = true, features = ["std"] } diff --git a/p2p/p2p/src/block_downloader/tests.rs b/p2p/p2p/src/block_downloader/tests.rs index 981c557f..f6ddbfc6 100644 --- a/p2p/p2p/src/block_downloader/tests.rs +++ b/p2p/p2p/src/block_downloader/tests.rs @@ -26,10 +26,8 @@ use cuprate_p2p_core::{ ProtocolResponse, }; use cuprate_pruning::PruningSeed; -use cuprate_wire::{ - common::{BlockCompleteEntry, TransactionBlobs}, - protocol::{ChainResponse, GetObjectsResponse}, -}; +use cuprate_types::{BlockCompleteEntry, TransactionBlobs}; +use cuprate_wire::protocol::{ChainResponse, GetObjectsResponse}; use crate::{ block_downloader::{download_blocks, BlockDownloaderConfig, ChainSvcRequest, ChainSvcResponse}, diff --git a/p2p/p2p/src/broadcast.rs b/p2p/p2p/src/broadcast.rs index cfda28bc..5d7d61e3 100644 --- a/p2p/p2p/src/broadcast.rs +++ b/p2p/p2p/src/broadcast.rs @@ -25,10 +25,8 @@ use tower::Service; use cuprate_p2p_core::{ client::InternalPeerID, BroadcastMessage, ConnectionDirection, NetworkZone, }; -use cuprate_wire::{ - common::{BlockCompleteEntry, TransactionBlobs}, - protocol::{NewFluffyBlock, NewTransactions}, -}; +use cuprate_types::{BlockCompleteEntry, TransactionBlobs}; +use cuprate_wire::protocol::{NewFluffyBlock, NewTransactions}; use crate::constants::{ DIFFUSION_FLUSH_AVERAGE_SECONDS_INBOUND, DIFFUSION_FLUSH_AVERAGE_SECONDS_OUTBOUND, diff --git a/types/Cargo.toml b/types/Cargo.toml index 7f6b8f8d..8f16eb48 100644 --- a/types/Cargo.toml +++ b/types/Cargo.toml @@ -9,11 +9,18 @@ repository = "https://github.com/Cuprate/cuprate/tree/main/types" keywords = ["cuprate", "types"] [features] -default = ["blockchain"] +default = ["blockchain", "epee", "serde"] blockchain = [] +epee = ["dep:cuprate-epee-encoding"] +serde = ["dep:serde"] [dependencies] +cuprate-epee-encoding = { path = "../net/epee-encoding", optional = true } +cuprate-fixed-bytes = { path = "../net/fixed-bytes" } + +bytes = { workspace = true } curve25519-dalek = { workspace = true } monero-serai = { workspace = true } +serde = { workspace = true, features = ["derive"], optional = true } [dev-dependencies] \ No newline at end of file diff --git a/types/README.md b/types/README.md index 6a2015af..4023e9ff 100644 --- a/types/README.md +++ b/types/README.md @@ -1,20 +1,11 @@ # `cuprate-types` -Various data types shared by Cuprate. +Shared data types within Cuprate. -- [1. File Structure](#1-file-structure) - - [1.1 `src/`](#11-src) +This crate is a kitchen-sink for data types that are shared across Cuprate. ---- - -## 1. File Structure -A quick reference of the structure of the folders & files in `cuprate-types`. - -Note that `lib.rs/mod.rs` files are purely for re-exporting/visibility/lints, and contain no code. Each sub-directory has a corresponding `mod.rs`. - -### 1.1 `src/` -The top-level `src/` files. - -| File | Purpose | -|---------------------|---------| -| `service.rs` | Types used in database requests; `enum {Request,Response}` -| `types.rs` | Various general types used by Cuprate \ No newline at end of file +# Features flags +| Feature flag | Does what | +|--------------|-----------| +| `blockchain` | Enables the `blockchain` module, containing the blockchain database request/response types +| `serde` | Enables `serde` on types where applicable +| `epee` | Enables `cuprate-epee-encoding` on types where applicable \ No newline at end of file diff --git a/types/src/block_complete_entry.rs b/types/src/block_complete_entry.rs new file mode 100644 index 00000000..ba5fc2b0 --- /dev/null +++ b/types/src/block_complete_entry.rs @@ -0,0 +1,154 @@ +//! Contains [`BlockCompleteEntry`] and the related types. + +//---------------------------------------------------------------------------------------------------- Import +#[cfg(feature = "epee")] +use bytes::Bytes; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +use cuprate_fixed_bytes::ByteArray; + +#[cfg(feature = "epee")] +use cuprate_epee_encoding::{ + epee_object, + macros::bytes::{Buf, BufMut}, + EpeeValue, InnerMarker, +}; + +//---------------------------------------------------------------------------------------------------- BlockCompleteEntry +/// A block that can contain transactions. +#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct BlockCompleteEntry { + /// `true` if transaction data is pruned. + pub pruned: bool, + /// The block. + pub block: Bytes, + /// The block weight/size. + pub block_weight: u64, + /// The block's transactions. + pub txs: TransactionBlobs, +} + +#[cfg(feature = "epee")] +epee_object!( + BlockCompleteEntry, + pruned: bool = false, + block: Bytes, + block_weight: u64 = 0_u64, + txs: TransactionBlobs = TransactionBlobs::None => + TransactionBlobs::tx_blob_read, + TransactionBlobs::tx_blob_write, + TransactionBlobs::should_write_tx_blobs, +); + +//---------------------------------------------------------------------------------------------------- TransactionBlobs +/// Transaction blobs within [`BlockCompleteEntry`]. +#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum TransactionBlobs { + /// Pruned transaction blobs. + Pruned(Vec), + /// Normal transaction blobs. + Normal(Vec), + #[default] + /// No transactions. + None, +} + +impl TransactionBlobs { + /// Returns [`Some`] if `self` is [`Self::Pruned`]. + pub fn take_pruned(self) -> Option> { + match self { + Self::Normal(_) => None, + Self::Pruned(txs) => Some(txs), + Self::None => Some(vec![]), + } + } + + /// Returns [`Some`] if `self` is [`Self::Normal`]. + pub fn take_normal(self) -> Option> { + match self { + Self::Normal(txs) => Some(txs), + Self::Pruned(_) => None, + Self::None => Some(vec![]), + } + } + + /// Returns the byte length of the blob. + pub fn len(&self) -> usize { + match self { + Self::Normal(txs) => txs.len(), + Self::Pruned(txs) => txs.len(), + Self::None => 0, + } + } + + /// Returns `true` if the byte length of the blob is `0`. + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Epee read function. + #[cfg(feature = "epee")] + fn tx_blob_read(b: &mut B) -> cuprate_epee_encoding::Result { + let marker = cuprate_epee_encoding::read_marker(b)?; + match marker.inner_marker { + InnerMarker::Object => Ok(Self::Pruned(Vec::read(b, &marker)?)), + InnerMarker::String => Ok(Self::Normal(Vec::read(b, &marker)?)), + _ => Err(cuprate_epee_encoding::Error::Value( + "Invalid marker for tx blobs".to_string(), + )), + } + } + + /// Epee write function. + #[cfg(feature = "epee")] + fn tx_blob_write( + self, + field_name: &str, + w: &mut B, + ) -> cuprate_epee_encoding::Result<()> { + if self.should_write_tx_blobs() { + match self { + Self::Normal(bytes) => { + cuprate_epee_encoding::write_field(bytes, field_name, w)?; + } + Self::Pruned(obj) => { + cuprate_epee_encoding::write_field(obj, field_name, w)?; + } + Self::None => (), + } + } + Ok(()) + } + + /// Epee should write function. + #[cfg(feature = "epee")] + fn should_write_tx_blobs(&self) -> bool { + !self.is_empty() + } +} + +//---------------------------------------------------------------------------------------------------- PrunedTxBlobEntry +/// A pruned transaction with the hash of the missing prunable data +#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct PrunedTxBlobEntry { + /// The transaction. + pub tx: Bytes, + /// The prunable transaction hash. + pub prunable_hash: ByteArray<32>, +} + +#[cfg(feature = "epee")] +epee_object!( + PrunedTxBlobEntry, + tx: Bytes, + prunable_hash: ByteArray<32>, +); + +//---------------------------------------------------------------------------------------------------- Import +#[cfg(test)] +mod tests {} diff --git a/types/src/lib.rs b/types/src/lib.rs index 2d161f7e..1cdb9d57 100644 --- a/types/src/lib.rs +++ b/types/src/lib.rs @@ -1,11 +1,4 @@ -//! Cuprate shared data types. -//! -//! This crate is a kitchen-sink for data types that are shared across `Cuprate`. -//! -//! # Features flags -//! The [`blockchain`] module, containing the blockchain database request/response -//! types, must be enabled with the `blockchain` feature (on by default). - +#![doc = include_str!("../README.md")] //---------------------------------------------------------------------------------------------------- Lints // Forbid lints. // Our code, and code generated (e.g macros) cannot overrule these. @@ -86,7 +79,10 @@ // // Documentation for each module is located in the respective file. +mod block_complete_entry; mod types; + +pub use block_complete_entry::{BlockCompleteEntry, PrunedTxBlobEntry, TransactionBlobs}; pub use types::{ ExtendedBlockHeader, OutputOnChain, VerifiedBlockInformation, VerifiedTransactionInformation, };