diff --git a/Cargo.lock b/Cargo.lock index 823c8c7..82a3fa0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1286,12 +1286,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2061,6 +2061,12 @@ dependencies = [ "cc", ] +[[package]] +name = "id-arena" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" + [[package]] name = "ident_case" version = "1.0.1" @@ -2291,9 +2297,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.159" +version = "0.2.168" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" +checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" [[package]] name = "liblmdb-sys" @@ -2884,6 +2890,29 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "lmdb-rkv" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "447a296f7aca299cfbb50f4e4f3d49451549af655fb7215d7f8c0c3d64bad42b" +dependencies = [ + "bitflags 1.3.2", + "byteorder", + "libc", + "lmdb-rkv-sys", +] + +[[package]] +name = "lmdb-rkv-sys" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61b9ce6b3be08acefa3003c57b7565377432a89ec24476bbe72e11d101f852fe" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + [[package]] name = "lmdb-zero" version = "0.4.4" @@ -3501,6 +3530,15 @@ dependencies = [ "num-traits", ] +[[package]] +name = "ordered-float" +version = "3.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1e1c390732d15f1d48471625cd92d154e66db2c56645e29a9cd26f4699f72dc" +dependencies = [ + "num-traits", +] + [[package]] name = "p256" version = "0.13.2" @@ -4279,6 +4317,29 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rkv" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6d906922d99c677624d2042a93f89b2b7df0f6411032237d5d99a602c2487c" +dependencies = [ + "arrayref", + "bincode", + "bitflags 2.6.0", + "byteorder", + "id-arena", + "lazy_static", + "lmdb-rkv", + "log", + "ordered-float 3.9.2", + "paste", + "serde", + "serde_derive", + "thiserror", + "url", + "uuid", +] + [[package]] name = "rtnetlink" version = "0.10.1" @@ -4332,15 +4393,15 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.37" +version = "0.38.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" dependencies = [ "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -4511,7 +4572,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" dependencies = [ - "ordered-float", + "ordered-float 2.10.1", "serde", ] @@ -4651,6 +4712,7 @@ dependencies = [ "minotari_node_grpc_client", "num", "rand", + "rkv", "serde", "serde_cbor", "serde_json", @@ -4661,6 +4723,7 @@ dependencies = [ "tari_script", "tari_shutdown", "tari_utilities", + "tempfile", "thiserror", "tokio", "tonic", @@ -5429,9 +5492,9 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.12.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" dependencies = [ "cfg-if", "fastrand", @@ -5952,6 +6015,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "uuid" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" + [[package]] name = "vcpkg" version = "0.2.15" diff --git a/Cargo.toml b/Cargo.toml index fe98688..b202d4f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,6 +62,8 @@ thiserror = "1.0" tokio = { version = "1.41.0", features = ["full"] } tonic = "0.12.3" lru = "0.12.5" +tempfile = "3.14.0" +rkv = { version = "0.19.0", features = ["lmdb"] } [package.metadata.cargo-machete] ignored = ["log4rs"] diff --git a/src/main.rs b/src/main.rs index 205e191..91d9b0b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ use std::{ fs::File, io::Write, panic, + process, time::{SystemTime, UNIX_EPOCH}, }; @@ -56,6 +57,11 @@ async fn main() -> anyhow::Result<()> { let mut file = File::create("panic.log").unwrap(); file.write_all(format!("Panic at {}: {}", location, message).as_bytes()) .unwrap(); + if cfg!(debug_assertions) { + // In debug mode, we want to see the panic message + eprintln!("Panic occurred at {}: {}", location, message); + process::exit(500); + } })); Cli::parse().handle_command(Shutdown::new().to_signal()).await?; diff --git a/src/server/config.rs b/src/server/config.rs index ccc5dc1..ef4f2a5 100644 --- a/src/server/config.rs +++ b/src/server/config.rs @@ -23,6 +23,7 @@ pub struct Config { pub max_relay_circuits_per_peer: Option, pub block_time: u64, pub share_window: u64, + pub block_cache_path: PathBuf, } impl Default for Config { @@ -41,6 +42,7 @@ impl Default for Config { max_relay_circuits_per_peer: None, block_time: 20, share_window: 2160, + block_cache_path: PathBuf::from("block_cache"), } } } diff --git a/src/server/p2p/network.rs b/src/server/p2p/network.rs index 39c05a4..c42517f 100644 --- a/src/server/p2p/network.rs +++ b/src/server/p2p/network.rs @@ -2124,6 +2124,16 @@ where S: ShareChain if num_connections > 20 { continue; } + if num_connections == 0 { + match self.dial_seed_peers().await { + Ok(_) => {}, + Err(e) => { + warn!(target: LOG_TARGET, "Failed to dial seed peers: {e:?}"); + }, + } + continue; + } + let mut num_dialed = 0; let store_read_lock = self.network_peer_store.read().await; // Rather try and search good peers rather than randomly dialing @@ -2131,7 +2141,7 @@ where S: ShareChain for record in store_read_lock.whitelist_peers().values() { // Only dial seed peers if we have 0 connections if !self.swarm.is_connected(&record.peer_id) - && (num_connections == 0 || !store_read_lock.is_seed_peer(&record.peer_id)) { + && !store_read_lock.is_seed_peer(&record.peer_id) { let _unused = self.swarm.dial(record.peer_id); num_dialed += 1; // We can only do 30 connections @@ -2430,6 +2440,13 @@ where S: ShareChain self.query_tx.clone() } + pub async fn dial_seed_peers(&mut self) -> Result<(), Error> { + info!(target: LOG_TARGET, squad = &self.config.squad; "Dialing seed peers..."); + let seed_peers = self.parse_seed_peers().await?; + self.join_seed_peers(seed_peers).await?; + Ok(()) + } + /// Starts p2p service. /// Please note that this is a blocking call! pub async fn start(&mut self) -> Result<(), Error> { @@ -2457,8 +2474,9 @@ where S: ShareChain } self.subscribe_to_topics().await; - let seed_peers = self.parse_seed_peers().await?; - self.join_seed_peers(seed_peers).await?; + self.dial_seed_peers().await?; + // let seed_peers = self.parse_seed_peers().await?; + // self.join_seed_peers(seed_peers).await?; // start initial share chain sync // let in_progress = self.sync_in_progress.clone(); diff --git a/src/sharechain/in_memory.rs b/src/sharechain/in_memory.rs index f2c4824..f14f0f6 100644 --- a/src/sharechain/in_memory.rs +++ b/src/sharechain/in_memory.rs @@ -21,7 +21,13 @@ use tari_core::{ use tari_utilities::{epoch_time::EpochTime, hex::Hex}; use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard}; -use super::{MAIN_REWARD_SHARE, MIN_RANDOMX_DIFFICULTY, MIN_SHA3X_DIFFICULTY, UNCLE_REWARD_SHARE}; +use super::{ + lmdb_block_storage::LmdbBlockStorage, + MAIN_REWARD_SHARE, + MIN_RANDOMX_DIFFICULTY, + MIN_SHA3X_DIFFICULTY, + UNCLE_REWARD_SHARE, +}; use crate::{ server::{http::stats_collector::StatsBroadcastClient, Config, PROTOCOL_VERSION}, sharechain::{ @@ -45,7 +51,7 @@ pub const UNCLE_START_HEIGHT: u64 = 10; pub const MAX_MISSING_PARENTS: usize = 10; pub(crate) struct InMemoryShareChain { - p2_chain: Arc>, + p2_chain: Arc>>, pow_algo: PowAlgorithm, block_validation_params: Option>, consensus_manager: ConsensusManager, @@ -73,6 +79,7 @@ impl InMemoryShareChain { config.share_window * 2, config.share_window, config.block_time, + LmdbBlockStorage::new_from_temp_dir(), ))), pow_algo, block_validation_params, @@ -171,7 +178,7 @@ impl InMemoryShareChain { /// Submits a new block to share chain. async fn submit_block_with_lock( &self, - p2_chain: &mut RwLockWriteGuard<'_, P2Chain>, + p2_chain: &mut RwLockWriteGuard<'_, P2Chain>, block: Arc, params: Option>, syncing: bool, @@ -234,7 +241,7 @@ impl InMemoryShareChain { async fn get_calculate_and_cache_hashmap_of_shares( &self, - p2_chain: &mut RwLockWriteGuard<'_, P2Chain>, + p2_chain: &mut RwLockWriteGuard<'_, P2Chain>, ) -> Result)>, ShareChainError> { fn update_insert( miner_shares: &mut HashMap)>, @@ -285,7 +292,7 @@ impl InMemoryShareChain { } while cur_block.height > stop_height { cur_block = p2_chain - .get_parent_block(cur_block) + .get_parent_block(&cur_block) .ok_or(ShareChainError::BlockNotFound)?; update_insert( &mut miners_to_shares, @@ -313,7 +320,7 @@ impl InMemoryShareChain { fn all_blocks_with_lock( &self, - p2_chain: &RwLockReadGuard<'_, P2Chain>, + p2_chain: &RwLockReadGuard<'_, P2Chain>, start_height: Option, page_size: usize, main_chain_only: bool, diff --git a/src/sharechain/lmdb_block_storage.rs b/src/sharechain/lmdb_block_storage.rs index 8d1a953..7a5e85a 100644 --- a/src/sharechain/lmdb_block_storage.rs +++ b/src/sharechain/lmdb_block_storage.rs @@ -1 +1,84 @@ -pub(crate) struct LmdbBlockStorage {} +use std::{ + collections::HashMap, + env, + fs, + path::{Path, PathBuf}, + sync::{Arc, RwLock}, +}; + +use rkv::{ + backend::{Lmdb, LmdbEnvironment}, + Manager, + Rkv, + StoreOptions, +}; +use tari_common_types::types::BlockHash; +use tari_utilities::ByteArray; +use tempfile::{Builder, TempDir}; + +use super::P2Block; + +pub(crate) struct LmdbBlockStorage { + // path: PathBuf, + temp_dir: TempDir, + file_handle: Arc>>, +} + +impl LmdbBlockStorage { + pub fn new_from_temp_dir() -> Self { + let root = Builder::new().prefix("p2pool").tempdir().unwrap(); + fs::create_dir_all(root.path()).unwrap(); + let path = root.path(); + let mut manager = Manager::::singleton().write().unwrap(); + let file_handle = manager.get_or_create(path, Rkv::new::).unwrap(); + + // { + // let env = file_handle.read().unwrap(); + + // // Then you can use the environment handle to get a handle to a datastore: + // let store = env.open_single("block_cache", StoreOptions::create()).unwrap(); + // } + Self { + temp_dir: root, + file_handle, + } + } +} + +impl BlockCache for LmdbBlockStorage { + fn get(&self, hash: &BlockHash) -> Option> { + None + // let env = self.file_handle.read().expect("reader"); + // // Then you can use the environment handle to get a handle to a datastore: + // let store = env.open_single("block_cache", StoreOptions::create()).unwrap(); + // let reader = env.read().expect("reader"); + // let block = store.get(&reader, hash.as_bytes()).unwrap(); + // // let block = block.map(|b| Arc::new(bincode::deserialize(&b).unwrap())); + // todo!() + } +} + +pub trait BlockCache { + fn get(&self, hash: &BlockHash) -> Option>; +} + +#[cfg(test)] +pub mod test { + use super::*; + + pub(crate) struct InMemoryBlockCache { + blocks: HashMap>, + } + + impl InMemoryBlockCache { + pub fn new() -> Self { + Self { blocks: HashMap::new() } + } + } + + impl BlockCache for InMemoryBlockCache { + fn get(&self, hash: &BlockHash) -> Option> { + self.blocks.get(hash).cloned() + } + } +} diff --git a/src/sharechain/p2chain.rs b/src/sharechain/p2chain.rs index 530c7c5..92c6377 100644 --- a/src/sharechain/p2chain.rs +++ b/src/sharechain/p2chain.rs @@ -34,6 +34,7 @@ use tari_common_types::types::FixedHash; use tari_core::proof_of_work::{lwma_diff::LinearWeightedMovingAverage, AccumulatedDifficulty}; use tari_utilities::hex::Hex; +use super::lmdb_block_storage::{BlockCache, LmdbBlockStorage}; use crate::sharechain::{ error::ShareChainError, in_memory::MAX_UNCLE_AGE, @@ -123,17 +124,18 @@ impl Display for ChainAddResult { } } -pub struct P2Chain { +pub struct P2Chain { pub block_time: u64, + block_cache: Arc, pub cached_shares: Option)>>, - pub(crate) levels: HashMap, + pub(crate) levels: HashMap>, total_size: u64, share_window: u64, current_tip: u64, pub lwma: LinearWeightedMovingAverage, } -impl P2Chain { +impl P2Chain { pub fn total_accumulated_tip_difficulty(&self) -> AccumulatedDifficulty { match self.get_tip() { Some(tip) => tip @@ -153,26 +155,27 @@ impl P2Chain { None } - pub fn level_at_height(&self, height: u64) -> Option<&P2ChainLevel> { + pub fn level_at_height(&self, height: u64) -> Option<&P2ChainLevel> { self.levels.get(&height) } - pub fn get_block_at_height(&self, height: u64, hash: &FixedHash) -> Option<&Arc> { + pub fn get_block_at_height(&self, height: u64, hash: &FixedHash) -> Option> { let level = self.level_at_height(height)?; level.get(hash) } #[cfg(test)] - fn get_chain_block_at_height(&self, height: u64) -> Option<&Arc> { + fn get_chain_block_at_height(&self, height: u64) -> Option> { let level = self.level_at_height(height)?; - level.get(&level.chain_block) + level.get(&level.chain_block()) } - pub fn new_empty(total_size: u64, share_window: u64, block_time: u64) -> Self { + pub fn new_empty(total_size: u64, share_window: u64, block_time: u64, block_cache: T) -> Self { let levels = HashMap::new(); let lwma = LinearWeightedMovingAverage::new(DIFFICULTY_ADJUSTMENT_WINDOW, block_time).expect("Failed to create LWMA"); Self { + block_cache: Arc::new(block_cache), block_time, cached_shares: None, levels, @@ -270,7 +273,7 @@ impl P2Chain { // now lets check the uncles for uncle in &block.uncles { if let Some(uncle_block) = self.get_block_at_height(uncle.0, &uncle.1) { - if self.get_parent_block(uncle_block).is_none() { + if self.get_parent_block(&uncle_block).is_none() { new_tip .missing_blocks .insert(uncle_block.prev_hash, uncle_block.height.saturating_sub(1)); @@ -311,7 +314,7 @@ impl P2Chain { .get_block_at_height(uncle.0, &uncle.1) .ok_or(ShareChainError::BlockNotFound)?; let uncle_parent = self - .get_parent_block(uncle_block) + .get_parent_block(&uncle_block) .ok_or(ShareChainError::BlockNotFound)?; let uncle_level = self .level_at_height(uncle.0.saturating_sub(1)) @@ -348,7 +351,7 @@ impl P2Chain { all_blocks_verified = false; // so this block is unverified, we cannot count it but lets see if it just misses some blocks so // we can ask for them - if self.get_parent_block(parent).is_none() { + if self.get_parent_block(&parent).is_none() { new_tip .missing_blocks .insert(parent.prev_hash, parent.height.saturating_sub(1)); @@ -529,7 +532,7 @@ impl P2Chain { let level = self .level_at_height(height) .ok_or(ShareChainError::BlockLevelNotFound)?; - level.add_block(Arc::new(actual_block)); + level.add_block(Arc::new(actual_block))?; return Ok(()); } @@ -548,7 +551,7 @@ impl P2Chain { let level = self .level_at_height(height) .ok_or(ShareChainError::BlockLevelNotFound)?; - level.add_block(Arc::new(actual_block)); + level.add_block(Arc::new(actual_block))?; } Ok(()) @@ -559,7 +562,7 @@ impl P2Chain { let block_hash = block.hash; // edge case no current chain, lets just add if self.levels.is_empty() { - let new_level = P2ChainLevel::new(block); + let new_level = P2ChainLevel::new(block, self.block_cache.clone()); self.levels.insert(new_block_height, new_level); return self.verify_chain(new_block_height, block_hash); } @@ -570,7 +573,7 @@ impl P2Chain { }, None => { let height = block.height; - let level = P2ChainLevel::new(block); + let level = P2ChainLevel::new(block, self.block_cache.clone()); self.levels.insert(height, level); self.verify_chain(new_block_height, block_hash) }, @@ -588,7 +591,7 @@ impl P2Chain { self.add_block_inner(block) } - pub fn get_parent_block(&self, block: &P2Block) -> Option<&Arc> { + pub fn get_parent_block(&self, block: &P2Block) -> Option> { let parent_height = match block.height.checked_sub(1) { Some(height) => height, None => return None, @@ -600,7 +603,7 @@ impl P2Chain { parent_level.get(&block.prev_hash) } - pub fn get_tip(&self) -> Option<&P2ChainLevel> { + pub fn get_tip(&self) -> Option<&P2ChainLevel> { self.level_at_height(self.current_tip) .filter(|&level| level.chain_block() != FixedHash::zero()) } @@ -663,7 +666,7 @@ mod test { #[test] fn test_only_keeps_size() { - let mut chain = P2Chain::new_empty(10, 5, 10); + let mut chain = P2Chain::new_empty(10, 5, 10, LmdbBlockStorage::new_from_temp_dir()); let mut tari_block = Block::new(BlockHeader::new(0), AggregateBody::empty()); let mut prev_block = None; for i in 0..2100 { @@ -691,7 +694,7 @@ mod test { #[test] fn get_tips() { - let mut chain = P2Chain::new_empty(10, 5, 10); + let mut chain = P2Chain::new_empty(10, 5, 10, LmdbBlockStorage::new_from_temp_dir()); let mut prev_block = None; let mut tari_block = Block::new(BlockHeader::new(0), AggregateBody::empty()); @@ -710,7 +713,7 @@ mod test { chain.add_block_to_chain(block.clone()).unwrap(); let level = chain.get_tip().unwrap(); - assert_eq!(level.height, i); + assert_eq!(level.height(), i); assert_eq!(level.block_in_main_chain().unwrap().original_header.nonce, i); } } @@ -719,7 +722,7 @@ mod test { fn test_does_not_set_tip_unless_full_chain() { // we have a window of 5, meaing that we need 5 valid blocks // if we dont start at 0, we need a chain of at least 6 blocks - let mut chain = P2Chain::new_empty(10, 5, 10); + let mut chain = P2Chain::new_empty(10, 5, 10, LmdbBlockStorage::new_from_temp_dir()); let mut prev_block = None; let mut tari_block = Block::new(BlockHeader::new(0), AggregateBody::empty()); @@ -751,7 +754,7 @@ mod test { chain.add_block_to_chain(block.clone()).unwrap(); let level = chain.get_tip().unwrap(); - assert_eq!(level.height, 6); + assert_eq!(level.height(), 6); // the whole chain must be verified chain.assert_share_window_verified(); @@ -765,7 +768,7 @@ mod test { // to test this properly we need 6 blocks in the chain, and not use 0 as zero will always be valid and counter // as chain start block height 2 will only be valid if it has parents aka block 1, so we need share // window + 1 blocks in chain-- - let mut chain = P2Chain::new_empty(10, 5, 10); + let mut chain = P2Chain::new_empty(10, 5, 10, LmdbBlockStorage::new_from_temp_dir()); let mut prev_block = None; let mut tari_block = Block::new(BlockHeader::new(0), AggregateBody::empty()); @@ -788,7 +791,7 @@ mod test { assert!(chain.get_tip().is_none()); assert_eq!(chain.current_tip, 0); assert_eq!(chain.levels.len(), 1); - assert_eq!(chain.levels[&6].height, 6); + assert_eq!(chain.levels[&6].height(), 6); for i in (2..6).rev() { chain.add_block_to_chain(blocks[i].clone()).unwrap(); @@ -798,7 +801,7 @@ mod test { chain.add_block_to_chain(blocks[1].clone()).unwrap(); let level = chain.get_tip().unwrap(); - assert_eq!(level.height, 6); + assert_eq!(level.height(), 6); chain.assert_share_window_verified(); } @@ -808,7 +811,7 @@ mod test { // to test this properly we need 6 blocks in the chain, and not use 0 as zero will always be valid and counter // as chain start block height 2 will only be valid if it has parents aka block 1, so we need share // window + 1 blocks in chain-- - let mut chain = P2Chain::new_empty(20, 10, 10); + let mut chain = P2Chain::new_empty(20, 10, 10, LmdbBlockStorage::new_from_temp_dir()); let mut prev_block = None; let mut tari_block = Block::new(BlockHeader::new(0), AggregateBody::empty()); @@ -829,16 +832,16 @@ mod test { } for i in 0..9 { chain.add_block_to_chain(blocks[i].clone()).unwrap(); - assert_eq!(chain.get_tip().unwrap().height, i as u64); + assert_eq!(chain.get_tip().unwrap().height(), i as u64); chain.add_block_to_chain(blocks[19 - i].clone()).unwrap(); - assert_eq!(chain.get_tip().unwrap().height, i as u64); + assert_eq!(chain.get_tip().unwrap().height(), i as u64); } chain.add_block_to_chain(blocks[9].clone()).unwrap(); - assert_eq!(chain.get_tip().unwrap().height, 9); + assert_eq!(chain.get_tip().unwrap().height(), 9); chain.add_block_to_chain(blocks[10].clone()).unwrap(); - assert_eq!(chain.get_tip().unwrap().height, 19); + assert_eq!(chain.get_tip().unwrap().height(), 19); chain.assert_share_window_verified(); } @@ -849,7 +852,7 @@ mod test { // to test this properly we need 6 blocks in the chain, and not use 0 as zero will always be valid and counter // as chain start block height 2 will only be valid if it has parents aka block 1, so we need share // window + 1 blocks in chain-- - let mut chain = P2Chain::new_empty(10, 5, 10); + let mut chain = P2Chain::new_empty(10, 5, 10, LmdbBlockStorage::new_from_temp_dir()); let mut prev_block = None; let mut tari_block = Block::new(BlockHeader::new(0), AggregateBody::empty()); @@ -897,7 +900,7 @@ mod test { assert!(chain.get_tip().is_none()); assert_eq!(chain.current_tip, 0); assert_eq!(chain.levels.len(), 1); - assert_eq!(chain.levels[&6].height, 6); + assert_eq!(chain.levels[&6].height(), 6); for i in (2..6).rev() { chain.add_block_to_chain(blocks[i].clone()).unwrap(); @@ -911,12 +914,12 @@ mod test { chain.add_block_to_chain(uncle_block).unwrap(); let level = chain.get_tip().unwrap(); - assert_eq!(level.height, 6); + assert_eq!(level.height(), 6); } #[test] fn get_parent() { - let mut chain = P2Chain::new_empty(10, 5, 10); + let mut chain = P2Chain::new_empty(10, 5, 10, LmdbBlockStorage::new_from_temp_dir()); let mut prev_block = None; let mut tari_block = Block::new(BlockHeader::new(0), AggregateBody::empty()); @@ -950,7 +953,7 @@ mod test { #[test] fn test_dont_set_tip_on_single_high_height() { - let mut chain = P2Chain::new_empty(10, 5, 10); + let mut chain = P2Chain::new_empty(10, 5, 10, LmdbBlockStorage::new_from_temp_dir()); let mut prev_block = None; let mut tari_block = Block::new(BlockHeader::new(0), AggregateBody::empty()); @@ -969,7 +972,7 @@ mod test { chain.add_block_to_chain(block.clone()).unwrap(); let level = chain.get_tip().unwrap(); - assert_eq!(level.height, i); + assert_eq!(level.height(), i); } // we do this so we can add a missing parent or 2 let address = new_random_address(); @@ -996,7 +999,7 @@ mod test { chain.add_block_to_chain(block.clone()).unwrap(); let level = chain.get_tip().unwrap(); - assert_eq!(level.height, 19); + assert_eq!(level.height(), 19); let address = new_random_address(); let block = P2BlockBuilder::new(prev_block.as_ref()) @@ -1031,7 +1034,7 @@ mod test { #[test] fn add_blocks_to_chain_happy_path() { - let mut chain = P2Chain::new_empty(10, 5, 10); + let mut chain = P2Chain::new_empty(10, 5, 10, LmdbBlockStorage::new_from_temp_dir()); let mut timestamp = EpochTime::now(); let mut prev_block = None; @@ -1062,7 +1065,7 @@ mod test { #[test] fn add_blocks_to_chain_small_reorg() { - let mut chain = P2Chain::new_empty(10, 5, 10); + let mut chain = P2Chain::new_empty(10, 5, 10, LmdbBlockStorage::new_from_temp_dir()); let mut timestamp = EpochTime::now(); let mut prev_block = None; @@ -1158,7 +1161,7 @@ mod test { #[test] fn add_blocks_to_chain_super_large_reorg() { // this test will verify that we reorg to a completely new chain - let mut chain = P2Chain::new_empty(10, 5, 20); + let mut chain = P2Chain::new_empty(10, 5, 20, LmdbBlockStorage::new_from_temp_dir()); let mut prev_block = None; let mut tari_block = Block::new(BlockHeader::new(0), AggregateBody::empty()); @@ -1180,7 +1183,7 @@ mod test { } assert_eq!(chain.current_tip, 999); - assert_eq!(chain.get_tip().unwrap().chain_block, prev_block.unwrap().hash); + assert_eq!(chain.get_tip().unwrap().chain_block(), prev_block.unwrap().hash); let mut prev_block = None; let mut tari_block = Block::new(BlockHeader::new(0), AggregateBody::empty()); @@ -1201,7 +1204,7 @@ mod test { chain.add_block_to_chain(block).unwrap(); } assert_eq!(chain.current_tip, 999); - assert_eq!(chain.get_tip().unwrap().chain_block, prev_block.unwrap().hash); + assert_eq!(chain.get_tip().unwrap().chain_block(), prev_block.unwrap().hash); assert_eq!( chain .get_tip() @@ -1219,7 +1222,7 @@ mod test { #[test] fn add_blocks_missing_block() { // this test will verify that we reorg to a completely new chain - let mut chain = P2Chain::new_empty(50, 25, 20); + let mut chain = P2Chain::new_empty(50, 25, 20, LmdbBlockStorage::new_from_temp_dir()); let mut prev_block = None; let mut tari_block = Block::new(BlockHeader::new(0), AggregateBody::empty()); @@ -1250,7 +1253,7 @@ mod test { chain.add_block_to_chain(blocks[25].clone()).unwrap(); assert_eq!(chain.current_tip, 49); - assert_eq!(chain.get_tip().unwrap().chain_block, prev_block.unwrap().hash); + assert_eq!(chain.get_tip().unwrap().chain_block(), prev_block.unwrap().hash); chain.assert_share_window_verified(); } @@ -1258,7 +1261,7 @@ mod test { #[test] fn reorg_with_missing_uncle() { // this test will verify that we reorg to a completely new chain - let mut chain = P2Chain::new_empty(50, 25, 20); + let mut chain = P2Chain::new_empty(50, 25, 20, LmdbBlockStorage::new_from_temp_dir()); let mut prev_block = None; let mut tari_block = Block::new(BlockHeader::new(0), AggregateBody::empty()); @@ -1280,7 +1283,7 @@ mod test { } assert_eq!(chain.current_tip, 49); - assert_eq!(chain.get_tip().unwrap().chain_block, prev_block.unwrap().hash); + assert_eq!(chain.get_tip().unwrap().chain_block(), prev_block.unwrap().hash); let mut prev_block = None; let mut tari_block = Block::new(BlockHeader::new(0), AggregateBody::empty()); @@ -1324,9 +1327,9 @@ mod test { assert_eq!(chain.current_tip, 49); let hash = prev_block.unwrap().hash; - assert_ne!(chain.get_tip().unwrap().chain_block, hash); + assert_ne!(chain.get_tip().unwrap().chain_block(), hash); chain.add_block_to_chain(uncle_block.unwrap()).unwrap(); - assert_eq!(chain.get_tip().unwrap().chain_block, hash); + assert_eq!(chain.get_tip().unwrap().chain_block(), hash); assert_eq!( chain .get_tip() @@ -1344,7 +1347,7 @@ mod test { #[test] fn add_blocks_to_chain_super_large_reorg_only_window() { // this test will verify that we reorg to a completely new chain - let mut chain = P2Chain::new_empty(10, 5, 20); + let mut chain = P2Chain::new_empty(10, 5, 20, LmdbBlockStorage::new_from_temp_dir()); let mut prev_block = None; let mut tari_block = Block::new(BlockHeader::new(0), AggregateBody::empty()); @@ -1366,7 +1369,7 @@ mod test { } assert_eq!(chain.current_tip, 999); - assert_eq!(chain.get_tip().unwrap().chain_block, prev_block.unwrap().hash); + assert_eq!(chain.get_tip().unwrap().chain_block(), prev_block.unwrap().hash); let mut prev_block = None; let mut tari_block = Block::new(BlockHeader::new(0), AggregateBody::empty()); @@ -1391,7 +1394,7 @@ mod test { chain.add_block_to_chain(block.clone()).unwrap(); } assert_eq!(chain.current_tip, 999); - assert_eq!(chain.get_tip().unwrap().chain_block, prev_block.unwrap().hash); + assert_eq!(chain.get_tip().unwrap().chain_block(), prev_block.unwrap().hash); assert_eq!( chain .get_tip() @@ -1408,7 +1411,7 @@ mod test { #[test] fn calculate_total_difficulty_correctly() { - let mut chain = P2Chain::new_empty(10, 5, 10); + let mut chain = P2Chain::new_empty(10, 5, 10, LmdbBlockStorage::new_from_temp_dir()); let mut timestamp = EpochTime::now(); let mut prev_block = None; @@ -1437,7 +1440,7 @@ mod test { #[test] fn calculate_total_difficulty_correctly_with_uncles() { - let mut chain = P2Chain::new_empty(10, 5, 10); + let mut chain = P2Chain::new_empty(10, 5, 10, LmdbBlockStorage::new_from_temp_dir()); let mut timestamp = EpochTime::now(); let mut prev_block = None; @@ -1489,7 +1492,7 @@ mod test { #[test] fn calculate_total_difficulty_correctly_with_wrapping_blocks() { - let mut chain = P2Chain::new_empty(10, 5, 10); + let mut chain = P2Chain::new_empty(10, 5, 10, LmdbBlockStorage::new_from_temp_dir()); let mut timestamp = EpochTime::now(); let mut prev_block = None; @@ -1541,7 +1544,7 @@ mod test { #[test] fn reorg_with_uncles() { - let mut chain = P2Chain::new_empty(10, 5, 10); + let mut chain = P2Chain::new_empty(10, 5, 10, LmdbBlockStorage::new_from_temp_dir()); let mut timestamp = EpochTime::now(); let mut prev_block = None; @@ -1647,7 +1650,7 @@ mod test { #[test] fn rerog_less_than_share_window() { - let mut chain = P2Chain::new_empty(20, 15, 20); + let mut chain = P2Chain::new_empty(20, 15, 20, LmdbBlockStorage::new_from_temp_dir()); let mut prev_block = None; let mut tari_block = Block::new(BlockHeader::new(0), AggregateBody::empty()); @@ -1668,7 +1671,7 @@ mod test { chain.add_block_to_chain(block.clone()).unwrap(); let level = chain.get_tip().unwrap(); - assert_eq!(level.height, i); + assert_eq!(level.height(), i); assert_eq!(level.block_in_main_chain().unwrap().original_header.nonce, i); } @@ -1695,7 +1698,7 @@ mod test { let level = chain.get_tip().unwrap(); - assert_eq!(level.height, 9); + assert_eq!(level.height(), 9); if i < 9 { // less than 9 it has not reorged yet assert_eq!(level.block_in_main_chain().unwrap().original_header.nonce, 9); @@ -1709,7 +1712,7 @@ mod test { #[test] fn rests_levels_after_reorg() { - let mut chain = P2Chain::new_empty(20, 15, 20); + let mut chain = P2Chain::new_empty(20, 15, 20, LmdbBlockStorage::new_from_temp_dir()); let mut prev_block = None; let mut tari_block = Block::new(BlockHeader::new(0), AggregateBody::empty()); @@ -1730,13 +1733,16 @@ mod test { chain.add_block_to_chain(block.clone()).unwrap(); let level = chain.get_tip().unwrap(); - assert_eq!(level.height, i); + assert_eq!(level.height(), i); assert_eq!(level.block_in_main_chain().unwrap().original_header.nonce, i); } let level = chain.get_tip().unwrap(); - assert_eq!(level.height, 9); + assert_eq!(level.height(), 9); assert_eq!(chain.total_accumulated_tip_difficulty().as_u128(), 90); - assert_eq!(chain.level_at_height(9).unwrap().chain_block, prev_block.unwrap().hash); + assert_eq!( + chain.level_at_height(9).unwrap().chain_block(), + prev_block.unwrap().hash + ); // lets create a new tip to reorg to branching off 2 from the tip let prev_block = Some((*chain.level_at_height(7).unwrap().block_in_main_chain().unwrap()).clone()); @@ -1757,14 +1763,14 @@ mod test { assert_eq!(chain.add_block_to_chain(block.clone()).unwrap().missing_blocks.len(), 0); let level = chain.get_tip().unwrap(); - assert_eq!(level.height, 8); + assert_eq!(level.height(), 8); assert_eq!(chain.total_accumulated_tip_difficulty().as_u128(), 172); - assert_eq!(chain.level_at_height(9).unwrap().chain_block, FixedHash::default()); + assert_eq!(chain.level_at_height(9).unwrap().chain_block(), FixedHash::default()); } #[test] fn difficulty_go_up() { - let mut chain = P2Chain::new_empty(10, 5, 10); + let mut chain = P2Chain::new_empty(10, 5, 10, LmdbBlockStorage::new_from_temp_dir()); let mut prev_block = None; let mut tari_block = Block::new(BlockHeader::new(0), AggregateBody::empty()); @@ -1797,13 +1803,13 @@ mod test { chain.add_block_to_chain(block.clone()).unwrap(); let level = chain.get_tip().unwrap(); - assert_eq!(level.height, i); + assert_eq!(level.height(), i); assert_eq!(level.block_in_main_chain().unwrap().original_header.nonce, i); } } #[test] fn difficulty_go_down() { - let mut chain = P2Chain::new_empty(10, 5, 10); + let mut chain = P2Chain::new_empty(10, 5, 10, LmdbBlockStorage::new_from_temp_dir()); let mut prev_block = None; let mut tari_block = Block::new(BlockHeader::new(0), AggregateBody::empty()); @@ -1836,7 +1842,7 @@ mod test { chain.add_block_to_chain(block.clone()).unwrap(); let level = chain.get_tip().unwrap(); - assert_eq!(level.height, i); + assert_eq!(level.height(), i); assert_eq!(level.block_in_main_chain().unwrap().original_header.nonce, i); } } @@ -1846,7 +1852,7 @@ mod test { // This test adds a block to the tip, and then adds second block, // but has an uncle that is not in the chain. This test checks that // the tip is not set to the new block, because the uncle is missing. - let mut chain = P2Chain::new_empty(10, 5, 10); + let mut chain = P2Chain::new_empty(10, 5, 10, LmdbBlockStorage::new_from_temp_dir()); let prev_block = None; @@ -1879,7 +1885,7 @@ mod test { #[test] fn test_only_reorg_to_chain_if_it_is_verified() { - let mut chain = P2Chain::new_empty(10, 5, 10); + let mut chain = P2Chain::new_empty(10, 5, 10, LmdbBlockStorage::new_from_temp_dir()); let prev_block = None; let block = P2BlockBuilder::new(prev_block.as_ref()) @@ -1930,13 +1936,13 @@ mod test { .unwrap(); assert_eq!(chain.current_tip, 1); - assert_eq!(chain.get_tip().unwrap().chain_block, block2.hash); + assert_eq!(chain.get_tip().unwrap().chain_block(), block2.hash); chain.add_block_to_chain(block3b).unwrap(); // Check that we don't reorg assert_eq!(chain.current_tip, 1); - assert_eq!(chain.get_tip().unwrap().chain_block, block2.hash); + assert_eq!(chain.get_tip().unwrap().chain_block(), block2.hash); chain.add_block_to_chain(unverified_uncle).unwrap(); @@ -1944,7 +1950,7 @@ mod test { chain.add_block_to_chain(block2b.clone()).unwrap(); // But chain tip should not be 3b because it is not verified assert_eq!(chain.current_tip, 1); - assert_eq!(chain.get_tip().unwrap().chain_block, block2b.hash); + assert_eq!(chain.get_tip().unwrap().chain_block(), block2b.hash); } fn diff(i: u64) -> Difficulty { diff --git a/src/sharechain/p2chain_level.rs b/src/sharechain/p2chain_level.rs index 1092d0d..e3fdc37 100644 --- a/src/sharechain/p2chain_level.rs +++ b/src/sharechain/p2chain_level.rs @@ -25,30 +25,30 @@ use std::{collections::HashMap, sync::Arc}; use tari_common_types::types::{BlockHash, FixedHash}; +use super::lmdb_block_storage::BlockCache; use crate::sharechain::{error::ShareChainError, lmdb_block_storage::LmdbBlockStorage, p2block::P2Block}; /// A collection of blocks with the same height. -pub struct P2ChainLevel { +pub struct P2ChainLevel { // pub blocks: HashMap>, - blocks: LmdbBlockStorage, + block_cache: Arc, height: u64, chain_block: BlockHash, } -impl P2ChainLevel { - pub fn new(block: Arc) -> Self { +impl P2ChainLevel { + pub fn new(block: Arc, block_cache: Arc) -> Self { // 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); - todo!(); - // Self { - // blocks, - // height, - // chain_block, - // } + Self { + block_cache, + height, + chain_block, + } } pub fn height(&self) -> u64 { @@ -80,9 +80,8 @@ impl P2ChainLevel { // self.blocks.get(&self.chain_block) } - pub fn get(&self, hash: &BlockHash) -> Option<&Arc> { - todo!() - // self.blocks.get(hash) + pub fn get(&self, hash: &BlockHash) -> Option> { + self.block_cache.get(hash) } pub fn contains(&self, hash: &BlockHash) -> bool { @@ -98,10 +97,13 @@ impl P2ChainLevel { #[cfg(test)] mod test { + use std::sync::Arc; + use tari_utilities::epoch_time::EpochTime; use crate::sharechain::{ in_memory::test::new_random_address, + lmdb_block_storage::test::InMemoryBlockCache, p2block::P2BlockBuilder, p2chain_level::P2ChainLevel, }; @@ -115,7 +117,7 @@ mod test { .with_miner_wallet_address(address.clone()) .build() .unwrap(); - let mut chain_level = P2ChainLevel::new(block.clone()); + let mut chain_level = P2ChainLevel::new(block.clone(), Arc::new(InMemoryBlockCache::new())); chain_level.chain_block = block.generate_hash(); assert_eq!(