mirror of
https://github.com/Cuprate/cuprate.git
synced 2024-12-22 19:49:28 +00:00
Keep pruning seeds decompressed (#90)
* keep pruning seeds decompressed. * add a function to check seeds for P2P network * review comments * add next_pruned_block tests * fix docs
This commit is contained in:
parent
8f22d8ab79
commit
729b0fb0cf
5 changed files with 330 additions and 135 deletions
|
@ -92,7 +92,7 @@ async fn add_new_peer_already_connected() {
|
||||||
addr: None,
|
addr: None,
|
||||||
id: 0,
|
id: 0,
|
||||||
handle,
|
handle,
|
||||||
pruning_seed: PruningSeed::try_from(385).unwrap(),
|
pruning_seed: PruningSeed::decompress(385).unwrap(),
|
||||||
rpc_port: 0,
|
rpc_port: 0,
|
||||||
rpc_credits_per_hash: 0,
|
rpc_credits_per_hash: 0,
|
||||||
},
|
},
|
||||||
|
@ -110,7 +110,7 @@ async fn add_new_peer_already_connected() {
|
||||||
addr: None,
|
addr: None,
|
||||||
id: 0,
|
id: 0,
|
||||||
handle,
|
handle,
|
||||||
pruning_seed: PruningSeed::try_from(385).unwrap(),
|
pruning_seed: PruningSeed::decompress(385).unwrap(),
|
||||||
rpc_port: 0,
|
rpc_port: 0,
|
||||||
rpc_credits_per_hash: 0,
|
rpc_credits_per_hash: 0,
|
||||||
},
|
},
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::collections::{BTreeMap, HashMap, HashSet};
|
||||||
use rand::{seq::SliceRandom, Rng};
|
use rand::{seq::SliceRandom, Rng};
|
||||||
|
|
||||||
use monero_p2p::{services::ZoneSpecificPeerListEntryBase, NetZoneAddress, NetworkZone};
|
use monero_p2p::{services::ZoneSpecificPeerListEntryBase, NetZoneAddress, NetworkZone};
|
||||||
use monero_pruning::{PruningSeed, CRYPTONOTE_MAX_BLOCK_NUMBER};
|
use monero_pruning::{PruningSeed, CRYPTONOTE_MAX_BLOCK_HEIGHT};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod tests;
|
pub mod tests;
|
||||||
|
@ -95,7 +95,7 @@ impl<Z: NetworkZone> PeerList<Z> {
|
||||||
if let Some(needed_height) = block_needed {
|
if let Some(needed_height) = block_needed {
|
||||||
let (_, addresses_with_block) = self.pruning_seeds.iter().find(|(seed, _)| {
|
let (_, addresses_with_block) = self.pruning_seeds.iter().find(|(seed, _)| {
|
||||||
// TODO: factor in peer blockchain height?
|
// TODO: factor in peer blockchain height?
|
||||||
seed.get_next_unpruned_block(needed_height, CRYPTONOTE_MAX_BLOCK_NUMBER)
|
seed.get_next_unpruned_block(needed_height, CRYPTONOTE_MAX_BLOCK_HEIGHT)
|
||||||
.expect("Explain")
|
.expect("Explain")
|
||||||
== needed_height
|
== needed_height
|
||||||
})?;
|
})?;
|
||||||
|
|
|
@ -18,7 +18,7 @@ fn make_fake_peer(
|
||||||
adr: TestNetZoneAddr(id),
|
adr: TestNetZoneAddr(id),
|
||||||
id: id as u64,
|
id: id as u64,
|
||||||
last_seen: 0,
|
last_seen: 0,
|
||||||
pruning_seed: PruningSeed::try_from(pruning_seed.unwrap_or(0)).unwrap(),
|
pruning_seed: PruningSeed::decompress(pruning_seed.unwrap_or(0)).unwrap(),
|
||||||
rpc_port: 0,
|
rpc_port: 0,
|
||||||
rpc_credits_per_hash: 0,
|
rpc_credits_per_hash: 0,
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ impl<A: NetZoneAddress> From<ZoneSpecificPeerListEntryBase<A>> for monero_wire::
|
||||||
adr: value.adr.into(),
|
adr: value.adr.into(),
|
||||||
id: value.id,
|
id: value.id,
|
||||||
last_seen: value.last_seen,
|
last_seen: value.last_seen,
|
||||||
pruning_seed: value.pruning_seed.into(),
|
pruning_seed: value.pruning_seed.compress(),
|
||||||
rpc_port: value.rpc_port,
|
rpc_port: value.rpc_port,
|
||||||
rpc_credits_per_hash: value.rpc_credits_per_hash,
|
rpc_credits_per_hash: value.rpc_credits_per_hash,
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ impl<A: NetZoneAddress> TryFrom<monero_wire::PeerListEntryBase>
|
||||||
adr: value.adr.try_into()?,
|
adr: value.adr.try_into()?,
|
||||||
id: value.id,
|
id: value.id,
|
||||||
last_seen: value.last_seen,
|
last_seen: value.last_seen,
|
||||||
pruning_seed: PruningSeed::try_from(value.pruning_seed)?,
|
pruning_seed: PruningSeed::decompress_p2p_rules(value.pruning_seed)?,
|
||||||
rpc_port: value.rpc_port,
|
rpc_port: value.rpc_port,
|
||||||
rpc_credits_per_hash: value.rpc_credits_per_hash,
|
rpc_credits_per_hash: value.rpc_credits_per_hash,
|
||||||
})
|
})
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
//! use monero_pruning::PruningSeed;
|
//! use monero_pruning::PruningSeed;
|
||||||
//!
|
//!
|
||||||
//! let seed: u32 = 386; // the seed you want to check is valid
|
//! let seed: u32 = 386; // the seed you want to check is valid
|
||||||
//! match PruningSeed::try_from(seed) {
|
//! match PruningSeed::decompress_p2p_rules(seed) {
|
||||||
//! Ok(seed) => seed, // seed is valid
|
//! Ok(seed) => seed, // seed is valid
|
||||||
//! Err(e) => panic!("seed is invalid")
|
//! Err(e) => panic!("seed is invalid")
|
||||||
//! };
|
//! };
|
||||||
|
@ -22,10 +22,12 @@ use std::cmp::Ordering;
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
pub const CRYPTONOTE_MAX_BLOCK_NUMBER: u64 = 500000000;
|
pub const CRYPTONOTE_MAX_BLOCK_HEIGHT: u64 = 500000000;
|
||||||
|
/// The default log stripes for Monero pruning.
|
||||||
pub const CRYPTONOTE_PRUNING_LOG_STRIPES: u32 = 3;
|
pub const CRYPTONOTE_PRUNING_LOG_STRIPES: u32 = 3;
|
||||||
|
/// The amount of blocks that peers keep before another stripe starts storing blocks.
|
||||||
pub const CRYPTONOTE_PRUNING_STRIPE_SIZE: u64 = 4096;
|
pub const CRYPTONOTE_PRUNING_STRIPE_SIZE: u64 = 4096;
|
||||||
|
/// The amount of blocks from the top of the chain that should not be pruned.
|
||||||
pub const CRYPTONOTE_PRUNING_TIP_BLOCKS: u64 = 5500;
|
pub const CRYPTONOTE_PRUNING_TIP_BLOCKS: u64 = 5500;
|
||||||
|
|
||||||
const PRUNING_SEED_LOG_STRIPES_SHIFT: u32 = 7;
|
const PRUNING_SEED_LOG_STRIPES_SHIFT: u32 = 7;
|
||||||
|
@ -39,9 +41,9 @@ pub enum PruningError {
|
||||||
LogStripesOutOfRange,
|
LogStripesOutOfRange,
|
||||||
#[error("Stripe is out of range")]
|
#[error("Stripe is out of range")]
|
||||||
StripeOutOfRange,
|
StripeOutOfRange,
|
||||||
#[error("The block height is greater than `CRYPTONOTE_MAX_BLOCK_NUMBER`")]
|
#[error("The block height is greater than `CRYPTONOTE_MAX_BLOCK_HEIGHT`")]
|
||||||
BlockHeightTooLarge,
|
BlockHeightTooLarge,
|
||||||
#[error("The blockchain height is greater than `CRYPTONOTE_MAX_BLOCK_NUMBER`")]
|
#[error("The blockchain height is greater than `CRYPTONOTE_MAX_BLOCK_HEIGHT`")]
|
||||||
BlockChainHeightTooLarge,
|
BlockChainHeightTooLarge,
|
||||||
#[error("The calculated height is smaller than the block height entered")]
|
#[error("The calculated height is smaller than the block height entered")]
|
||||||
CalculatedHeightSmallerThanEnteredBlock,
|
CalculatedHeightSmallerThanEnteredBlock,
|
||||||
|
@ -49,18 +51,137 @@ pub enum PruningError {
|
||||||
SeedDoesNotHaveCorrectLogStripes,
|
SeedDoesNotHaveCorrectLogStripes,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A Monero pruning seed which has methods to get the next pruned/ unpruned block.
|
/// A valid pruning seed for a Monero node.
|
||||||
///
|
///
|
||||||
// Internally we use an Option<u32> to represent if a pruning seed is 0 (None)which means
|
/// A pruning seed tells nodes which blocks they should keep and which they should prune.
|
||||||
// no pruning will take place.
|
|
||||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "borsh",
|
feature = "borsh",
|
||||||
derive(borsh::BorshSerialize, borsh::BorshDeserialize)
|
derive(borsh::BorshSerialize, borsh::BorshDeserialize)
|
||||||
)]
|
)]
|
||||||
pub struct PruningSeed(Option<u32>);
|
pub enum PruningSeed {
|
||||||
|
/// A peer with this seed is not pruned.
|
||||||
|
NotPruned,
|
||||||
|
/// A peer with this seed is pruned.
|
||||||
|
Pruned(DecompressedPruningSeed),
|
||||||
|
}
|
||||||
|
|
||||||
impl PartialOrd for PruningSeed {
|
impl PruningSeed {
|
||||||
|
/// Creates a new [`PruningSeed::Pruned`] seed.
|
||||||
|
///
|
||||||
|
/// See: [`DecompressedPruningSeed::new`]
|
||||||
|
pub fn new_pruned(stripe: u32, log_stripes: u32) -> Result<Self, PruningError> {
|
||||||
|
Ok(PruningSeed::Pruned(DecompressedPruningSeed::new(
|
||||||
|
stripe,
|
||||||
|
log_stripes,
|
||||||
|
)?))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempts to decompress a raw pruning seed.
|
||||||
|
///
|
||||||
|
/// An error means the pruning seed was invalid.
|
||||||
|
pub fn decompress(seed: u32) -> Result<Self, PruningError> {
|
||||||
|
Ok(DecompressedPruningSeed::decompress(seed)?
|
||||||
|
.map(PruningSeed::Pruned)
|
||||||
|
.unwrap_or(PruningSeed::NotPruned))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decompresses the seed, performing the same checks as [`PruningSeed::decompress`] and some more according to
|
||||||
|
/// Monero's p2p networks rules.
|
||||||
|
///
|
||||||
|
/// The only added check currently is that `log_stripes` == 3.
|
||||||
|
pub fn decompress_p2p_rules(seed: u32) -> Result<Self, PruningError> {
|
||||||
|
let seed = Self::decompress(seed)?;
|
||||||
|
|
||||||
|
if let Some(log_stripes) = seed.get_log_stripes() {
|
||||||
|
if log_stripes != CRYPTONOTE_PRUNING_LOG_STRIPES {
|
||||||
|
return Err(PruningError::LogStripesOutOfRange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(seed)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compresses this pruning seed to a u32.
|
||||||
|
pub fn compress(&self) -> u32 {
|
||||||
|
match self {
|
||||||
|
PruningSeed::NotPruned => 0,
|
||||||
|
PruningSeed::Pruned(seed) => seed.compress(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the `log_stripes` for this seed, if this seed is pruned otherwise [`None`] is returned.
|
||||||
|
pub fn get_log_stripes(&self) -> Option<u32> {
|
||||||
|
match self {
|
||||||
|
PruningSeed::NotPruned => None,
|
||||||
|
PruningSeed::Pruned(seed) => Some(seed.log_stripes),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the `stripe` for this seed, if this seed is pruned otherwise [`None`] is returned.
|
||||||
|
pub fn get_stripe(&self) -> Option<u32> {
|
||||||
|
match self {
|
||||||
|
PruningSeed::NotPruned => None,
|
||||||
|
PruningSeed::Pruned(seed) => Some(seed.stripe),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the next pruned block for a given `block_height` and `blockchain_height`
|
||||||
|
///
|
||||||
|
/// Each seed will store, in a cyclic manner, a portion of blocks while discarding
|
||||||
|
/// the ones that are out of your stripe. This function is finding the next height
|
||||||
|
/// for which a specific seed will start pruning blocks.
|
||||||
|
///
|
||||||
|
/// This will return Ok(None) if the seed does no pruning or if there is no pruned block
|
||||||
|
/// after this one.
|
||||||
|
///
|
||||||
|
/// ### Errors
|
||||||
|
///
|
||||||
|
/// This function will return an Error if the inputted `block_height` or
|
||||||
|
/// `blockchain_height` is greater than [`CRYPTONOTE_MAX_BLOCK_HEIGHT`].
|
||||||
|
///
|
||||||
|
/// This function will also error if `block_height` > `blockchain_height`
|
||||||
|
pub fn get_next_pruned_block(
|
||||||
|
&self,
|
||||||
|
block_height: u64,
|
||||||
|
blockchain_height: u64,
|
||||||
|
) -> Result<Option<u64>, PruningError> {
|
||||||
|
Ok(match self {
|
||||||
|
PruningSeed::NotPruned => None,
|
||||||
|
PruningSeed::Pruned(seed) => {
|
||||||
|
seed.get_next_pruned_block(block_height, blockchain_height)?
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the next unpruned block for a given `block_height` and `blockchain_height`
|
||||||
|
///
|
||||||
|
/// Each seed will store, in a cyclic manner, a portion of blocks while discarding
|
||||||
|
/// the ones that are out of your stripe. This function is finding the next height
|
||||||
|
/// for which a specific seed will start storing blocks.
|
||||||
|
///
|
||||||
|
/// ### Errors
|
||||||
|
///
|
||||||
|
/// This function will return an Error if the inputted `block_height` or
|
||||||
|
/// `blockchain_height` is greater than [`CRYPTONOTE_MAX_BLOCK_HEIGHT`].
|
||||||
|
///
|
||||||
|
/// This function will also error if `block_height` > `blockchain_height`
|
||||||
|
///
|
||||||
|
pub fn get_next_unpruned_block(
|
||||||
|
&self,
|
||||||
|
block_height: u64,
|
||||||
|
blockchain_height: u64,
|
||||||
|
) -> Result<u64, PruningError> {
|
||||||
|
Ok(match self {
|
||||||
|
PruningSeed::NotPruned => block_height,
|
||||||
|
PruningSeed::Pruned(seed) => {
|
||||||
|
seed.get_next_unpruned_block(block_height, blockchain_height)?
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd<Self> for PruningSeed {
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
Some(self.cmp(other))
|
Some(self.cmp(other))
|
||||||
}
|
}
|
||||||
|
@ -68,19 +189,53 @@ impl PartialOrd for PruningSeed {
|
||||||
|
|
||||||
impl Ord for PruningSeed {
|
impl Ord for PruningSeed {
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
match (self.get_log_stripes(), other.get_log_stripes()) {
|
match (self, other) {
|
||||||
(None, None) => Ordering::Equal,
|
// Make sure pruning seeds storing more blocks are greater.
|
||||||
(None, Some(_)) => Ordering::Greater,
|
(PruningSeed::NotPruned, PruningSeed::NotPruned) => Ordering::Equal,
|
||||||
(Some(_), None) => Ordering::Less,
|
(PruningSeed::NotPruned, PruningSeed::Pruned(_)) => Ordering::Greater,
|
||||||
(Some(stripe_s), Some(stripe_o)) => match stripe_s.cmp(&stripe_o) {
|
(PruningSeed::Pruned(_), PruningSeed::NotPruned) => Ordering::Less,
|
||||||
Ordering::Equal => self.get_stripe().unwrap().cmp(&other.get_stripe().unwrap()),
|
|
||||||
ordering => ordering,
|
(PruningSeed::Pruned(seed1), PruningSeed::Pruned(seed2)) => seed1.cmp(seed2),
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PruningSeed {
|
/// This represents a valid Monero pruning seed.
|
||||||
|
///
|
||||||
|
/// It does allow representations of pruning seeds that Monero's P2P network would not allow, i.e.
|
||||||
|
/// it does not restrict the seed to only have a `log_stripes` of 8.
|
||||||
|
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "borsh",
|
||||||
|
derive(borsh::BorshSerialize, borsh::BorshDeserialize)
|
||||||
|
)]
|
||||||
|
pub struct DecompressedPruningSeed {
|
||||||
|
/// The amount of portions the blockchain is split into.
|
||||||
|
log_stripes: u32,
|
||||||
|
/// The specific portion this peer keeps.
|
||||||
|
///
|
||||||
|
/// *MUST* be between 1..=2^log_stripes
|
||||||
|
stripe: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd<Self> for DecompressedPruningSeed {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for DecompressedPruningSeed {
|
||||||
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
// Compare the `log_stripes` first so peers which store more blocks are greater than peers
|
||||||
|
// storing less.
|
||||||
|
match self.log_stripes.cmp(&other.log_stripes) {
|
||||||
|
Ordering::Equal => self.stripe.cmp(&other.stripe),
|
||||||
|
ord => ord,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DecompressedPruningSeed {
|
||||||
/// Creates a new pruning seed from a `stripe` and `log_stripes`
|
/// Creates a new pruning seed from a `stripe` and `log_stripes`
|
||||||
///
|
///
|
||||||
/// ### What is a `stripe`
|
/// ### What is a `stripe`
|
||||||
|
@ -105,29 +260,47 @@ impl PruningSeed {
|
||||||
/// a valid seed you currently MUST pass in a number 1 to 8 for `stripe`
|
/// a valid seed you currently MUST pass in a number 1 to 8 for `stripe`
|
||||||
/// and 3 for `log_stripes`.*
|
/// and 3 for `log_stripes`.*
|
||||||
///
|
///
|
||||||
pub fn new(stripe: u32, log_stripes: u32) -> Result<PruningSeed, PruningError> {
|
pub fn new(stripe: u32, log_stripes: u32) -> Result<Self, PruningError> {
|
||||||
if log_stripes > PRUNING_SEED_LOG_STRIPES_MASK {
|
if log_stripes > PRUNING_SEED_LOG_STRIPES_MASK {
|
||||||
Err(PruningError::LogStripesOutOfRange)
|
Err(PruningError::LogStripesOutOfRange)
|
||||||
} else if !(stripe > 0 && stripe <= (1 << log_stripes)) {
|
} else if !(stripe > 0 && stripe <= (1 << log_stripes)) {
|
||||||
Err(PruningError::StripeOutOfRange)
|
Err(PruningError::StripeOutOfRange)
|
||||||
} else {
|
} else {
|
||||||
Ok(PruningSeed(Some(
|
Ok(DecompressedPruningSeed {
|
||||||
(log_stripes << PRUNING_SEED_LOG_STRIPES_SHIFT)
|
log_stripes,
|
||||||
| ((stripe - 1) << PRUNING_SEED_STRIPE_SHIFT),
|
stripe,
|
||||||
)))
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets log2 of the total amount of stripes this seed is using.
|
/// Attempts to decompress a raw pruning seed.
|
||||||
fn get_log_stripes(&self) -> Option<u32> {
|
///
|
||||||
let seed: u32 = self.0?;
|
/// Will return Ok(None) if the pruning seed means no pruning.
|
||||||
Some((seed >> PRUNING_SEED_LOG_STRIPES_SHIFT) & PRUNING_SEED_LOG_STRIPES_MASK)
|
///
|
||||||
|
/// An error means the pruning seed was invalid.
|
||||||
|
pub fn decompress(seed: u32) -> Result<Option<Self>, PruningError> {
|
||||||
|
if seed == 0 {
|
||||||
|
// No pruning.
|
||||||
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets the specific stripe of this seed.
|
let log_stripes = (seed >> PRUNING_SEED_LOG_STRIPES_SHIFT) & PRUNING_SEED_LOG_STRIPES_MASK;
|
||||||
fn get_stripe(&self) -> Option<u32> {
|
let stripe = 1 + ((seed >> PRUNING_SEED_STRIPE_SHIFT) & PRUNING_SEED_STRIPE_MASK);
|
||||||
let seed: u32 = self.0?;
|
|
||||||
Some(1 + ((seed >> PRUNING_SEED_STRIPE_SHIFT) & PRUNING_SEED_STRIPE_MASK))
|
if stripe > (1 << log_stripes) {
|
||||||
|
return Err(PruningError::StripeOutOfRange);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Some(DecompressedPruningSeed {
|
||||||
|
log_stripes,
|
||||||
|
stripe,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compresses the pruning seed into a u32.
|
||||||
|
pub fn compress(&self) -> u32 {
|
||||||
|
(self.log_stripes << PRUNING_SEED_LOG_STRIPES_SHIFT)
|
||||||
|
| ((self.stripe - 1) << PRUNING_SEED_STRIPE_SHIFT)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the next unpruned block for a given `block_height` and `blockchain_height`
|
/// Gets the next unpruned block for a given `block_height` and `blockchain_height`
|
||||||
|
@ -139,7 +312,7 @@ impl PruningSeed {
|
||||||
/// ### Errors
|
/// ### Errors
|
||||||
///
|
///
|
||||||
/// This function will return an Error if the inputted `block_height` or
|
/// This function will return an Error if the inputted `block_height` or
|
||||||
/// `blockchain_height` is greater than [`CRYPTONOTE_MAX_BLOCK_NUMBER`].
|
/// `blockchain_height` is greater than [`CRYPTONOTE_MAX_BLOCK_HEIGHT`].
|
||||||
///
|
///
|
||||||
/// This function will also error if `block_height` > `blockchain_height`
|
/// This function will also error if `block_height` > `blockchain_height`
|
||||||
///
|
///
|
||||||
|
@ -148,27 +321,23 @@ impl PruningSeed {
|
||||||
block_height: u64,
|
block_height: u64,
|
||||||
blockchain_height: u64,
|
blockchain_height: u64,
|
||||||
) -> Result<u64, PruningError> {
|
) -> Result<u64, PruningError> {
|
||||||
if block_height > CRYPTONOTE_MAX_BLOCK_NUMBER || block_height > blockchain_height {
|
if block_height > CRYPTONOTE_MAX_BLOCK_HEIGHT || block_height > blockchain_height {
|
||||||
Err(PruningError::BlockHeightTooLarge)
|
return Err(PruningError::BlockHeightTooLarge);
|
||||||
} else if blockchain_height > CRYPTONOTE_MAX_BLOCK_NUMBER {
|
}
|
||||||
Err(PruningError::BlockChainHeightTooLarge)
|
|
||||||
} else {
|
if blockchain_height > CRYPTONOTE_MAX_BLOCK_HEIGHT {
|
||||||
let Some(seed_stripe) = self.get_stripe() else {
|
return Err(PruningError::BlockChainHeightTooLarge);
|
||||||
// If the `get_stripe` returns None that means no pruning so the next
|
}
|
||||||
// unpruned block is the one inputted.
|
|
||||||
return Ok(block_height);
|
|
||||||
};
|
|
||||||
if block_height + CRYPTONOTE_PRUNING_TIP_BLOCKS >= blockchain_height {
|
if block_height + CRYPTONOTE_PRUNING_TIP_BLOCKS >= blockchain_height {
|
||||||
// If we are within `CRYPTONOTE_PRUNING_TIP_BLOCKS` of the chain we should
|
// If we are within `CRYPTONOTE_PRUNING_TIP_BLOCKS` of the chain we should
|
||||||
// not prune blocks.
|
// not prune blocks.
|
||||||
return Ok(block_height);
|
return Ok(block_height);
|
||||||
}
|
}
|
||||||
let seed_log_stripes = self
|
|
||||||
.get_log_stripes()
|
let block_pruning_stripe = get_block_pruning_stripe(block_height, blockchain_height, self.log_stripes)
|
||||||
.unwrap_or(CRYPTONOTE_PRUNING_LOG_STRIPES);
|
|
||||||
let block_pruning_stripe = get_block_pruning_stripe(block_height, blockchain_height, seed_log_stripes)
|
|
||||||
.expect("We just checked if `block_height + CRYPTONOTE_PRUNING_TIP_BLOCKS >= blockchain_height`");
|
.expect("We just checked if `block_height + CRYPTONOTE_PRUNING_TIP_BLOCKS >= blockchain_height`");
|
||||||
if seed_stripe == block_pruning_stripe {
|
if self.stripe == block_pruning_stripe {
|
||||||
// if we have the same stripe as a block that means we keep the block so
|
// if we have the same stripe as a block that means we keep the block so
|
||||||
// the entered block is the next un-pruned one.
|
// the entered block is the next un-pruned one.
|
||||||
return Ok(block_height);
|
return Ok(block_height);
|
||||||
|
@ -176,27 +345,26 @@ impl PruningSeed {
|
||||||
|
|
||||||
// cycles: how many times each seed has stored blocks so when all seeds have
|
// cycles: how many times each seed has stored blocks so when all seeds have
|
||||||
// stored blocks thats 1 cycle
|
// stored blocks thats 1 cycle
|
||||||
let cycles = (block_height / CRYPTONOTE_PRUNING_STRIPE_SIZE) >> seed_log_stripes;
|
let cycles = (block_height / CRYPTONOTE_PRUNING_STRIPE_SIZE) >> self.log_stripes;
|
||||||
// if our seed is before the blocks seed in a cycle that means we have already past our
|
// if our seed is before the blocks seed in a cycle that means we have already past our
|
||||||
// seed this cycle and need to start the next
|
// seed this cycle and need to start the next
|
||||||
let cycles_start = cycles
|
let cycles_start = cycles
|
||||||
+ if seed_stripe > block_pruning_stripe {
|
+ if self.stripe > block_pruning_stripe {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
1
|
1
|
||||||
};
|
};
|
||||||
|
|
||||||
// amt_of_cycles * blocks in a cycle + how many blocks through a cycles until the seed starts storing blocks
|
// amt_of_cycles * blocks in a cycle + how many blocks through a cycles until the seed starts storing blocks
|
||||||
let calculated_height = cycles_start
|
let calculated_height = cycles_start * (CRYPTONOTE_PRUNING_STRIPE_SIZE << self.log_stripes)
|
||||||
* (CRYPTONOTE_PRUNING_STRIPE_SIZE << seed_log_stripes)
|
+ (self.stripe as u64 - 1) * CRYPTONOTE_PRUNING_STRIPE_SIZE;
|
||||||
+ (seed_stripe as u64 - 1) * CRYPTONOTE_PRUNING_STRIPE_SIZE;
|
|
||||||
if calculated_height + CRYPTONOTE_PRUNING_TIP_BLOCKS > blockchain_height {
|
if calculated_height + CRYPTONOTE_PRUNING_TIP_BLOCKS > blockchain_height {
|
||||||
// if our calculated height is greater than the amount of tip blocks the the start of the tip blocks will be the next un-pruned
|
// if our calculated height is greater than the amount of tip blocks then the start of the tip blocks will be the next un-pruned
|
||||||
return Ok(blockchain_height.saturating_sub(CRYPTONOTE_PRUNING_TIP_BLOCKS));
|
Ok(blockchain_height.saturating_sub(CRYPTONOTE_PRUNING_TIP_BLOCKS))
|
||||||
}
|
} else if calculated_height < block_height {
|
||||||
if calculated_height < block_height {
|
Err(PruningError::CalculatedHeightSmallerThanEnteredBlock)
|
||||||
return Err(PruningError::CalculatedHeightSmallerThanEnteredBlock);
|
} else {
|
||||||
}
|
|
||||||
Ok(calculated_height)
|
Ok(calculated_height)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -210,7 +378,7 @@ impl PruningSeed {
|
||||||
/// ### Errors
|
/// ### Errors
|
||||||
///
|
///
|
||||||
/// This function will return an Error if the inputted `block_height` or
|
/// This function will return an Error if the inputted `block_height` or
|
||||||
/// `blockchain_height` is greater than [`CRYPTONOTE_MAX_BLOCK_NUMBER`].
|
/// `blockchain_height` is greater than [`CRYPTONOTE_MAX_BLOCK_HEIGHT`].
|
||||||
///
|
///
|
||||||
/// This function will also error if `block_height` > `blockchain_height`
|
/// This function will also error if `block_height` > `blockchain_height`
|
||||||
///
|
///
|
||||||
|
@ -218,58 +386,34 @@ impl PruningSeed {
|
||||||
&self,
|
&self,
|
||||||
block_height: u64,
|
block_height: u64,
|
||||||
blockchain_height: u64,
|
blockchain_height: u64,
|
||||||
) -> Result<u64, PruningError> {
|
) -> Result<Option<u64>, PruningError> {
|
||||||
let Some(seed_stripe) = self.get_stripe() else {
|
|
||||||
// If the `get_stripe` returns None that means no pruning so the next
|
|
||||||
// pruned block is nonexistent so we return the blockchain_height.
|
|
||||||
return Ok(blockchain_height);
|
|
||||||
};
|
|
||||||
if block_height + CRYPTONOTE_PRUNING_TIP_BLOCKS >= blockchain_height {
|
if block_height + CRYPTONOTE_PRUNING_TIP_BLOCKS >= blockchain_height {
|
||||||
// If we are within `CRYPTONOTE_PRUNING_TIP_BLOCKS` of the chain we should
|
// If we are within `CRYPTONOTE_PRUNING_TIP_BLOCKS` of the chain we should
|
||||||
// not prune blocks.
|
// not prune blocks.
|
||||||
return Ok(blockchain_height);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
let seed_log_stripes = self
|
|
||||||
.get_log_stripes()
|
let block_pruning_stripe = get_block_pruning_stripe(block_height, blockchain_height, self.log_stripes)
|
||||||
.unwrap_or(CRYPTONOTE_PRUNING_LOG_STRIPES);
|
|
||||||
let block_pruning_stripe = get_block_pruning_stripe(block_height, blockchain_height, seed_log_stripes)
|
|
||||||
.expect("We just checked if `block_height + CRYPTONOTE_PRUNING_TIP_BLOCKS >= blockchain_height`");
|
.expect("We just checked if `block_height + CRYPTONOTE_PRUNING_TIP_BLOCKS >= blockchain_height`");
|
||||||
if seed_stripe != block_pruning_stripe {
|
if self.stripe != block_pruning_stripe {
|
||||||
// if our stripe != the blocks stripe that means we prune that block
|
// if our stripe != the blocks stripe that means we prune that block
|
||||||
return Ok(block_height);
|
return Ok(Some(block_height));
|
||||||
}
|
}
|
||||||
|
|
||||||
// We can get the end of our "non-pruning" cycle by getting the next stripe's after us first un-pruned block height
|
// We can get the end of our "non-pruning" cycle by getting the next stripe's first un-pruned block height.
|
||||||
// so we calculate the next un-pruned block for the next stripe and return it as our next pruned block
|
// So we calculate the next un-pruned block for the next stripe and return it as our next pruned block
|
||||||
let next_stripe = (1 + seed_log_stripes) & ((1 << seed_log_stripes) - 1);
|
let next_stripe = 1 + (self.stripe & ((1 << self.log_stripes) - 1));
|
||||||
let seed = PruningSeed::new(next_stripe, seed_log_stripes)?;
|
let seed = DecompressedPruningSeed::new(next_stripe, self.log_stripes)
|
||||||
seed.get_next_unpruned_block(block_height, blockchain_height)
|
.expect("We just made sure this stripe is in range for this log_stripe");
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<u32> for PruningSeed {
|
let calculated_height = seed.get_next_unpruned_block(block_height, blockchain_height)?;
|
||||||
type Error = PruningError;
|
|
||||||
|
|
||||||
fn try_from(value: u32) -> Result<Self, Self::Error> {
|
if calculated_height + CRYPTONOTE_PRUNING_TIP_BLOCKS > blockchain_height {
|
||||||
if value == 0 {
|
// If the calculated height is in tip blocks then there is no next block to prune
|
||||||
Ok(PruningSeed(None))
|
Ok(None)
|
||||||
} else {
|
} else {
|
||||||
let seed = Self(Some(value));
|
Ok(Some(calculated_height))
|
||||||
let log_stripes = seed.get_log_stripes().expect("This will only return None if the inner value is None which will only happen if the seed is 0 but we checked for that");
|
|
||||||
if log_stripes != CRYPTONOTE_PRUNING_LOG_STRIPES {
|
|
||||||
return Err(PruningError::SeedDoesNotHaveCorrectLogStripes);
|
|
||||||
}
|
}
|
||||||
if seed.get_stripe().expect("same as above") > (1 << log_stripes) {
|
|
||||||
return Err(PruningError::StripeOutOfRange);
|
|
||||||
}
|
|
||||||
Ok(seed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<PruningSeed> for u32 {
|
|
||||||
fn from(value: PruningSeed) -> Self {
|
|
||||||
value.0.unwrap_or(0)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,9 +437,9 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
fn make_all_pruning_seeds() -> Vec<PruningSeed> {
|
fn make_all_pruning_seeds() -> Vec<PruningSeed> {
|
||||||
let possible_stripes = 1..(1 << CRYPTONOTE_PRUNING_LOG_STRIPES);
|
let possible_stripes = 1..=(1 << CRYPTONOTE_PRUNING_LOG_STRIPES);
|
||||||
possible_stripes
|
possible_stripes
|
||||||
.map(|stripe| PruningSeed::new(stripe, CRYPTONOTE_PRUNING_LOG_STRIPES).unwrap())
|
.map(|stripe| PruningSeed::new_pruned(stripe, CRYPTONOTE_PRUNING_LOG_STRIPES).unwrap())
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,11 +447,11 @@ mod tests {
|
||||||
fn from_u32_for_pruning_seed() {
|
fn from_u32_for_pruning_seed() {
|
||||||
let good_seeds = 384..=391;
|
let good_seeds = 384..=391;
|
||||||
for seed in good_seeds {
|
for seed in good_seeds {
|
||||||
assert!(PruningSeed::try_from(seed).is_ok());
|
assert!(PruningSeed::decompress(seed).is_ok());
|
||||||
}
|
}
|
||||||
let bad_seeds = [383, 392];
|
let bad_seeds = [383, 392];
|
||||||
for seed in bad_seeds {
|
for seed in bad_seeds {
|
||||||
assert!(PruningSeed::try_from(seed).is_err());
|
assert!(PruningSeed::decompress(seed).is_err());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -316,7 +460,7 @@ mod tests {
|
||||||
let invalid_stripes = [0, (1 << CRYPTONOTE_PRUNING_LOG_STRIPES) + 1];
|
let invalid_stripes = [0, (1 << CRYPTONOTE_PRUNING_LOG_STRIPES) + 1];
|
||||||
|
|
||||||
for stripe in invalid_stripes {
|
for stripe in invalid_stripes {
|
||||||
assert!(PruningSeed::new(stripe, CRYPTONOTE_PRUNING_LOG_STRIPES).is_err());
|
assert!(PruningSeed::new_pruned(stripe, CRYPTONOTE_PRUNING_LOG_STRIPES).is_err());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -421,18 +565,69 @@ mod tests {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
let zero_seed = PruningSeed(None);
|
let zero_seed = PruningSeed::NotPruned;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
zero_seed.get_next_unpruned_block(33443, 5565445).unwrap(),
|
zero_seed.get_next_unpruned_block(33443, 5565445).unwrap(),
|
||||||
33443
|
33443
|
||||||
);
|
);
|
||||||
|
|
||||||
let seed = PruningSeed(Some(384));
|
let seed = PruningSeed::decompress(384).unwrap();
|
||||||
|
|
||||||
// the next unpruned block is the first tip block
|
// the next unpruned block is the first tip block
|
||||||
assert_eq!(seed.get_next_unpruned_block(5000, 11000).unwrap(), 5500)
|
assert_eq!(seed.get_next_unpruned_block(5000, 11000).unwrap(), 5500)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: next_pruned_block
|
#[test]
|
||||||
|
fn next_pruned_block() {
|
||||||
|
let all_valid_seeds = make_all_pruning_seeds();
|
||||||
|
let blockchain_height = 76437863;
|
||||||
|
|
||||||
|
for seed in all_valid_seeds.iter().skip(1) {
|
||||||
|
assert_eq!(
|
||||||
|
seed.get_next_pruned_block(0, blockchain_height)
|
||||||
|
.unwrap()
|
||||||
|
.unwrap(),
|
||||||
|
0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i, seed) in all_valid_seeds.iter().enumerate() {
|
||||||
|
assert_eq!(
|
||||||
|
seed.get_next_pruned_block((i as u64 + 1) * 4096, blockchain_height)
|
||||||
|
.unwrap()
|
||||||
|
.unwrap(),
|
||||||
|
(i as u64 + 1) * 4096
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i, seed) in all_valid_seeds.iter().enumerate() {
|
||||||
|
assert_eq!(
|
||||||
|
seed.get_next_pruned_block((i as u64 + 8) * 4096, blockchain_height)
|
||||||
|
.unwrap()
|
||||||
|
.unwrap(),
|
||||||
|
(i as u64 + 9) * 4096
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
for seed in all_valid_seeds.iter() {
|
||||||
|
assert_eq!(
|
||||||
|
seed.get_next_pruned_block(76437863 - 1, blockchain_height)
|
||||||
|
.unwrap(),
|
||||||
|
None
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let zero_seed = PruningSeed::NotPruned;
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
zero_seed.get_next_pruned_block(33443, 5565445).unwrap(),
|
||||||
|
None
|
||||||
|
);
|
||||||
|
|
||||||
|
let seed = PruningSeed::decompress(384).unwrap();
|
||||||
|
|
||||||
|
// there is no next pruned block
|
||||||
|
assert_eq!(seed.get_next_pruned_block(5000, 10000).unwrap(), None)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue