diff --git a/src/sharechain/in_memory.rs b/src/sharechain/in_memory.rs index 8484a59..f2c4824 100644 --- a/src/sharechain/in_memory.rs +++ b/src/sharechain/in_memory.rs @@ -180,8 +180,8 @@ impl InMemoryShareChain { // Check if already added. if let Some(level) = p2_chain.level_at_height(new_block_p2pool_height) { - if level.blocks.contains_key(&block.hash) { - let block_in_chain = level.blocks.get(&block.hash).unwrap(); + if level.contains(&block.hash) { + let block_in_chain = level.get(&block.hash).unwrap(); info!(target: LOG_TARGET, "[{:?}] ✅ Block already added: {}:{}, verified: {}", self.pow_algo, block.height, &block.hash.to_hex()[0..8], block_in_chain.verified); @@ -198,7 +198,7 @@ impl InMemoryShareChain { } // this is safe as we already checked it does exist - let tip_height = p2_chain.get_tip().unwrap().height; + let tip_height = p2_chain.get_tip().unwrap().height(); // We keep more blocks than the share window, but its only to validate the share window. If a block comes in // older than the share window is way too old for us to care about. if block.height < tip_height.saturating_sub(self.config.share_window) && !syncing { @@ -260,10 +260,9 @@ impl InMemoryShareChain { }; // we want to count 1 short,as the final share will be for this node - let stop_height = tip_level.height.saturating_sub(self.config.share_window - 1); + let stop_height = tip_level.height().saturating_sub(self.config.share_window - 1); let mut cur_block = tip_level - .blocks - .get(&tip_level.chain_block) + .get(&tip_level.chain_block()) .ok_or(ShareChainError::BlockNotFound)?; update_insert( &mut miners_to_shares, @@ -275,7 +274,6 @@ impl InMemoryShareChain { let uncle_block = p2_chain .level_at_height(uncle.0) .ok_or(ShareChainError::UncleBlockNotFound)? - .blocks .get(&uncle.1) .ok_or(ShareChainError::UncleBlockNotFound)?; update_insert( @@ -299,7 +297,6 @@ impl InMemoryShareChain { let uncle_block = p2_chain .level_at_height(uncle.0) .ok_or(ShareChainError::UncleBlockNotFound)? - .blocks .get(&uncle.1) .ok_or(ShareChainError::UncleBlockNotFound)?; update_insert( @@ -350,7 +347,7 @@ impl InMemoryShareChain { res.push(block.clone()); } } else { - for block in level.blocks.values() { + for block in level.all_blocks() { num_actual_blocks += 1; res.push(block.clone()); } @@ -359,7 +356,7 @@ impl InMemoryShareChain { return Ok(res); } - level = if let Some(new_level) = p2_chain.level_at_height(level.height + 1) { + level = if let Some(new_level) = p2_chain.level_at_height(level.height() + 1) { new_level } else { break; @@ -489,7 +486,7 @@ impl ShareChain for InMemoryShareChain { let bl = self.p2_chain.read().await; let tip_level = bl.get_tip(); if let Some(tip_level) = tip_level { - Ok(Some((tip_level.height, tip_level.chain_block))) + Ok(Some((tip_level.height(), tip_level.chain_block()))) } else { Ok(None) } @@ -545,7 +542,6 @@ impl ShareChain for InMemoryShareChain { let uncle_block = chain_read_lock .level_at_height(uncle.0) .ok_or(ShareChainError::UncleBlockNotFound)? - .blocks .get(&uncle.1) .ok_or(ShareChainError::UncleBlockNotFound)?; miners_to_shares.insert( @@ -614,7 +610,7 @@ impl ShareChain for InMemoryShareChain { for uncle in &chain_block.uncles { excluded_uncles.push(uncle.1); } - for block in older_level.blocks.values() { + for block in older_level.all_blocks() { uncles.push(block.clone()); } } @@ -638,7 +634,7 @@ impl ShareChain for InMemoryShareChain { if chain_read_lock .level_at_height(parent.height) .ok_or(ShareChainError::BlockLevelNotFound)? - .chain_block != + .chain_block() != parent.hash { excluded_uncles.push(uncle.hash); @@ -668,7 +664,7 @@ impl ShareChain for InMemoryShareChain { for block in requested_blocks { if let Some(level) = p2_chain_read_lock.level_at_height(block.0) { - if let Some(block) = level.blocks.get(&block.1) { + if let Some(block) = level.get(&block.1) { blocks.push(block.clone()); } else { // if sync requestee only sees their behind on tip, they will fill in fixedhash::zero(), so it wont @@ -707,7 +703,7 @@ impl ShareChain for InMemoryShareChain { for their_block in their_blocks { if let Some(level) = p2_chain_read.level_at_height(their_block.0) { // Only split if the block is in the main chain - if level.chain_block == their_block.1 { + if level.chain_block() == their_block.1 { split_height2 = their_block.0.saturating_add(1); break; } @@ -720,7 +716,7 @@ impl ShareChain for InMemoryShareChain { self.all_blocks_with_lock(&p2_chain_read, Some(cmp::max(split_height, split_height2)), limit, true)?; let tip_level = p2_chain_read .get_tip() - .map(|tip_level| (tip_level.height, tip_level.chain_block)); + .map(|tip_level| (tip_level.height(), tip_level.chain_block())); let chain_pow = p2_chain_read.total_accumulated_tip_difficulty(); Ok((blocks, tip_level, chain_pow)) } @@ -760,7 +756,7 @@ impl ShareChain for InMemoryShareChain { let p2_chain_read_lock = self.p2_chain.read().await; let mut i_have_blocks = Vec::with_capacity(size); if let Some(tip) = p2_chain_read_lock.get_tip() { - let tip_height = tip.height; + let tip_height = tip.height(); let mut height = tip_height; for _ in 0..size { if let Some(level) = p2_chain_read_lock.level_at_height(height) { diff --git a/src/sharechain/lmdb_block_storage.rs b/src/sharechain/lmdb_block_storage.rs new file mode 100644 index 0000000..8d1a953 --- /dev/null +++ b/src/sharechain/lmdb_block_storage.rs @@ -0,0 +1 @@ +pub(crate) struct LmdbBlockStorage {} diff --git a/src/sharechain/mod.rs b/src/sharechain/mod.rs index 3322097..19882ce 100644 --- a/src/sharechain/mod.rs +++ b/src/sharechain/mod.rs @@ -47,6 +47,7 @@ pub const MIN_SHA3X_DIFFICULTY: u64 = 100_000_000; // 1 Mhs every ten seconds pub mod error; pub mod in_memory; +pub(crate) mod lmdb_block_storage; pub mod p2block; pub mod p2chain; mod p2chain_level; diff --git a/src/sharechain/p2chain.rs b/src/sharechain/p2chain.rs index 6eba14b..530c7c5 100644 --- a/src/sharechain/p2chain.rs +++ b/src/sharechain/p2chain.rs @@ -159,17 +159,13 @@ impl P2Chain { pub fn get_block_at_height(&self, height: u64, hash: &FixedHash) -> Option<&Arc> { let level = self.level_at_height(height)?; - level.blocks.get(hash) + level.get(hash) } #[cfg(test)] fn get_chain_block_at_height(&self, height: u64) -> Option<&Arc> { let level = self.level_at_height(height)?; - level.blocks.get(&level.chain_block) - } - - pub fn level_at_height_mut(&mut self, height: u64) -> Option<&mut P2ChainLevel> { - self.levels.get_mut(&height) + level.get(&level.chain_block) } pub fn new_empty(total_size: u64, share_window: u64, block_time: u64) -> Self { @@ -209,10 +205,10 @@ impl P2Chain { // the newly added block == 0 self.lwma.add_back(block.timestamp, block.target_difficulty()); let level = self - .level_at_height_mut(new_height) + .level_at_height(new_height) .ok_or(ShareChainError::BlockLevelNotFound)?; - level.chain_block = hash; - self.current_tip = level.height; + level.set_chain_block(hash); + self.current_tip = level.height(); self.cleanup_chain() } @@ -307,7 +303,7 @@ impl P2Chain { return Ok((new_tip, Vec::new())); } - if self.get_tip().is_some() && self.get_tip().unwrap().chain_block == block.prev_hash { + if self.get_tip().is_some() && self.get_tip().unwrap().chain_block() == block.prev_hash { // easy this builds on the tip info!(target: LOG_TARGET, "[{:?}] New block added to tip, and is now the new tip: {:?}:{}", algo, new_block_height, &block.hash.to_hex()[0..8]); for uncle in &block.uncles { @@ -320,13 +316,13 @@ impl P2Chain { let uncle_level = self .level_at_height(uncle.0.saturating_sub(1)) .ok_or(ShareChainError::BlockLevelNotFound)?; - if uncle_level.chain_block != uncle_parent.hash { + if uncle_level.chain_block() != uncle_parent.hash { return Err(ShareChainError::UncleParentNotInMainChain); } let own_level = self .level_at_height(uncle.0) .ok_or(ShareChainError::BlockLevelNotFound)?; - if own_level.chain_block == uncle.1 { + if own_level.chain_block() == uncle.1 { return Err(ShareChainError::UncleInMainChain { height: uncle.0, hash: uncle.1, @@ -379,7 +375,7 @@ impl P2Chain { let level = self .level_at_height(current_counting_block.height) .ok_or(ShareChainError::BlockLevelNotFound)?; - if level.chain_block == current_counting_block.hash { + if level.chain_block() == current_counting_block.hash { break; } // we can unwrap as we now the parent exists @@ -403,17 +399,17 @@ impl P2Chain { .expect("Failed to create LWMA"); self.lwma.add_front(block.timestamp, block.target_difficulty()); let chain_height = self - .level_at_height_mut(block.height) + .level_at_height(block.height) .ok_or(ShareChainError::BlockLevelNotFound)?; - chain_height.chain_block = block.hash; + chain_height.set_chain_block(block.hash); self.cached_shares = None; self.current_tip = block.height; // lets fix the chain // lets first go up and reset all chain block links let mut current_height = block.height; while self.level_at_height(current_height.saturating_add(1)).is_some() { - let mut_child_level = self.level_at_height_mut(current_height.saturating_add(1)).unwrap(); - mut_child_level.chain_block = FixedHash::zero(); + let mut_child_level = self.level_at_height(current_height.saturating_add(1)).unwrap(); + mut_child_level.set_chain_block(FixedHash::zero()); current_height += 1; } @@ -422,29 +418,27 @@ impl P2Chain { while self.level_at_height(current_block.height.saturating_sub(1)).is_some() { counter += 1; let parent_level = (self.level_at_height(current_block.height.saturating_sub(1)).unwrap()).clone(); - if current_block.prev_hash != parent_level.chain_block { + if current_block.prev_hash != parent_level.chain_block() { // safety check - let nextblock = parent_level.blocks.get(¤t_block.prev_hash); + let nextblock = parent_level.get(¤t_block.prev_hash); if nextblock.is_none() { error!(target: LOG_TARGET, "FATAL: Reorging (block in chain) failed because parent block was not found and chain data is corrupted."); panic!( "FATAL: Reorging (block in chain) failed because parent block was not found and chain \ data is corrupted. current_block: {:?}, current tip: {:?}", current_block, - self.get_tip() + self.get_tip().map(|t| t.height()) ); } // fix the main chain - let mut_parent_level = self - .level_at_height_mut(current_block.height.saturating_sub(1)) - .unwrap(); - mut_parent_level.chain_block = current_block.prev_hash; + let mut_parent_level = self.level_at_height(current_block.height.saturating_sub(1)).unwrap(); + mut_parent_level.set_chain_block(current_block.prev_hash); current_block = nextblock.unwrap().clone(); self.lwma .add_front(current_block.timestamp, current_block.target_difficulty()); } else if !self.lwma.is_full() { // we still need more blocks to fill up the lwma - let nextblock = parent_level.blocks.get(¤t_block.prev_hash); + let nextblock = parent_level.get(¤t_block.prev_hash); if nextblock.is_none() { error!(target: LOG_TARGET, "FATAL: Reorging (block not in chain) failed because parent block was not found and chain data is corrupted."); panic!( @@ -452,7 +446,7 @@ impl P2Chain { parent block was not found and chain data is corrupted. current_block: {:?}, current \ tip: {:?}", current_block, - self.get_tip() + self.get_tip().map(|t| t.height()) ); } @@ -486,14 +480,14 @@ impl P2Chain { // let see if we already have a block is a missing block of some other block for check_height in (height + 1)..height + MAX_UNCLE_AGE { if let Some(level) = self.level_at_height(check_height) { - for block in &level.blocks { - for uncles in &block.1.uncles { + for block in &level.all_blocks() { + for uncles in &block.uncles { if uncles.1 == hash { - next_level_data.push((block.1.height, block.1.hash)); + next_level_data.push((block.height, block.hash)); } } - if block.1.prev_hash == hash { - next_level_data.push((block.1.height, block.1.hash)); + if block.prev_hash == hash { + next_level_data.push((block.height, block.hash)); } } } @@ -506,7 +500,7 @@ impl P2Chain { let level = self .level_at_height(height) .ok_or(ShareChainError::BlockLevelNotFound)?; - let block = level.blocks.get(&hash).ok_or(ShareChainError::BlockNotFound)?; + let block = level.get(&hash).ok_or(ShareChainError::BlockNotFound)?; if block.verified { return Ok(()); } @@ -533,9 +527,9 @@ impl P2Chain { // lets replace this actual_block.verified = verified; let level = self - .level_at_height_mut(height) + .level_at_height(height) .ok_or(ShareChainError::BlockLevelNotFound)?; - level.blocks.insert(hash, Arc::new(actual_block)); + level.add_block(Arc::new(actual_block)); return Ok(()); } @@ -552,9 +546,9 @@ impl P2Chain { // lets replace this actual_block.verified = verified; let level = self - .level_at_height_mut(height) + .level_at_height(height) .ok_or(ShareChainError::BlockLevelNotFound)?; - level.blocks.insert(hash, Arc::new(actual_block)); + level.add_block(Arc::new(actual_block)); } Ok(()) @@ -569,7 +563,7 @@ impl P2Chain { self.levels.insert(new_block_height, new_level); return self.verify_chain(new_block_height, block_hash); } - match self.level_at_height_mut(new_block_height) { + match self.level_at_height(new_block_height) { Some(level) => { level.add_block(block)?; self.verify_chain(new_block_height, block_hash) @@ -603,16 +597,16 @@ impl P2Chain { Some(level) => level, None => return None, }; - parent_level.blocks.get(&block.prev_hash) + parent_level.get(&block.prev_hash) } pub fn get_tip(&self) -> Option<&P2ChainLevel> { self.level_at_height(self.current_tip) - .filter(|&level| level.chain_block != FixedHash::zero()) + .filter(|&level| level.chain_block() != FixedHash::zero()) } pub fn get_height(&self) -> u64 { - self.get_tip().map(|tip| tip.height).unwrap_or(0) + self.get_tip().map(|tip| tip.height()).unwrap_or(0) } pub fn get_max_chain_length(&self) -> usize { diff --git a/src/sharechain/p2chain_level.rs b/src/sharechain/p2chain_level.rs index 3dc661e..1092d0d 100644 --- a/src/sharechain/p2chain_level.rs +++ b/src/sharechain/p2chain_level.rs @@ -1,9 +1,3 @@ -use std::{collections::HashMap, sync::Arc}; - -use tari_common_types::types::{BlockHash, FixedHash}; - -use crate::sharechain::{error::ShareChainError, p2block::P2Block}; - // Copyright 2024. The Tari Project // // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the @@ -18,48 +12,87 @@ use crate::sharechain::{error::ShareChainError, p2block::P2Block}; // 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote // products derived from this software without specific prior written permission. // -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +// DAMAGE. + +use std::{collections::HashMap, sync::Arc}; + +use tari_common_types::types::{BlockHash, FixedHash}; + +use crate::sharechain::{error::ShareChainError, lmdb_block_storage::LmdbBlockStorage, p2block::P2Block}; + /// A collection of blocks with the same height. -#[derive(Debug, Clone)] pub struct P2ChainLevel { - pub blocks: HashMap>, - pub height: u64, - pub chain_block: BlockHash, + // pub blocks: HashMap>, + blocks: LmdbBlockStorage, + height: u64, + chain_block: BlockHash, } impl P2ChainLevel { pub fn new(block: Arc) -> Self { - let mut blocks = HashMap::new(); + // let mut blocks = HashMap::new(); // although this is the only block on this level, it might not be part of the main chain, so we need to set this // later let chain_block = FixedHash::zero(); let height = block.height; - blocks.insert(block.hash, block); - Self { - blocks, - height, - chain_block, - } + // blocks.insert(block.hash, block); + todo!(); + // Self { + // blocks, + // height, + // chain_block, + // } } - pub fn add_block(&mut self, block: Arc) -> Result<(), ShareChainError> { + pub fn height(&self) -> u64 { + self.height + } + + pub fn chain_block(&self) -> BlockHash { + self.chain_block + } + + pub fn set_chain_block(&self, hash: BlockHash) { + todo!() + // self.chain_block = hash; + } + + pub fn add_block(&self, block: Arc) -> Result<(), ShareChainError> { if self.height != block.height { return Err(ShareChainError::InvalidBlock { reason: "Block height does not match the chain level height".to_string(), }); } - self.blocks.insert(block.hash, block); + todo!(); + // self.blocks.insert(block.hash, block); Ok(()) } pub fn block_in_main_chain(&self) -> Option<&Arc> { - self.blocks.get(&self.chain_block) + todo!() + // self.blocks.get(&self.chain_block) + } + + pub fn get(&self, hash: &BlockHash) -> Option<&Arc> { + todo!() + // self.blocks.get(hash) + } + + pub fn contains(&self, hash: &BlockHash) -> bool { + todo!() + // self.blocks.contains_key(hash) + } + + pub fn all_blocks(&self) -> Vec> { + todo!() + // self.blocks.values().cloned().collect() } }