From bf6c21c71e28ad42c0a3a0263bfef0adcc82c38a Mon Sep 17 00:00:00 2001 From: hinto-janai Date: Wed, 24 Apr 2024 16:47:48 -0400 Subject: [PATCH] database: split `cumulative_difficulty` into low/high bits (#114) * types: split `cumulative_difficulty` into low/high bits * helper: add `map` module * database: use `helper`'s cumulative_diff functions * helper: rename functions splitting bits isn't necessarily `cumulative_difficulty` specific * database: fix tests * helper: docs * helper: test output is low-endian bits * helper: docs * Update helper/src/map.rs Co-authored-by: Boog900 * Update helper/src/map.rs Co-authored-by: Boog900 --------- Co-authored-by: Boog900 --- database/Cargo.toml | 2 +- database/src/backend/tests.rs | 3 +- database/src/ops/block.rs | 14 +++++++-- database/src/types.rs | 9 ++++-- helper/Cargo.toml | 3 +- helper/src/lib.rs | 3 ++ helper/src/map.rs | 58 +++++++++++++++++++++++++++++++++++ 7 files changed, 84 insertions(+), 8 deletions(-) create mode 100644 helper/src/map.rs diff --git a/database/Cargo.toml b/database/Cargo.toml index 5d7bb8b7..f73dcb44 100644 --- a/database/Cargo.toml +++ b/database/Cargo.toml @@ -25,7 +25,7 @@ cfg-if = { workspace = true } # FIXME: # We only need the `thread` feature if `service` is enabled. # Figure out how to enable features of an already pulled in dependency conditionally. -cuprate-helper = { path = "../helper", features = ["fs", "thread"] } +cuprate-helper = { path = "../helper", features = ["fs", "thread", "map"] } cuprate-types = { path = "../types", features = ["service"] } curve25519-dalek = { workspace = true } monero-pruning = { path = "../pruning" } diff --git a/database/src/backend/tests.rs b/database/src/backend/tests.rs index 9d05872b..0c530548 100644 --- a/database/src/backend/tests.rs +++ b/database/src/backend/tests.rs @@ -434,7 +434,8 @@ test_tables! { timestamp: 1, cumulative_generated_coins: 123, weight: 321, - cumulative_difficulty: 111, + cumulative_difficulty_low: 111, + cumulative_difficulty_high: 111, block_hash: [54; 32], cumulative_rct_outs: 2389, long_term_weight: 2389, diff --git a/database/src/ops/block.rs b/database/src/ops/block.rs index 8cfadc3f..8a70ba1f 100644 --- a/database/src/ops/block.rs +++ b/database/src/ops/block.rs @@ -10,6 +10,7 @@ use monero_serai::{ transaction::{Input, Timelock, Transaction}, }; +use cuprate_helper::map::{combine_low_high_bits_to_u128, split_u128_into_low_high_bits}; use cuprate_types::{ExtendedBlockHeader, TransactionVerificationData, VerifiedBlockInformation}; use crate::{ @@ -102,14 +103,18 @@ pub fn add_block( cumulative_generated_coins(&block.height.saturating_sub(1), tables.block_infos())? + block.generated_coins; + let (cumulative_difficulty_low, cumulative_difficulty_high) = + split_u128_into_low_high_bits(block.cumulative_difficulty); + // Block Info. tables.block_infos_mut().put( &block.height, &BlockInfo { + cumulative_difficulty_low, + cumulative_difficulty_high, cumulative_generated_coins, cumulative_rct_outs, timestamp: block.block.header.timestamp, - cumulative_difficulty: block.cumulative_difficulty, block_hash: block.block_hash, // INVARIANT: #[cfg] @ lib.rs asserts `usize == u64` weight: block.weight as u64, @@ -192,13 +197,18 @@ pub fn get_block_extended_header_from_height( let block_blob = tables.block_blobs().get(block_height)?.0; let block = Block::read(&mut block_blob.as_slice())?; + let cumulative_difficulty = combine_low_high_bits_to_u128( + block_info.cumulative_difficulty_low, + block_info.cumulative_difficulty_high, + ); + // INVARIANT: #[cfg] @ lib.rs asserts `usize == u64` #[allow(clippy::cast_possible_truncation)] Ok(ExtendedBlockHeader { + cumulative_difficulty, version: block.header.major_version, vote: block.header.minor_version, timestamp: block.header.timestamp, - cumulative_difficulty: block_info.cumulative_difficulty, block_weight: block_info.weight as usize, long_term_weight: block_info.long_term_weight as usize, }) diff --git a/database/src/types.rs b/database/src/types.rs index 8f36e5e5..6ff06a3c 100644 --- a/database/src/types.rs +++ b/database/src/types.rs @@ -138,7 +138,8 @@ pub struct PreRctOutputId { /// timestamp: 1, /// cumulative_generated_coins: 123, /// weight: 321, -/// cumulative_difficulty: 112, +/// cumulative_difficulty_low: 112, +/// cumulative_difficulty_high: 112, /// block_hash: [54; 32], /// cumulative_rct_outs: 2389, /// long_term_weight: 2389, @@ -165,8 +166,10 @@ pub struct BlockInfo { pub cumulative_generated_coins: u64, /// TODO pub weight: u64, - /// TODO - pub cumulative_difficulty: u128, + /// Least-significant 64 bits of the 128-bit cumulative difficulty. + pub cumulative_difficulty_low: u64, + /// Most-significant 64 bits of the 128-bit cumulative difficulty. + pub cumulative_difficulty_high: u64, /// TODO pub block_hash: [u8; 32], /// TODO diff --git a/helper/Cargo.toml b/helper/Cargo.toml index 306143a3..e77fe4e7 100644 --- a/helper/Cargo.toml +++ b/helper/Cargo.toml @@ -10,13 +10,14 @@ repository = "https://github.com/Cuprate/cuprate/tree/main/consensus" [features] # All features on by default. -default = ["std", "atomic", "asynch", "fs", "num", "time", "thread", "constants"] +default = ["std", "atomic", "asynch", "fs", "num", "map", "time", "thread", "constants"] std = [] atomic = ["dep:crossbeam"] asynch = ["dep:futures", "dep:rayon"] constants = [] fs = ["dep:dirs"] num = [] +map = [] time = ["dep:chrono", "std"] thread = ["std", "dep:target_os_lib"] diff --git a/helper/src/lib.rs b/helper/src/lib.rs index fc947a65..90f420d6 100644 --- a/helper/src/lib.rs +++ b/helper/src/lib.rs @@ -51,6 +51,9 @@ pub mod network; #[cfg(feature = "num")] pub mod num; +#[cfg(feature = "map")] +pub mod map; + #[cfg(feature = "thread")] pub mod thread; diff --git a/helper/src/map.rs b/helper/src/map.rs new file mode 100644 index 00000000..b17d6348 --- /dev/null +++ b/helper/src/map.rs @@ -0,0 +1,58 @@ +//! Mapping of data types. +//! +//! This module provides functions solely for mapping data types into others, mostly similar ones. +//! +//! `#[no_std]` compatible. + +//---------------------------------------------------------------------------------------------------- Use + +//---------------------------------------------------------------------------------------------------- `(u64, u64) <-> u128` +/// Split a [`u128`] value into 2 64-bit values. +/// +/// The tuple returned is `(low, high)` where `low` is the least significant +/// 64-bits of `number`, and `high` is the most significant. +/// +/// Note that the output of this function are `u64` representations of _bits_, not numerical values. +/// +/// See [`combine_low_high_bits_to_u128`] for the inverse function. +/// +/// ```rust +/// # use cuprate_helper::map::*; +/// let value = u128::MAX - 1; +/// let low = u64::MAX - 1; +/// let high = u64::MAX; +/// +/// assert_eq!(split_u128_into_low_high_bits(value), (low, high)); +/// ``` +#[inline] +pub const fn split_u128_into_low_high_bits(value: u128) -> (u64, u64) { + (value as u64, (value >> 64) as u64) +} + +/// Combine 2 64-bit values into a single [`u128`] value. +/// +/// The inputs: +/// - `low_bits` are the _least_ significant 64-bits of `cumulative_difficulty` +/// - `high_bits` are the _most_ significant 64-bits of `cumulative_difficulty` +/// +/// Note that `low_bits` & `high_bits` should be `u64` representation of _bits_, not numerical values. +/// +/// See [`split_u128_into_low_high_bits`] for the inverse function. +/// +/// ```rust +/// # use cuprate_helper::map::*; +/// let value = u128::MAX - 1; +/// let low = u64::MAX - 1; +/// let high = u64::MAX; +/// +/// assert_eq!(combine_low_high_bits_to_u128(low, high), value); +/// ``` +#[inline] +pub const fn combine_low_high_bits_to_u128(low_bits: u64, high_bits: u64) -> u128 { + let res = (high_bits as u128) << 64; + res | (low_bits as u128) +} + +//---------------------------------------------------------------------------------------------------- Tests +#[cfg(test)] +mod test {}